diff options
Diffstat (limited to 'src/mscorlib/shared/Microsoft/Win32/SafeHandles')
3 files changed, 87 insertions, 11 deletions
diff --git a/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index d13b536204..f28f44fdad 100644 --- a/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; +using System.IO; using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles @@ -38,18 +39,30 @@ namespace Microsoft.Win32.SafeHandles internal static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int mode) { Debug.Assert(path != null); + SafeFileHandle handle = Interop.Sys.Open(path, flags, mode); - // 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. - bool enoentDueToDirectory = (flags & Interop.Sys.OpenFlags.O_CREAT) != 0; - - // Open the file. - SafeFileHandle handle = Interop.CheckIo( - Interop.Sys.Open(path, flags, mode), - path, - isDirectory: enoentDueToDirectory, - errorRewriter: e => (e.Error == Interop.Error.EISDIR) ? Interop.Error.EACCES.Info() : e); + if (handle.IsInvalid) + { + handle.Dispose(); + Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); + + // 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(Path.GetDirectoryName(PathInternal.TrimEndingDirectorySeparator(path)))); + + Interop.CheckIo( + error.Error, + path, + isDirectory, + errorRewriter: e => (e.Error == Interop.Error.EISDIR) ? Interop.Error.EACCES.Info() : e); + } // Make sure it's not a directory; we do this after opening it once we have a file descriptor // to avoid race conditions. @@ -68,6 +81,31 @@ namespace Microsoft.Win32.SafeHandles return handle; } + private static bool DirectoryExists(string fullPath) + { + int fileType = Interop.Sys.FileTypes.S_IFDIR; + + Interop.Sys.FileStatus fileinfo; + Interop.ErrorInfo errorInfo = default(Interop.ErrorInfo); + + // First use stat, as we want to follow symlinks. If that fails, it could be because the symlink + // is broken, we don't have permissions, etc., in which case fall back to using LStat to evaluate + // based on the symlink itself. + if (Interop.Sys.Stat(fullPath, out fileinfo) < 0 && + Interop.Sys.LStat(fullPath, out fileinfo) < 0) + { + errorInfo = Interop.Sys.GetLastErrorInfo(); + return false; + } + + // Something exists at this path. If the caller is asking for a directory, return true if it's + // a directory and false for everything else. If the caller is asking for a file, return false for + // a directory and true for everything else. + return + (fileType == Interop.Sys.FileTypes.S_IFDIR) == + ((fileinfo.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR); + } + /// <summary>Opens a SafeFileHandle for a file descriptor created by a provided delegate.</summary> /// <param name="fdFunc"> /// The function that creates the file descriptor. Returns the file descriptor on success, or an invalid diff --git a/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeHandleMinusOneIsInvalid.cs b/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeHandleMinusOneIsInvalid.cs new file mode 100644 index 0000000000..5415f2c35d --- /dev/null +++ b/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeHandleMinusOneIsInvalid.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.Win32.SafeHandles +{ + // Class of safe handle which uses only -1 as an invalid handle. + public abstract class SafeHandleMinusOneIsInvalid : SafeHandle + { + protected SafeHandleMinusOneIsInvalid(bool ownsHandle) : base(new IntPtr(-1), ownsHandle) + { + } + + public override bool IsInvalid => handle == new IntPtr(-1); + } +} diff --git a/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeHandleZeroOrMinusOneIsInvalid.cs b/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeHandleZeroOrMinusOneIsInvalid.cs new file mode 100644 index 0000000000..8d0220bf90 --- /dev/null +++ b/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeHandleZeroOrMinusOneIsInvalid.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.Win32.SafeHandles +{ + // Class of safe handle which uses 0 or -1 as an invalid handle. + public abstract class SafeHandleZeroOrMinusOneIsInvalid : SafeHandle + { + protected SafeHandleZeroOrMinusOneIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) + { + } + + public override bool IsInvalid => handle == IntPtr.Zero || handle == new IntPtr(-1); + } +} |