/// <inheritdoc /> /// <exception cref="UnauthorizedAccessException">Thrown when trying to open or modify a file/directory without having sufficient access permissions.</exception> /// <exception cref="FileNotFoundException">Thrown when a file/directory or parts of its path cannot be found.</exception> /// <exception cref="ArgumentException">Thrown when the provided ACL is invalid.</exception> /// <exception cref="NativeException">Generic exception thrown when a native method fails, and the error was not covered by one of the other possible exceptions. This exception is also always included as the <see cref="Exception.InnerException"/>.</exception> public void SetPermissionData(string fileName, int setDefaultAcl, ref NativePermissionDataContainer dataContainer, AccessControlListEntry[] entries) { // Ensure exclusive access to native functions lock (_nativeFunctionsLock) { // Make sure the meta data object is valid dataContainer.AclSize = entries.Length; // Set permissions and ACL NativeErrorCodes err = SetFilePermissionDataAndAcl(fileName, setDefaultAcl, ref dataContainer, entries); if (err != NativeErrorCodes.NATIVE_ERROR_SUCCESS) { // Throw suitable exceptions var nativeException = RetrieveErrnoAndBuildException(nameof(SetFilePermissionDataAndAcl), err, out var _, out var errnoSymbolic); switch (err) { // Handle certain special exception cases case NativeErrorCodes.NATIVE_ERROR_OPEN_FAILED when errnoSymbolic == Errno.EACCES: throw new UnauthorizedAccessException($"Could not open \"{fileName}\" for reading.", nativeException); case NativeErrorCodes.NATIVE_ERROR_OPEN_FAILED when errnoSymbolic == Errno.ENOENT: throw new FileNotFoundException($"Could not open \"{fileName}\" for reading.", nativeException); case NativeErrorCodes.NATIVE_ERROR_CHOWN_FAILED when errnoSymbolic == Errno.EPERM: throw new UnauthorizedAccessException($"Permission denied when using fchown() on \"{fileName}\".", nativeException); case NativeErrorCodes.NATIVE_ERROR_CHMOD_FAILED when errnoSymbolic == Errno.EPERM: throw new UnauthorizedAccessException($"Permission denied when using fchmod() on \"{fileName}\".", nativeException); case NativeErrorCodes.NATIVE_ERROR_VALIDATE_ACL_FAILED when errnoSymbolic == Errno.EINVAL: throw new ArgumentException($"The given ACL is invalid.", nativeException); case NativeErrorCodes.NATIVE_ERROR_SET_ACL_FAILED when errnoSymbolic == Errno.EINVAL: throw new ArgumentException($"Could not assign ACL to file \"{fileName}\" using acl_set_fd().", nativeException); case NativeErrorCodes.NATIVE_ERROR_SET_ACL_FAILED when errnoSymbolic == Errno.EPERM: throw new UnauthorizedAccessException($"Permission denied when assigning ACL using acl_set_fd() on \"{fileName}\".", nativeException); // Unhandled case, just throw generic exception directly default: throw nativeException; } } } }
/// <summary> /// <para>Applies the contained permissions to the given file or directory.</para> /// <para> /// Order in ACL: /// <list type="number"> /// <item>UserObj (owner permissions)</item> /// <item>GroupObj (group permissions)</item> /// <item>Other (other permissions)</item> /// <item>User (permissions of other users)</item> /// <item>Group (permissions of other groups)</item> /// <item>Mask (bitwise OR of all group permissions)</item> /// </list> /// </para> /// </summary> /// <param name="fullPath">The file or directory to apply the permissions to.</param> /// <param name="asDefault">Optional. Specifies whether to update a directory's default ACL. When using this option, the contained UNIX permissions are not applied to the file (chown/chmod), but are still assumed as consistent! This must be false for files.</param> internal void ApplyPermissions(string fullPath, bool asDefault) { // Calculate ACL size first int aclSize = 3 + _aclUserPermissions.Count + _aclGroupPermissions.Count + 1; // Collect base permissions NativePermissionDataContainer dataContainer = new NativePermissionDataContainer() { AclSize = aclSize, // These are ignored when a directory's default ACL is set OwnerId = OwnerId, OwnerPermissions = OwnerPermissions, GroupId = GroupId, GroupPermissions = GroupPermissions, OtherPermissions = OtherPermissions }; // Allocate ACL AccessControlListEntry[] aclEntries = new AccessControlListEntry[aclSize]; int pos = 0; // Add UNIX permissions aclEntries[pos++] = new AccessControlListEntry { TagType = AccessControlListEntryTagTypes.UserObj, TagQualifier = OwnerId, Permissions = OwnerPermissions }; aclEntries[pos++] = new AccessControlListEntry { TagType = AccessControlListEntryTagTypes.GroupObj, TagQualifier = GroupId, Permissions = GroupPermissions }; aclEntries[pos++] = new AccessControlListEntry { TagType = AccessControlListEntryTagTypes.Other, TagQualifier = -1, Permissions = OtherPermissions }; // Add user and group entries FilePermissions aclMask = GroupPermissions | OtherPermissions; foreach (var perm in _aclUserPermissions) { aclMask |= perm.Value; aclEntries[pos++] = new AccessControlListEntry { TagType = AccessControlListEntryTagTypes.User, TagQualifier = perm.Key, Permissions = perm.Value }; } foreach (var perm in _aclGroupPermissions) { aclMask |= perm.Value; aclEntries[pos++] = new AccessControlListEntry { TagType = AccessControlListEntryTagTypes.Group, TagQualifier = perm.Key, Permissions = perm.Value }; } // Add mask aclEntries[pos++] = new AccessControlListEntry { TagType = AccessControlListEntryTagTypes.Mask, TagQualifier = -1, Permissions = aclMask }; // Apply permissions _nativeLibraryInterface.SetPermissionData(fullPath, asDefault ? 1 : 0, ref dataContainer, aclEntries); // TODO handle/document exceptions }
/// <inheritdoc /> /// <exception cref="UnauthorizedAccessException">Thrown when trying to open a file/directory without having sufficient access permissions.</exception> /// <exception cref="FileNotFoundException">Thrown when a file/directory or parts of its path cannot be found.</exception> /// <exception cref="NativeException">Generic exception thrown when a native method fails, and the error was not covered by one of the other possible exceptions. This exception is also always included as the <see cref="Exception.InnerException"/>.</exception> public AccessControlListEntry[] GetPermissionData(string fileName, int loadDefaultAcl, out NativePermissionDataContainer dataContainer) { // Ensure exclusive access to native functions lock (_nativeFunctionsLock) { // Read permission data and retrieve ACL size NativeErrorCodes err = OpenFileAndReadPermissionData(fileName, loadDefaultAcl, out dataContainer); if (err != NativeErrorCodes.NATIVE_ERROR_SUCCESS) { // Throw suitable exceptions var nativeException = RetrieveErrnoAndBuildException(nameof(OpenFileAndReadPermissionData), err, out var _, out var errnoSymbolic); switch (err) { // Handle certain special exception cases case NativeErrorCodes.NATIVE_ERROR_OPEN_FAILED when errnoSymbolic == Errno.EACCES: throw new UnauthorizedAccessException($"Could not open \"{fileName}\" for reading.", nativeException); case NativeErrorCodes.NATIVE_ERROR_OPEN_FAILED when errnoSymbolic == Errno.ENOENT: throw new FileNotFoundException($"Could not open \"{fileName}\" for reading.", nativeException); // Unhandled case, just throw generic exception directly default: throw nativeException; } } // Read ACL AccessControlListEntry[] acl = new AccessControlListEntry[dataContainer.AclSize]; err = ReadFileAclAndClose(acl); if (err != NativeErrorCodes.NATIVE_ERROR_SUCCESS) { throw RetrieveErrnoAndBuildException(nameof(ReadFileAclAndClose), err, out var _, out var _); } return(acl); } }
private static extern NativeErrorCodes SetFilePermissionDataAndAcl([In, MarshalAs(UnmanagedType.LPUTF8Str)] string fileName, [In] int setDefaultAcl, [In] ref NativePermissionDataContainer dataContainer, [In] AccessControlListEntry[] entries);
private static extern NativeErrorCodes OpenFileAndReadPermissionData([In, MarshalAs(UnmanagedType.LPUTF8Str)] string fileName, [In] int loadDefaultAcl, [Out] out NativePermissionDataContainer dataContainer);