summaryrefslogtreecommitdiff
path: root/src/mscorlib/shared/Microsoft/Win32/SafeHandles
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/shared/Microsoft/Win32/SafeHandles')
-rw-r--r--src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs60
-rw-r--r--src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeHandleMinusOneIsInvalid.cs19
-rw-r--r--src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeHandleZeroOrMinusOneIsInvalid.cs19
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);
+ }
+}