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