//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: DirNode.sg // // Note: [vjm] First "Atomic" version of the Nameserver. The // atomic blocks have deliberately been made coarse because of // the recursive nature of all functions. The recursive // structure of functions means that the entire function body // becomes a part of an atomic block on a recursive call. This // means that simply "writing" a fine grained looking atomic // block doesn't give us an actual fine-grain atomic block, the // code essentially forces the atomic block to span across the // entire function in the presence of recursion. The fine // granularity might lead to smaller atomic blocks in the // absence of recursion consequently leading to smaller windows // of contention among concurrent transactions. // // Haven't atomized kernel version functions since that "might" // lead to undesirable interactions with other kernel // components. // //#define BREAK_ON_FAILURE //#define CHECK_TRAVERSE_ACCESS using System; using System.Text; using System.Collections; using System.Threading; using Microsoft.SingSharp; using Microsoft.Singularity; using Microsoft.Singularity.Io; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.Security; #if !SINGULARITY_PROCESS namespace Microsoft.Singularity.Directory #else using Microsoft.Singularity; using Microsoft.Singularity.V1.Services; namespace Microsoft.Application.DSP #endif { #if SINGULARITY_PROCESS public class Kernel { public static void Waypoint(int num) { } } #endif public class DirNode : Node { public readonly SortedList! names; private Hashtable! notificationList; private StringBuilder sb; private Mutex! dirMutex; public void AcquireLock() { dirMutex.WaitOne(); } public void ReleaseLock() { dirMutex.ReleaseMutex(); } public DirNode(string! name, Node! parent) { names = new SortedList(); dirMutex = new Mutex(); notificationList = new Hashtable(); sb = new StringBuilder(256); base(NodeType.Directory, name, parent); } public DirNode(string! name, AclCore! core, IAclPolicy! policy) { names = new SortedList(); dirMutex = new Mutex(); notificationList = new Hashtable(); sb = new StringBuilder(256); base(NodeType.Directory, name, core, policy); } /////////////////////////////////////////////////////////////////////////////// // begin kernel only functions /////////////////////////////////////////////////////////////////////////////// #if !SINGULARITY_PROCESS public bool CreateDirectory(string! name, out ErrorCode error) { error = ErrorCode.NoError; bool success = false; AcquireLock(); if (names.ContainsKey(name)) { success = false; error = ErrorCode.AlreadyExists; DebugStub.Break(); ReleaseLock(); return false; } else { DirNode d = new DirNode(name, this); Tracing.Log(Tracing.Debug,"createDir: parent="+name); names.Add(name,d); HandleNotification(name,NotifyType.Creation); success = true; } ReleaseLock(); return success; } public bool CreateLink(string! name, string! val) { bool success = false; AcquireLock(); if (names.ContainsKey(name)) { success = false; #if BREAK_ON_FAILURE DebugStub.Break(); #endif } else { SymLinkNode sym = new SymLinkNode(val, name, this); names.Add(name,sym); success = true; } ReleaseLock(); return success; } public DirNode FindDirectory(StringBuilder! p) { /* sb.Append("\nFindDirectory "); sb.Append(SbUtils.PathString(p)); sb.Append(" at dir "); sb.Append(this.NodeName); DebugStub.WriteLine(sb.ToString()); Tracing.Log(Tracing.Debug,sb.ToString()); */ if (p.Length == 0) { return this; } string first = SbUtils.FirstElement(p); /* sb.Length = 0; sb.Append(" findDir: p="); sb.Append(p.ToString()); sb.Append(" First="); sb.Append(first); DebugStub.WriteLine(sb.ToString()); */ //DebugStub.Break(); if (null == first) { return null; } Node n = (Node)this.names[first]; if (null == n) { return null; } p = SbUtils.RemoveFirstElement(p); /* sb.Length = 0; sb.Append(" afterRemove: p="); sb.Append(p.ToString()); sb.Append(" First="); sb.Append(first); DebugStub.WriteLine(sb.ToString()); */ if (n is DirNode) { return ((DirNode) n).FindDirectory(p); } else { //DebugStub.Break(); return null; } } /// /// Special kernel interface for adding in memory images to the namespace. /// public bool RegisterIoMemory(string! name, IoMemory! ioMemory) { Tracing.Log(Tracing.Debug,"RegisterIoMemory: "+name+" in "+name+" VA:{0} Len:{1}", ioMemory.VirtualAddress, (UIntPtr)ioMemory.Length); if (ioMemory.VirtualAddress == UIntPtr.Zero) { DebugStub.Break(); } bool success = false; try { this.AcquireLock(); this.names.Add(name, new IoMemoryNode(ioMemory, name, this)); success = true; //success } finally { this.ReleaseLock(); } return success; } #endif /////////////////////////////////////////////////////////////////////////////// // end kernel only functions /////////////////////////////////////////////////////////////////////////////// public override bool GetAttributes(StringBuilder! p, Principal pr, out bool linkFound, out ErrorCode error, out bool reparse, out string link, out long length, out NodeType nodeType ) { Kernel.Waypoint(3050); atomic { reparse = false; link = null; length = 0; linkFound = false; error = ErrorCode.NoError; nodeType = NodeType.BadNode; if (CheckEmpty(p)) { //the user wants the attributes of the directory object Kernel.Waypoint(3051); Kernel.Waypoint(3052); //check read access for this object if (!CheckAccess(DirPermissions.AccessModeRead, pr)) { error = ErrorCode.AccessDenied; return false; } nodeType = NodeType.Directory; return true; } // there is more path, continue processing string first = SbUtils.FirstElement(p); if (null == first) { error = ErrorCode.NotFound; return false; } // can the caller traverse the current node? Kernel.Waypoint(3052); #if CHECK_TRAVERSE_ACCESS if (!CheckAccess(DirPermissions.AccessModeTraverse, pr)) { error = ErrorCode.AccessDenied; Kernel.Waypoint(3053); return false; } #endif Kernel.Waypoint(3054); Node n = (Node)this.names[first]; if (null == n) { error = ErrorCode.NotFound; nodeType = NodeType.BadNode; return false; } p = SbUtils.RemoveFirstElement(p); Kernel.Waypoint(3055); bool ok = n.GetAttributes(p, pr, out linkFound, out error, out reparse, out link, out length, out nodeType ); Kernel.Waypoint(3056); return ok; } } private bool isMatch(string! name, string! pattern) { bool match = false; bool continueMatch = true; string prefix = null; string postfix = null; int starPos = pattern.IndexOf("*"); if (starPos != -1) { if (starPos == 0) { prefix = null; postfix = pattern.Substring(1); } else if (starPos == pattern.Length-1) { prefix = pattern.Substring(0, pattern.Length-1); postfix = null; } else { prefix = pattern.Substring(0, starPos); postfix = pattern.Substring(starPos+1); } if (prefix != null) { if (!name.StartsWith(prefix)) { continueMatch = false; } } if (continueMatch && postfix != null) { if (!name.EndsWith(postfix)) { continueMatch = false; } } match = continueMatch; } else if (pattern == name) { match = true; } //DebugStub.WriteLine("name={0} pre={1} post={2} match={3}", // __arglist(name, prefix, postfix, match)); return match; } public void HandleNotification(string! name, NotifyType type) { if (notificationList == null) { return; } foreach (DictionaryEntry n in notificationList) { string pattern = (string!) n.Key; bool match = isMatch (name,pattern); if (match) { NotifyContract.Imp notifyEP = ((TRef !) n.Value).Acquire(); assert notifyEP.InState(NotifyContract.Notify.Value); notifyEP.SendChangeNotification(Bitter.FromString2(name),type); // should really be a switch receive notifyEP.RecvAckChangeNotification(); Tracing.Log(Tracing.Debug,"notifying: pattern="+pattern+" path="+name+" type="+type); ((TRef !) n.Value).Release(notifyEP); } } //foreach } /// /// null on success, the service argument if it failed. /// public override ServiceContract.Exp Bind(StringBuilder! p, Principal pr, out bool success, out bool linkFound, out ErrorCode error, out bool reparse, out string link, [Claims] ServiceContract.Exp! service) { Tracing.Log(Tracing.Debug, "bind "+ SbUtils.PathString(p)); Kernel.Waypoint(3000); atomic { success = false; linkFound = false; reparse = false; link = null; error = ErrorCode.NoError; if (CheckEmpty(p)) { //the user wants to bind to a directory object in the namespace Kernel.Waypoint(3001); DirectoryServiceContract.Exp dirEP = service as DirectoryServiceContract.Exp; Kernel.Waypoint(3002); if (dirEP != null) { // check Traverse access for both caller and supplied EP #if CHECK_TRAVERSE_ACCESS if (!CheckAccess(DirPermissions.AccessModeTraverse, pr, service)) { DebugStub.WriteLine("No access to DirectoryService.Bind"); Kernel.Waypoint(3003); error = ErrorCode.AccessDenied; return service; } #endif //Tracing.Log(Tracing.Debug," found '/' starting dir worker"); Kernel.Waypoint(3004); DirectoryServiceWorker.Create(this, dirEP); Kernel.Waypoint(3005); success = true; return null; } else { error = ErrorCode.ContractNotSupported; return service; } } Kernel.Waypoint(3006); Node n = GetNextNode(p, pr, out error); if (null == n) { return service; } Kernel.Waypoint(3009); ServiceContract.Exp sp = n.Bind(p, pr, out success, out linkFound, out error, out reparse, out link, service); Kernel.Waypoint(3010); return sp; } } public override ServiceProviderContract.Imp Register(StringBuilder! p, Principal pr, [Claims] ServiceProviderContract.Imp! sp, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { bool firstFound; Node n; #if !SINGULARITY_PROCESS Tracing.Log(Tracing.Debug,"({1:x8}) Register: path ={0}\n", SbUtils.PathString(p),(UIntPtr)Kernel.AddressOf(Thread.CurrentThread)); #endif atomic { reparse = false; linkFound = false; link = null; error = ErrorCode.NoError; if (CheckEmpty(p) ) { error = ErrorCode.NotFound; return sp; } if (SbUtils.IsTail(p)) { string! name = (!)SbUtils.FirstElement(p); if (names.ContainsKey(name)) { error = ErrorCode.AlreadyExists; return sp; } // check register access for both caller and EP else if (!CheckAccess(DirPermissions.AccessModeRegister, pr, sp)) { error = ErrorCode.AccessDenied; return sp; } else { HandleNotification(name, NotifyType.Creation); this.names.Add(name, new ProviderNode(sp, name, this)); error = ErrorCode.NoError; return null; } } else { n = GetNextNode(p, pr, out error); if (n == null) { return sp; } return n.Register(p, pr, sp, out linkFound, out error, out reparse, out link); } } } /// /// The endpoint on success, null, if it fails. /// public override ServiceProviderContract.Imp:Start Deregister(StringBuilder! p, Principal pr, DirectoryServiceContract.Exp! ep, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { bool firstFound; Node n; #if !SINGULARITY_PROCESS Tracing.Log(Tracing.Debug,"({1:x8}) DeRegister: path ={0}\n", SbUtils.PathString(p),(UIntPtr)Kernel.AddressOf(Thread.CurrentThread)); #endif atomic { reparse = false; linkFound = false; link = null; error = ErrorCode.NoError; if (CheckEmpty(p) ) { error = ErrorCode.NotFound; return null; } if (SbUtils.IsTail(p)) { string! name = (!)SbUtils.FirstElement(p); if (!names.ContainsKey(name)) { error = ErrorCode.NotFound; return null; } // check deregister access of caller else if (!CheckAccess(DirPermissions.AccessModeDeregister, pr)) { error = ErrorCode.AccessDenied; return null; } else { HandleNotification(name, NotifyType.Deletion); n = (Node) names[name]; if (n != null && n is ProviderNode) { ServiceProviderContract.Imp:Start sp = ((ProviderNode) n).GetServiceEndpoint(); names.Remove(name); error = ErrorCode.NoError; return sp; } else { error = ErrorCode.NotProvider; return null; } } } else { n = GetNextNode(p, pr, out error); if (n == null) { return null; } return n.Deregister(p, pr, ep, out linkFound, out error, out reparse, out link); } } return null; } // Local Enumeration Records. We need this // because we don't want to do ExHeap allocations in an // atomic block (due to the resulting side effects) in // the Enumerate() method below. struct LocalEnumerationRecords { public string Path; public NodeType Type; } /// /// The a vector of vectors if success, null, if it fails. /// public EnumerationRecords[] in ExHeap Enumerate(Principal pr, out ErrorCode error) { error = ErrorCode.Unknown; // check read access for directory enumerate if (!CheckAccess(DirPermissions.AccessModeRead, pr)) { error = ErrorCode.AccessDenied; return null; } // store the enumeration in a local vector // from within the atomic block, and after the // atomic block succeeds copy the local vector to // the ExHeap buffer that is used to send out the // enumeration EnumerationRecords[] in ExHeap responses; LocalEnumerationRecords[] localResponses; atomic { error = ErrorCode.Unknown; localResponses = new LocalEnumerationRecords[names.Count]; for (int i = 0; i < names.Count; i++){ localResponses[i].Path = (!) (string)names.GetKey(i); localResponses[i].Type = ((!) (Node)names.GetByIndex(i)).Type; } } responses = new [ExHeap] EnumerationRecords[localResponses.Length]; for(int i = 0; i < localResponses.Length; i++) { expose (responses[i]) { delete responses[i].Path; // checker doesn't know its null. responses[i].Path = Bitter.FromString2((!) localResponses[i].Path); responses[i].Type = localResponses[i].Type; } } return responses; } /// /// true on success, false, if it fails. /// public override NotifyContract.Imp Notify(StringBuilder! path, Principal pr, string! pattern, bool sendExisting, out bool linkFound, out ErrorCode error, out bool reparse, out string link, [Claims] NotifyContract.Imp! notifyImp) { //DebugStub.WriteLine(" dir Notify() sb.len={0}, s={1}",__arglist(pathSpec.Length,pathSpec.ToString())); atomic { error = ErrorCode.NoError; linkFound = false; reparse = false; link = null; if (path.Length == 0) { // got it! DebugStub.Break(); return notifyImp; } if (SbUtils.IsTail(path)) { string! first = (!)SbUtils.FirstElement(path); // check access: can the caller AND dest EP received notifications for this object if (!CheckAccess(DirPermissions.AccessModeNotify, pr, notifyImp)) { error = ErrorCode.AccessDenied; return notifyImp; } notifyImp.SendBegin(); TRef notifyEP = new TRef (notifyImp); notificationList.Add(first,notifyEP); Tracing.Log(Tracing.Debug,"notify: pattern="+first); return null; } else { // check access: can the caller traverse the current folder #if CHECK_TRAVERSE_ACCESS if (!CheckAccess(DirPermissions.AccessModeTraverse, pr)) { error = ErrorCode.AccessDenied; return notifyImp; } #endif string first = SbUtils.FirstElement(path); sb.Length = 0; sb.Append(" dirnode Notify: str="); sb.Append(path.ToString()); sb.Append(" first = "); sb.Append(first); //DebugStub.WriteLine(sb.ToString()); Node n = (Node)this.names[first]; if (null == n) { DebugStub.Break(); return notifyImp; } path = SbUtils.RemoveFirstElement(path); return n.Notify(path, pr, pattern, sendExisting, out linkFound, out error, out reparse, out link, notifyImp); } } } public override FileContract.Imp CreateAndBindFile(StringBuilder! p, Principal pr, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { linkFound = false; error = ErrorCode.NotImplemented; reparse = false; link = null; return null; } public override bool CreateDirectory(StringBuilder! p, Principal pr, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { Node n; atomic { linkFound = false; reparse = false; link = null; error = ErrorCode.NoError; if (SbUtils.IsTail(p)) { string! name = (!)SbUtils.FirstElement(p); if (names.ContainsKey(name)) { error = ErrorCode.AlreadyExists; return false; } // check write access to parent if (!CheckAccess(DirPermissions.AccessModeWrite, pr)) { error = ErrorCode.AccessDenied; return false; } else { DirNode d = new DirNode(name, this); Tracing.Log(Tracing.Debug,"createDir: parent="+name); names.Add(name,d); // not sure if this is needed // here, maybe needed only for // Registering and Deregistering // services; hence commenting out // the notification part //HandleNotification(name, NotifyType.Creation); return true; } } else { // pass the request on to the next node if there is one. if (CheckEmpty(p)) { error = ErrorCode.NotFound; DebugStub.Break(); return false; } n = GetNextNode(p, pr, out error); if (n == null) { return false; } return ((!)n).CreateDirectory(p, pr, out linkFound, out error, out reparse, out link); } return false; } } public override bool CreateFile(StringBuilder! p, Principal pr, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { Node n; linkFound = false; reparse = false; link = null; error = ErrorCode.NoError; // since the function implementation (commented out // below) always returns a "false" value and does // not update the system state, we'll simply return // a "false" value here itself return false; /* if (SbUtils.IsTail(p)) { // true files are not supported in the namespace error = ErrorCode.NotImplemented; return false; } else { // pass the request on to the next node if there is one. if (CheckEmpty(p)) { error = ErrorCode.NotImplemented; return false; } n = GetNextNode(p, pr, out error); if (n == null) { return false; } return ((!)n).CreateFile(p, pr, out linkFound, out error, out reparse, out link); } return false; */ } private bool CheckEmpty(StringBuilder! p) { if ( (p.Length == 0) || ( (p.Length == 1 ) && (p[0] == '/') ) ) { return true; } return false; } private Node GetNextNode(StringBuilder! p, Principal pr, out ErrorCode error) { //DebugStub.Break(); string first = SbUtils.FirstElement(p); error = ErrorCode.NoError; if (null == first) { return null; } Kernel.Waypoint(3006); #if CHECK_TRAVERSE_ACCESS if (!CheckAccess(DirPermissions.AccessModeTraverse, pr)) { Kernel.Waypoint(3007); error = ErrorCode.AccessDenied; return null; } #endif Kernel.Waypoint(3008); Node n = (Node)this.names[first]; if (null == n) { error = ErrorCode.NotFound; return null; } SbUtils.RemoveFirstElement(p); return n; } public override bool CreateLink(StringBuilder! p, Principal pr, string! value, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { Node n; atomic { linkFound = false; link = null; reparse = false; error = ErrorCode.NoError; if (SbUtils.IsTail(p)) { string! name = (!)SbUtils.FirstElement(p); if (names.ContainsKey(name)) { error = ErrorCode.AlreadyExists; return false; } // check write access to parent else if (!CheckAccess(DirPermissions.AccessModeWrite, pr)) { error = ErrorCode.AccessDenied; return false; } else { SymLinkNode sym = new SymLinkNode(value, name, this); names.Add(name,sym); return true; } } else { //recurse if (CheckEmpty(p)) { error = ErrorCode.NotFound; return false; } n = GetNextNode(p, pr, out error); if (n == null) { return false; } return ((!)n).CreateLink(p, pr, value, out linkFound, out error, out reparse, out link); } } } public override bool DeleteDirectory(StringBuilder! p, Principal pr, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { Node n; DirNode child; atomic { linkFound = false; reparse = false; link = null; error = ErrorCode.NotFound; if (CheckEmpty(p)) { return false; } if (SbUtils.IsTail(p)) { string! name = (!)SbUtils.FirstElement(p); if (names.ContainsKey(name)) { // check to see if this node has entries // if so let the caller know n = GetNextNode(p, pr, out error); if (n == null) { return false; } if (n is DirNode) { child = (DirNode) n; if (child.names.Count != 0) { DebugStub.WriteLine("Directory is not empty({0})!", __arglist(child.names.Count)); error = ErrorCode.DirectoryNotEmpty; return false; } // check write access to parent else if (!CheckAccess(DirPermissions.AccessModeWrite, pr)) { error = ErrorCode.AccessDenied; return false; } // check write access to child else if (!n.CheckAccess(DirPermissions.AccessModeWrite, pr)) { error = ErrorCode.AccessDenied; return false; } } else { error = ErrorCode.NotDirectory; return false; } error = ErrorCode.NoError; names.Remove(name); return true; } else { return false; } } n = GetNextNode(p, pr, out error); if (n == null) { linkFound = false; reparse = false; link = null; return false; } return ((!)n).DeleteDirectory(p, pr, out linkFound, out error, out reparse, out link); } } public override bool DeleteFile(StringBuilder! p, Principal pr, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { Node n; linkFound = false; reparse = false; link = null; error = ErrorCode.NotFound; // since the function implementation (commented out // below) always returns a "false" value and does // not update the system state, we'll simply return // a "false" value here itself return false; /* if (CheckEmpty(p)) { error = ErrorCode.NotFound; return false; } if (SbUtils.IsTail(p)) { string! name = (!)SbUtils.FirstElement(p); if (names.ContainsKey(name)) { // check write access to parent if (!CheckAccess(DirPermissions.AccessModeWrite, pr)) { error = ErrorCode.AccessDenied; return false; } // continue on to actual node for deletion } else { return false; } } n = GetNextNode(p, pr, out error); if (n == null) { return false; } return ((!)n).DeleteFile(p, pr, out linkFound, out error, out reparse, out link); */ } public override bool DeleteLink(StringBuilder! p, Principal pr, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { Node n; //DebugStub.Break(); atomic { linkFound = false; reparse = false; link = null; error = ErrorCode.NotFound; if (CheckEmpty(p)) { return false; } if (SbUtils.IsTail(p)) { string! name = (!)SbUtils.FirstElement(p); if (names.ContainsKey(name)) { //check write access to parent if (!CheckAccess(DirPermissions.AccessModeWrite, pr)) { error = ErrorCode.AccessDenied; return false; } error = ErrorCode.NoError; // this should check whether the node is a link! names.Remove(name); return true; } else { return false; } } n = GetNextNode(p, pr, out error); if (n == null) { linkFound = false; reparse = false; link = null; return false; } return ((!)n).DeleteLink(p, pr, out linkFound, out error, out reparse, out link); } } public override bool GetLinkValue(StringBuilder! p, Principal pr, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { Node n; atomic { error = ErrorCode.NotLink; linkFound = false; reparse = false; link = null; //DebugStub.Break(); if (CheckEmpty(p)) { return false; } n = GetNextNode(p, pr, out error); if (n == null) { return false; } return ((!)n).GetLinkValue(p, pr, out linkFound, out error, out reparse, out link); } } public override bool QueryACL(StringBuilder! p, Principal pr, out bool linkFound, out ErrorCode error, out bool reparse, out string link, out Acl acl ) { Node n; acl = new Acl(); atomic { error = ErrorCode.NoError; linkFound = false; reparse = false; link = null; if (CheckEmpty(p)) { if (CheckAccess(DirPermissions.AccessModeRead, pr)) { error = ErrorCode.AccessDenied; return false; } acl = this.GetInstanceAcl(); return true; } n = GetNextNode(p, pr, out error); if (n == null) { return false; } return ((!)n).QueryACL(p, pr, out linkFound, out error, out reparse, out link, out acl); } } public override bool StoreACL(StringBuilder! p, Principal pr, Acl acl, out bool linkFound, out ErrorCode error, out bool reparse, out string link ) { Node n; atomic { linkFound = false; reparse = false; link = null; error = ErrorCode.NoError; if (CheckEmpty(p)) { if (!CheckAccess(DirPermissions.AccessModeSetAcl, pr)) { error = ErrorCode.AccessDenied; return false; } SetInstanceAcl(acl); return true; } n = GetNextNode(p, pr, out error); if (n == null) { return false; } return ((!)n).StoreACL(p, pr, acl, out linkFound, out error, out reparse, out link); } } } }