diff --git a/src/libraries/Common/src/Interop/Unix/Interop.IOErrors.cs b/src/libraries/Common/src/Interop/Unix/Interop.IOErrors.cs index 137b39405962ed..56c82686621b04 100644 --- a/src/libraries/Common/src/Interop/Unix/Interop.IOErrors.cs +++ b/src/libraries/Common/src/Interop/Unix/Interop.IOErrors.cs @@ -8,19 +8,19 @@ internal static partial class Interop { - private static void ThrowExceptionForIoErrno(ErrorInfo errorInfo, string? path, bool isDirectory) + private static void ThrowExceptionForIoErrno(ErrorInfo errorInfo, string? path, bool isDirError) { Debug.Assert(errorInfo.Error != Error.SUCCESS); Debug.Assert(errorInfo.Error != Error.EINTR, "EINTR errors should be handled by the native shim and never bubble up to managed code"); - throw Interop.GetExceptionForIoErrno(errorInfo, path, isDirectory); + throw Interop.GetExceptionForIoErrno(errorInfo, path, isDirError); } - internal static void CheckIo(Error error, string? path = null, bool isDirectory = false) + internal static void CheckIo(Error error, string? path = null, bool isDirError = false) { if (error != Interop.Error.SUCCESS) { - ThrowExceptionForIoErrno(error.Info(), path, isDirectory); + ThrowExceptionForIoErrno(error.Info(), path, isDirError); } } @@ -31,15 +31,15 @@ internal static void CheckIo(Error error, string? path = null, bool isDirectory /// /// The result of the system call. /// The path with which this error is associated. This may be null. - /// true if the is known to be a directory; otherwise, false. + /// true if error is caused by a directory issue. /// /// On success, returns the non-negative result long that was validated. /// - internal static long CheckIo(long result, string? path = null, bool isDirectory = false) + internal static long CheckIo(long result, string? path = null, bool isDirError = false) { if (result < 0) { - ThrowExceptionForIoErrno(Sys.GetLastErrorInfo(), path, isDirectory); + ThrowExceptionForIoErrno(Sys.GetLastErrorInfo(), path, isDirError); } return result; @@ -53,9 +53,9 @@ internal static long CheckIo(long result, string? path = null, bool isDirectory /// /// On success, returns the non-negative result int that was validated. /// - internal static int CheckIo(int result, string? path = null, bool isDirectory = false) + internal static int CheckIo(int result, string? path = null, bool isDirError = false) { - CheckIo((long)result, path, isDirectory); + CheckIo((long)result, path, isDirError); return result; } @@ -68,9 +68,9 @@ internal static int CheckIo(int result, string? path = null, bool isDirectory = /// /// On success, returns the non-negative result IntPtr that was validated. /// - internal static IntPtr CheckIo(IntPtr result, string? path = null, bool isDirectory = false) + internal static IntPtr CheckIo(IntPtr result, string? path = null, bool isDirError = false) { - CheckIo((long)result, path, isDirectory); + CheckIo((long)result, path, isDirError); return result; } @@ -83,12 +83,12 @@ internal static IntPtr CheckIo(IntPtr result, string? path = null, bool isDirect /// /// On success, returns the valid SafeFileHandle that was validated. /// - internal static TSafeHandle CheckIo(TSafeHandle handle, string? path = null, bool isDirectory = false) + internal static TSafeHandle CheckIo(TSafeHandle handle, string? path = null, bool isDirError = false) where TSafeHandle : SafeHandle { if (handle.IsInvalid) { - Exception e = Interop.GetExceptionForIoErrno(Sys.GetLastErrorInfo(), path, isDirectory); + Exception e = Interop.GetExceptionForIoErrno(Sys.GetLastErrorInfo(), path, isDirError); handle.Dispose(); throw e; } @@ -101,27 +101,29 @@ internal static TSafeHandle CheckIo(TSafeHandle handle, string? pat /// /// The error info /// The path with which this error is associated. This may be null. - /// true if the is known to be a directory; otherwise, false. + /// true if error is caused by a directory issue. /// - internal static Exception GetExceptionForIoErrno(ErrorInfo errorInfo, string? path = null, bool isDirectory = false) + internal static Exception GetExceptionForIoErrno(ErrorInfo errorInfo, string? path = null, bool isDirError = false) { // Translate the errno into a known set of exception types. For cases where multiple errnos map // to the same exception type, include an inner exception with the details. switch (errorInfo.Error) { case Error.ENOENT: - if (isDirectory) - { - return !string.IsNullOrEmpty(path) ? - new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, path)) : - new DirectoryNotFoundException(SR.IO_PathNotFound_NoPathName); - } - else + // For Windows compatibility, throw DirectoryNotFoundException instead of FileNotFoundException + // when the parent folder does not exist. + if (!isDirError && (path is null || ParentDirectoryExists(path))) { return !string.IsNullOrEmpty(path) ? new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, path), path) : new FileNotFoundException(SR.IO_FileNotFound); } + goto case Error.ENOTDIR; + + case Error.ENOTDIR: + return !string.IsNullOrEmpty(path) ? + new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, path)) : + new DirectoryNotFoundException(SR.IO_PathNotFound_NoPathName); case Error.EACCES: case Error.EBADF: @@ -156,6 +158,13 @@ internal static Exception GetExceptionForIoErrno(ErrorInfo errorInfo, string? pa default: return GetIOException(errorInfo, path); + + static bool ParentDirectoryExists(string fullPath) + { + string? parentPath = Path.GetDirectoryName(Path.TrimEndingDirectorySeparator(fullPath)); + + return Directory.Exists(parentPath); + } } } diff --git a/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Unix.cs b/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Unix.cs index e1eb1b53e0ee43..dc42bde044b0c6 100644 --- a/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Unix.cs +++ b/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveInfo.Unix.cs @@ -90,7 +90,7 @@ private void CheckStatfsResultAndThrowIfNecessary(int result) } else { - throw Interop.GetExceptionForIoErrno(errorInfo, isDirectory: true); + throw Interop.GetExceptionForIoErrno(errorInfo); } } } diff --git a/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs b/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs index 6439e2496ad616..2cc4226613ff26 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Linux.cs @@ -787,8 +787,7 @@ private bool TryReadEvent(out NotifyEvent notifyEvent) { fixed (byte* buf = &_buffer[0]) { - _bufferAvailable = Interop.CheckIo(Interop.Sys.Read(_inotifyHandle, buf, this._buffer.Length), - isDirectory: true); + _bufferAvailable = Interop.CheckIo(Interop.Sys.Read(_inotifyHandle, buf, this._buffer.Length)); Debug.Assert(_bufferAvailable <= this._buffer.Length); } } diff --git a/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.OSX.cs b/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.OSX.cs index ba8cc8f4ee1ece..c561022ed66c87 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.OSX.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.OSX.cs @@ -160,7 +160,7 @@ internal unsafe RunningInstance( _fullDirectory = Interop.Sys.RealPath(_fullDirectory); if (_fullDirectory is null) { - throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, isDirectory: true); + throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, isDirError: true); } // Also ensure it has a trailing slash. diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index b30b9ffc44129c..fb5c8faad46880 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -105,43 +105,17 @@ private static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int throw ex; } - // If we fail to open the file due to a path not existing, we need to know whether to blame - // the file itself or its directory. If we're creating the file, then we blame the directory, - // otherwise we blame the file. - // - // When opening, we need to align with Windows, which considers a missing path to be - // FileNotFound only if the containing directory exists. - - bool isDirectory = (error.Error == Interop.Error.ENOENT) && - ((flags & Interop.Sys.OpenFlags.O_CREAT) != 0 - || !DirectoryExists(System.IO.Path.GetDirectoryName(System.IO.Path.TrimEndingDirectorySeparator(path!))!)); - if (error.Error == Interop.Error.EISDIR) { error = Interop.Error.EACCES.Info(); } - Interop.CheckIo( - error.Error, - path, - isDirectory); + Interop.CheckIo(error.Error, path); } return handle; } - private static bool DirectoryExists(string fullPath) - { - Interop.Sys.FileStatus fileinfo; - - if (Interop.Sys.Stat(fullPath, out fileinfo) < 0) - { - return false; - } - - return ((fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR); - } - // Each thread will have its own copy. This prevents race conditions if the handle had the last error. [ThreadStatic] internal static Interop.ErrorInfo? t_lastCloseErrorInfo; @@ -336,7 +310,7 @@ private bool Init(string path, FileMode mode, FileAccess access, FileShare share if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) { - throw Interop.GetExceptionForIoErrno(Interop.Error.EACCES.Info(), path, isDirectory: true); + throw Interop.GetExceptionForIoErrno(Interop.Error.EACCES.Info(), path); } if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFREG) @@ -367,7 +341,7 @@ private bool Init(string path, FileMode mode, FileAccess access, FileShare share Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); if (errorInfo.Error == Interop.Error.EWOULDBLOCK) { - throw Interop.GetExceptionForIoErrno(errorInfo, path, isDirectory: false); + throw Interop.GetExceptionForIoErrno(errorInfo, path); } } @@ -434,7 +408,7 @@ private bool Init(string path, FileMode mode, FileAccess access, FileShare share // We know the file descriptor is valid and we know the size argument to FTruncate is correct, // so if EBADF or EINVAL is returned, it means we're dealing with a special file that can't be // truncated. Ignore the error in such cases; in all others, throw. - throw Interop.GetExceptionForIoErrno(errorInfo, path, isDirectory: false); + throw Interop.GetExceptionForIoErrno(errorInfo, path); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.UnixOrBrowser.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.UnixOrBrowser.cs index 846147ec3a7bc6..3f04333e6b3932 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.UnixOrBrowser.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.UnixOrBrowser.cs @@ -17,7 +17,7 @@ public static partial class Environment private static string CurrentDirectoryCore { get => Interop.Sys.GetCwd(); - set => Interop.CheckIo(Interop.Sys.ChDir(value), value, isDirectory: true); + set => Interop.CheckIo(Interop.Sys.ChDir(value), value, isDirError: true); } private static string ExpandEnvironmentVariablesCore(string name) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs index 7262476bee23df..a13ed274b7e640 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs @@ -76,7 +76,7 @@ private IntPtr CreateDirectoryHandle(string path, bool ignoreNotFound = false) { return IntPtr.Zero; } - throw Interop.GetExceptionForIoErrno(info, path, isDirectory: true); + throw Interop.GetExceptionForIoErrno(info, path, isDirError: true); } return handle; } @@ -198,7 +198,7 @@ private unsafe void FindNextEntry(byte* entryBufferPtr, int bufferLength) } else { - throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(result), _currentPath, isDirectory: true); + throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(result), _currentPath, isDirError: true); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStatus.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStatus.Unix.cs index 1adeaa5decb85b..fe14a0aa99a19f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStatus.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStatus.Unix.cs @@ -11,14 +11,15 @@ internal partial struct FileStatus { private const int NanosecondsPerTick = 100; - private const int InitializedExistsBrokenLink = -4; // target is link with no target. - private const int InitializedExistsDir = -3; // target is directory. - private const int InitializedExistsFile = -2; // target is file. + private const int InitializedExistsBrokenLink = -5; // target is link with no target. + private const int InitializedExistsDir = -4; // target is directory. + private const int InitializedExistsFile = -3; // target is file. + private const int InitializedNotExistsNotADir = -2; // entry parent path is not a dir. private const int InitializedNotExists = -1; // entry does not exist. private const int Uninitialized = 0; // uninitialized, '0' to make default(FileStatus) uninitialized. // Tracks the initialization state. - // < 0 : initialized successfully. Value is InitializedNotExists, InitializedExistsFile, InitializedExistsDir or InitializedExistsBrokenLink. + // < 0 : initialized succesfully. Value is one of the Initialized* consts. // 0 : uninitialized. // > 0 : initialized with error. Value is raw errno. private int _state; @@ -240,7 +241,7 @@ private void SetAttributes(SafeFileHandle? handle, string? path, FileAttributes EnsureCachesInitialized(handle, path); if (!EntryExists) - FileSystemInfo.ThrowNotFound(path); + ThrowNotFound(path); if (Interop.Sys.CanSetHiddenFlag) { @@ -385,7 +386,7 @@ private unsafe void SetAccessOrWriteTimeCore(SafeFileHandle? handle, string? pat EnsureCachesInitialized(handle, path); if (!EntryExists) - FileSystemInfo.ThrowNotFound(path); + ThrowNotFound(path); // we use utimes()/utimensat() to set the accessTime and writeTime Interop.Sys.TimeSpec* buf = stackalloc Interop.Sys.TimeSpec[2]; @@ -480,15 +481,6 @@ private void SetUnixFileMode(SafeFileHandle? handle, string? path, UnixFileMode throw new ArgumentException(SR.Arg_InvalidUnixFileMode, nameof(UnixFileMode)); } - // Use ThrowNotFound to throw the appropriate exception when the file doesn't exist. - if (handle is null && path is not null) - { - EnsureCachesInitialized(path); - - if (!EntryExists || IsBrokenLink) - FileSystemInfo.ThrowNotFound(path); - } - // Linux does not support link permissions. // To have consistent cross-platform behavior we operate on the link target. int rv = handle is not null ? Interop.Sys.FChMod(handle, (int)mode) @@ -518,15 +510,18 @@ internal void RefreshCaches(SafeFileHandle? handle, ReadOnlySpan path) { Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); - if (errorInfo.Error == Interop.Error.ENOENT || // A component of the path does not exist, or path is an empty string - errorInfo.Error == Interop.Error.ENOTDIR) // A component of the path prefix of path is not a directory + switch (errorInfo.Error) { - _state = InitializedNotExists; - } - else - { - Debug.Assert(errorInfo.RawErrno > 0); // Expect a positive integer - _state = errorInfo.RawErrno; // Initialized with error. + case Interop.Error.ENOENT: + _state = InitializedNotExists; + break; + case Interop.Error.ENOTDIR: + _state = InitializedNotExistsNotADir; + break; + default: + Debug.Assert(errorInfo.RawErrno > 0); // Expect a positive integer + _state = errorInfo.RawErrno; // Initialized with error. + break; } return; @@ -591,5 +586,11 @@ private static long UnixTimeSecondsToNanoseconds(DateTimeOffset time, long secon const long TicksPerSecond = TicksPerMillisecond * 1000; return (time.UtcDateTime.Ticks - DateTimeOffset.UnixEpoch.Ticks - seconds * TicksPerSecond) * NanosecondsPerTick; } + + private void ThrowNotFound(string? path) + { + Interop.Error error = _state == InitializedNotExistsNotADir ? Interop.Error.ENOTDIR : Interop.Error.ENOENT; + throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(error), path); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index dd28e27822605d..67237805c3a744 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -89,34 +89,23 @@ private static void LinkOrCopyFile (string sourceFullPath, string destFullPath) { // The operation failed. Within reason, try to determine which path caused the problem // so we can throw a detailed exception. - string? path = null; - bool isDirectory = false; if (errorInfo.Error == Interop.Error.ENOENT) { if (!Directory.Exists(Path.GetDirectoryName(destFullPath))) { - // The parent directory of destFile can't be found. - // Windows distinguishes between whether the directory or the file isn't found, - // and throws a different exception in these cases. We attempt to approximate that - // here; there is a race condition here, where something could change between - // when the error occurs and our checks, but it's the best we can do, and the - // worst case in such a race condition (which could occur if the file system is - // being manipulated concurrently with these checks) is that we throw a - // FileNotFoundException instead of DirectoryNotFoundexception. - path = destFullPath; - isDirectory = true; + throw Interop.GetExceptionForIoErrno(errorInfo, destFullPath, isDirError: true); } else { - path = sourceFullPath; + throw Interop.GetExceptionForIoErrno(errorInfo, sourceFullPath); } } else if (errorInfo.Error == Interop.Error.EEXIST) { - path = destFullPath; + throw Interop.GetExceptionForIoErrno(errorInfo, destFullPath); } - throw Interop.GetExceptionForIoErrno(errorInfo, path, isDirectory); + throw Interop.GetExceptionForIoErrno(errorInfo); } } @@ -125,12 +114,8 @@ public static void ReplaceFile(string sourceFullPath, string destFullPath, strin { // Unix rename works in more cases, we limit to what is allowed by Windows File.Replace. // These checks are not atomic, the file could change after a check was performed and before it is renamed. - Interop.Sys.FileStatus sourceStat; - if (Interop.Sys.LStat(sourceFullPath, out sourceStat) != 0) - { - Interop.ErrorInfo errno = Interop.Sys.GetLastErrorInfo(); - throw Interop.GetExceptionForIoErrno(errno, sourceFullPath); - } + Interop.CheckIo(Interop.Sys.LStat(sourceFullPath, out Interop.Sys.FileStatus sourceStat), sourceFullPath); + // Check source is not a directory. if ((sourceStat.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR) { @@ -208,16 +193,7 @@ public static void MoveFile(string sourceFullPath, string destFullPath, bool ove } else { - // Windows distinguishes between whether the directory or the file isn't found, - // and throws a different exception in these cases. We attempt to approximate that - // here; there is a race condition here, where something could change between - // when the error occurs and our checks, but it's the best we can do, and the - // worst case in such a race condition (which could occur if the file system is - // being manipulated concurrently with these checks) is that we throw a - // FileNotFoundException instead of DirectoryNotFoundException. - throw Interop.GetExceptionForIoErrno(errorInfo, destFullPath, - isDirectory: errorInfo.Error == Interop.Error.ENOENT && !Directory.Exists(Path.GetDirectoryName(destFullPath)) // The parent directory of destFile can't be found - ); + throw Interop.GetExceptionForIoErrno(errorInfo, destFullPath); } } @@ -305,7 +281,10 @@ public static void CreateDirectory(string fullPath, UnixFileMode unixCreateMode) return; // fullPath is '/'. } - int result = Interop.Sys.MkDir(fullPath, (int)unixCreateMode); + // macOS returns ENOTDIR when the path refers to a file and ends with '/'. + // Trim the separator so we get EEXIST instead. + ReadOnlySpan path = PathInternal.TrimEndingDirectorySeparator(fullPath.AsSpan()); + int result = Interop.Sys.MkDir(path, (int)unixCreateMode); if (result == 0) { return; // Created directory. @@ -322,7 +301,7 @@ public static void CreateDirectory(string fullPath, UnixFileMode unixCreateMode) } else { - throw Interop.GetExceptionForIoErrno(errorInfo, fullPath, isDirectory: true); + throw Interop.GetExceptionForIoErrno(errorInfo, fullPath); } } @@ -372,7 +351,7 @@ private static void CreateParentsAndDirectory(string fullPath, UnixFileMode unix } else { - throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath.ToString(), isDirectory: true); + throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath.ToString()); } i--; } while (i > 0); @@ -400,7 +379,7 @@ private static void CreateParentsAndDirectory(string fullPath, UnixFileMode unix } } - throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath.ToString(), isDirectory: true); + throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath.ToString()); } } } @@ -469,7 +448,7 @@ private static void MoveDirectory(string sourceFullPath, string destFullPath, bo case Interop.Error.ENOTDIR: // sourceFullPath exists and it's not a directory throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath)); default: - throw Interop.GetExceptionForIoErrno(errorInfo, isDirectory: true); + throw Interop.GetExceptionForIoErrno(errorInfo); } } } @@ -576,7 +555,7 @@ private static bool RemoveEmptyDirectory(string fullPath, bool topLevel = false, throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, fullPath)); } - throw Interop.GetExceptionForIoErrno(errorInfo, fullPath, isDirectory: true); + throw Interop.GetExceptionForIoErrno(errorInfo, fullPath, isDirError: true); } return true; @@ -595,7 +574,7 @@ public static FileAttributes GetAttributes(string fullPath) FileAttributes attributes = new FileInfo(fullPath, null).Attributes; if (attributes == (FileAttributes)(-1)) - FileSystemInfo.ThrowNotFound(fullPath); + throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.ENOENT), fullPath); return attributes; } @@ -614,7 +593,7 @@ public static UnixFileMode GetUnixFileMode(string fullPath) UnixFileMode mode = default(FileStatus).GetUnixFileMode(fullPath); if (mode == (UnixFileMode)(-1)) - FileSystemInfo.ThrowNotFound(fullPath); + throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.ENOENT), fullPath); return mode; } @@ -675,7 +654,7 @@ public static string[] GetLogicalDrives() internal static void CreateSymbolicLink(string path, string pathToTarget, bool isDirectory) { - Interop.CheckIo(Interop.Sys.SymLink(pathToTarget, path), path, isDirectory); + Interop.CheckIo(Interop.Sys.SymLink(pathToTarget, path), path); } internal static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget, bool isDirectory) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index fbea595b861f3d..faf3af387cc00c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -289,7 +289,7 @@ private static void GetFindData(string fullPath, bool isDirectory, bool ignoreAc // File not found doesn't make much sense coming from a directory. if (isDirectory && errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND; - if (isDirectory && errorCode == Interop.Errors.ERROR_ACCESS_DENIED && ignoreAccessDenied) + if (ignoreAccessDenied && errorCode == Interop.Errors.ERROR_ACCESS_DENIED) return; throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs index 30242825dbf8d2..0c3dc399a0874d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystemInfo.Unix.cs @@ -75,24 +75,6 @@ public void Refresh() _fileStatus.RefreshCaches(FullPath); } - internal static void ThrowNotFound(ReadOnlySpan path) - { - ThrowNotFound(path.Length == 0 ? default : path.ToString()); - } - - internal static void ThrowNotFound(string? path) - { - // Windows distinguishes between whether the directory or the file isn't found, - // and throws a different exception in these cases. We attempt to approximate that - // here; there is a race condition here, where something could change between - // when the error occurs and our checks, but it's the best we can do, and the - // worst case in such a race condition (which could occur if the file system is - // being manipulated concurrently with these checks) is that we throw a - // FileNotFoundException instead of DirectoryNotFoundException. - bool directoryError = path is not null && !FileSystem.DirectoryExists(Path.GetDirectoryName(Path.TrimEndingDirectorySeparator(path.AsSpan()))); - throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.ENOENT), path, directoryError); - } - // There is no special handling for Unix- see Windows code for the reason we do this internal string NormalizedPath => FullPath; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs index 534a49217408cb..9e2f06d2d015ff 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs @@ -23,7 +23,7 @@ internal static long CheckFileCall(long result, string? path, bool ignoreNotSupp Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); if (!(ignoreNotSupported && errorInfo.Error == Interop.Error.ENOTSUP)) { - throw Interop.GetExceptionForIoErrno(errorInfo, path, isDirectory: false); + throw Interop.GetExceptionForIoErrno(errorInfo, path); } } @@ -51,7 +51,7 @@ internal static void FlushToDisk(SafeFileHandle handle) // In such cases there's nothing to flush. break; default: - throw Interop.GetExceptionForIoErrno(errorInfo, handle.Path, isDirectory: false); + throw Interop.GetExceptionForIoErrno(errorInfo, handle.Path); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs index a36afb7aa0d295..a9d6c462610fe6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs @@ -194,7 +194,7 @@ private static unsafe void EnumerateFilesRecursively(string path, Predicate