////////////////////////////////////////////////////////////////////////////////
//
// 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);
}
}
}
}