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