context: //
  // System.Security.AccessControl.NativeObjectSecurity implementation
  //
  // Authors:
  //	Dick Porter  <dick@ximian.com>
  //	James Bellinger  <jfb@zer7.com>
  //
  // Copyright (C) 2005, 2006 Novell, Inc (http://www.novell.com)
  // Copyright (C) 2012       James Bellinger
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  using System;
  using System.IO;
  using System.Runtime.InteropServices;
  using System.Security.Principal;
  
  namespace System.Security.AccessControl
  {
  	public abstract class NativeObjectSecurity : CommonObjectSecurity
  	{
  		ExceptionFromErrorCode exception_from_error_code;
  		ResourceType resource_type;
  		
  		protected internal delegate Exception ExceptionFromErrorCode (int errorCode,
  									      string name, SafeHandle handle,
  									      object context);
  		
  		internal NativeObjectSecurity (CommonSecurityDescriptor securityDescriptor, ResourceType resourceType)
  			: base (securityDescriptor)
  		{
  			resource_type = resourceType;
  		}
  
  		protected NativeObjectSecurity (bool isContainer,
  						ResourceType resourceType)
  			: this (isContainer, resourceType, null, null)
  		{
  		}
  
  		protected NativeObjectSecurity (bool isContainer,
  						ResourceType resourceType,
  						ExceptionFromErrorCode exceptionFromErrorCode,
  						object exceptionContext)
  			: base (isContainer)
  		{
  			exception_from_error_code = exceptionFromErrorCode;
  			resource_type = resourceType;
  		}
  		
  		protected NativeObjectSecurity (bool isContainer,
  						ResourceType resourceType,
  						SafeHandle handle,
  						AccessControlSections includeSections)
  			: this (isContainer, resourceType, handle, includeSections, null, null)
  		{
  		}
  		
  		protected NativeObjectSecurity (bool isContainer,
  						ResourceType resourceType,
  						string name,
  						AccessControlSections includeSections)
  			: this (isContainer, resourceType, name, includeSections, null, null)
  		{
  		}
  		
  		protected NativeObjectSecurity (bool isContainer,
  						ResourceType resourceType,
  						SafeHandle handle,
  						AccessControlSections includeSections,
  						ExceptionFromErrorCode exceptionFromErrorCode,
  						object exceptionContext)
  			: this (isContainer, resourceType, exceptionFromErrorCode, exceptionContext)
  		{
  			RaiseExceptionOnFailure (InternalGet (handle, includeSections),
  						 null, handle, exceptionContext);
  			 ClearAccessControlSectionsModified ();
  		}
  		
  		protected NativeObjectSecurity (bool isContainer,
  						ResourceType resourceType,
  						string name,
  						AccessControlSections includeSections,
  						ExceptionFromErrorCode exceptionFromErrorCode,
  						object exceptionContext)
  			: this (isContainer, resourceType, exceptionFromErrorCode, exceptionContext)
  		{
  			RaiseExceptionOnFailure (InternalGet (name, includeSections),
  						 name, null, exceptionContext);
  			ClearAccessControlSectionsModified ();
  		}
  
  		void ClearAccessControlSectionsModified ()
  		{
  			WriteLock ();
  			try {
  				AccessControlSectionsModified = AccessControlSections.None;
  			} finally {
  				WriteUnlock ();
  			}
  		}
  		
  		protected override sealed void Persist (SafeHandle handle,
  							AccessControlSections includeSections)
  		{
  			Persist (handle, includeSections, null);
  		}
  		
  		protected override sealed void Persist (string name,
  							AccessControlSections includeSections)
  		{
  			Persist (name, includeSections, null);
  		}
  		
  		internal void PersistModifications (SafeHandle handle)
  		{
  			WriteLock();
  			try {
  				Persist (handle, AccessControlSectionsModified, null);
  			} finally {
  				WriteUnlock ();
  			}
  		}
  		
  		protected void Persist (SafeHandle handle,
  					AccessControlSections includeSections,
  					object exceptionContext)
  		{
  			WriteLock ();
  			try {
  				RaiseExceptionOnFailure (InternalSet (handle, includeSections), null, handle, exceptionContext);
  				AccessControlSectionsModified &= ~includeSections;
  			} finally {
  				WriteUnlock ();
  			}
  		}
  
  		internal void PersistModifications (string name)
  		{
  			WriteLock();
  			try {
  				Persist (name, AccessControlSectionsModified, null);
  			} finally {
  				WriteUnlock ();
  			}
  		}
  		
  		protected void Persist (string name,
  					AccessControlSections includeSections,
  					object exceptionContext)
  		{
  			if (null == name)
  				throw new ArgumentNullException ("name");
  
  			WriteLock ();
  			try {
  				RaiseExceptionOnFailure (InternalSet (name, includeSections), name, null, exceptionContext);
  				AccessControlSectionsModified &= ~includeSections;
  			} finally {
  				WriteUnlock ();
  			}
  		}
  		
  		internal static Exception DefaultExceptionFromErrorCode (int errorCode,
  									 string name, SafeHandle handle,
  									 object context)
  		{
  			switch (errorCode) {
  				case 2: return new FileNotFoundException ();
  				case 3: return new DirectoryNotFoundException ();
  				case 5: return new UnauthorizedAccessException ();
  				case 1314: return new PrivilegeNotHeldException (); // happens with audit rules
  				default: return new InvalidOperationException ("OS error code " + errorCode.ToString());
  			}
  		}
  		
  		void RaiseExceptionOnFailure (int errorCode, string name, SafeHandle handle, object context)
  		{
  			if (errorCode == 0) return;
  			throw (exception_from_error_code ?? DefaultExceptionFromErrorCode)(errorCode, name, handle, context);
  		}
  		
  		// InternalGet/InternalSet are virtual so that non-Windows platforms which do not share an
  		// API between files, mutexes, etc. can override in the subclass and do their own thing.
  		internal virtual int InternalGet (SafeHandle handle,
  						  AccessControlSections includeSections)
  		{
  			if (Environment.OSVersion.Platform != PlatformID.Win32NT)
  				throw new PlatformNotSupportedException ();
  
  			return Win32GetHelper (delegate (SecurityInfos securityInfos,
  							 out IntPtr owner, out IntPtr group,
  							 out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor)
  				{
  					return GetSecurityInfo (handle, ResourceType, securityInfos,
  								out owner, out group,
  								out dacl, out sacl, out descriptor);
  				}, includeSections);
  		}
  		
  		internal virtual int InternalGet (string name,
  						  AccessControlSections includeSections)
  		{
  			if (Environment.OSVersion.Platform != PlatformID.Win32NT)
  				throw new PlatformNotSupportedException ();
  
  			return Win32GetHelper (delegate (SecurityInfos securityInfos,
  							 out IntPtr owner, out IntPtr group,
  							 out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor)
  				{
  					return GetNamedSecurityInfo (Win32FixName (name), ResourceType, securityInfos,
  								     out owner, out group,
  								     out dacl, out sacl, out descriptor);
  				}, includeSections);
  		}
  		
  		internal virtual int InternalSet (SafeHandle handle,
  						  AccessControlSections includeSections)
  		{
  			if (Environment.OSVersion.Platform != PlatformID.Win32NT)
  				throw new PlatformNotSupportedException ();
  
  			return Win32SetHelper ((securityInfos, owner, group, dacl, sacl) =>
  				SetSecurityInfo (handle, ResourceType, securityInfos, owner, group, dacl, sacl),
  				includeSections);
  		}
  		
  		internal virtual int InternalSet (string name,
  						  AccessControlSections includeSections)
  		{	
  			if (Environment.OSVersion.Platform != PlatformID.Win32NT)
  				throw new PlatformNotSupportedException ();
  		
  			return Win32SetHelper ((securityInfos, owner, group, dacl, sacl) =>
  				SetNamedSecurityInfo (Win32FixName (name), ResourceType, securityInfos, owner, group, dacl, sacl),
  				includeSections);
  		}
  		
  		internal ResourceType ResourceType {
  			get { return resource_type; }
  		}
  		
  		#region Win32 Details		
  		int Win32GetHelper (GetSecurityInfoNativeCall nativeCall,
  				    AccessControlSections includeSections)
  		{
  			bool getOwner = 0 != (includeSections & AccessControlSections.Owner);
  			bool getGroup = 0 != (includeSections & AccessControlSections.Group);
  			bool getDacl = 0 != (includeSections & AccessControlSections.Access);
  			bool getSacl = 0 != (includeSections & AccessControlSections.Audit);
  			
  			SecurityInfos securityInfos = 0;
  			if (getOwner) securityInfos |= SecurityInfos.Owner;
  			if (getGroup) securityInfos |= SecurityInfos.Group;
  			if (getDacl ) securityInfos |= SecurityInfos.DiscretionaryAcl;
  			if (getSacl ) securityInfos |= SecurityInfos.SystemAcl;
  			
  			IntPtr owner, group, dacl, sacl, descriptor;
  			int result = nativeCall (securityInfos,
  						 out owner, out group, out dacl, out sacl, out descriptor);
  			if (0 != result) return result;
  			
  			try {
  				int binaryLength = 0;
  				if (IsValidSecurityDescriptor (descriptor))
  					binaryLength = GetSecurityDescriptorLength (descriptor);
  					
  				byte[] binaryForm = new byte[binaryLength];
  				Marshal.Copy (descriptor, binaryForm, 0, binaryLength);
  				SetSecurityDescriptorBinaryForm (binaryForm, includeSections);
  			} finally {
  				LocalFree (descriptor);
  			}
  			return 0;
  		}
  		
  		int Win32SetHelper (SetSecurityInfoNativeCall nativeCall,
  				    AccessControlSections includeSections)
  		{
  			// SE_REGISTRY_KEY will fail UnauthorizedAccessException without this check.
  			if (AccessControlSections.None == includeSections) return 0;
  			
  			SecurityInfos securityInfos = 0;
  			byte[] owner = null, group = null, dacl = null, sacl = null;
  			
  			if (0 != (includeSections & AccessControlSections.Owner)) {
  
gt: 				securityInfos |= SecurityInfos.Owner;