/// <summary> /// Combines the exception data into a single generic message string. /// </summary> /// <param name="nativeMethodName">Name of the native API method which returned the error code.</param> /// <param name="errorCode">Native error code of this exception.</param> /// <param name="errno">Value of errno.</param> /// <param name="errnoString">The string representation of errno.</param> private static string BuildExceptionMessageString(string nativeMethodName, NativeErrorCodes errorCode, long errno, string errnoString) { string prefix = $"Error in {nativeMethodName}: "; string functionErrnoSuffix = $"() failed with errno = {errnoString} [{errno}]."; return(prefix + errorCode switch { NativeErrorCodes.NATIVE_ERROR_OPEN_FAILED => prefix + "open" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_FSTAT_FAILED => prefix + "fstat" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_GET_ACL_FAILED => prefix + "acl_get_fd" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_GET_ACL_ENTRY_FAILED => prefix + "acl_get_entry" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_GET_ACL_ENTRY_TAG_TYPE_FAILED => prefix + "acl_get_tag_type" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_GET_ACL_ENTRY_QUALIFIER_FAILED => prefix + "acl_get_qualifier" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_GET_ACL_ENTRY_PERMSET_FAILED => prefix + "acl_get_permset" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_GET_ACL_ENTRY_PERM_FAILED => prefix + "acl_get_perm" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_CHOWN_FAILED => prefix + "fchown" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_CHMOD_FAILED => prefix + "fchmod" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_INIT_ACL_FAILED => prefix + "acl_init" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_CREATE_ACL_ENTRY_FAILED => prefix + "acl_create_entry" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_INVALID_TAG_TYPE => prefix + "The given entry tag type was invalid.", NativeErrorCodes.NATIVE_ERROR_SET_ACL_ENTRY_TAG_TYPE_FAILED => prefix + "acl_set_tag_type" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_SET_ACL_ENTRY_QUALIFIER_FAILED => prefix + "acl_set_qualifier" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_CLEAR_ACL_ENTRY_PERMS_FAILED => prefix + "acl_clear_perms" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_ADD_ACL_ENTRY_PERM_FAILED => prefix + "acl_add_perm" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_VALIDATE_ACL_FAILED => prefix + "acl_valid" + functionErrnoSuffix, NativeErrorCodes.NATIVE_ERROR_SET_ACL_FAILED => prefix + "acl_set_file" + functionErrnoSuffix, _ => "Unknown native error.", });
/// <summary> /// Creates a new exception from the given native error code and errno value, using the given exception message. /// </summary> /// <param name="nativeFunctionName">Name of the native API method which returned the error code.</param> /// <param name="errorCode">Native error code of this exception.</param> /// <param name="errno">Value of errno (or 0, if not applicable).</param> /// <param name="errnoString">The string representation of errno (if applicable).</param> /// <param name="message">The exception message to display.</param> protected NativeException(string nativeMethodName, NativeErrorCodes errorCode, long errno, string errnoString, string message) : base(message) { NativeMethodName = nativeMethodName; ErrorCode = errorCode; Errno = errno; ErrnoString = errnoString; }
/// <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; } } } }
/// <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> /// Creates a new exception from the given native error code and errno value. /// </summary> /// <param name="nativeFunctionName">Name of the native API method which returned the error code.</param> /// <param name="errorCode">Native error code of this exception.</param> /// <param name="errno">Value of errno (or 0, if not applicable).</param> /// <param name="errnoString">The string representation of errno (if applicable).</param> internal NativeException(string nativeMethodName, NativeErrorCodes errorCode, long errno, string errnoString) : this(nativeMethodName, errorCode, errno, errnoString, BuildExceptionMessageString(nativeMethodName, errorCode, errno, errnoString)) { }
/// <summary> /// Retrieves the error information from the native implementation and constructs a new <see cref="NativeException"/> object, that can be thrown afterwards. /// </summary> /// <param name="nativeMethodName">Name of the native API method which returned the error code.</param> /// <param name="errorCode">The error code returned by the native API method.</param> /// <param name="errnoResolvable">This will tell whether the retrieved errno could be resolved into a symbolic representation.</param> /// <param name="errnoSymbolic">This will hold the symbolic value of the retrieved errno; check the <paramref name="errnoResolvable"/> parameter beforehand!</param> private NativeException RetrieveErrnoAndBuildException(string nativeMethodName, NativeErrorCodes errorCode, out bool errnoResolvable, out Errno errnoSymbolic) { // Retrieve errno var errnoStringBuffer = new StringBuilder(256); long errno = GetLastErrnoValue(errnoStringBuffer, errnoStringBuffer.Capacity); // Try to resolve to symbolic representation errnoResolvable = NativeConvert.TryToErrno((int)errno, out errnoSymbolic); // Create native exception object return(new NativeException(nativeMethodName, errorCode, errno, (errnoResolvable ? errnoSymbolic.ToString() + ", " : "") + errnoStringBuffer.ToString())); }