summaryrefslogtreecommitdiff
path: root/src/mscorlib/corefx/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/corefx/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs')
-rw-r--r--src/mscorlib/corefx/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs122
1 files changed, 122 insertions, 0 deletions
diff --git a/src/mscorlib/corefx/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/mscorlib/corefx/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs
new file mode 100644
index 0000000000..5ddb31ad36
--- /dev/null
+++ b/src/mscorlib/corefx/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs
@@ -0,0 +1,122 @@
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Win32.SafeHandles
+{
+ [System.Security.SecurityCritical]
+ public sealed class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ /// <summary>A handle value of -1.</summary>
+ private static readonly IntPtr s_invalidHandle = new IntPtr(-1);
+
+ private SafeFileHandle() : this(ownsHandle: true)
+ {
+ }
+
+ private SafeFileHandle(bool ownsHandle)
+ : base(ownsHandle)
+ {
+ SetHandle(s_invalidHandle);
+ }
+
+ public SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle) : this(ownsHandle)
+ {
+ SetHandle(preexistingHandle);
+ }
+
+ internal bool? IsAsync { get; set; }
+
+ /// <summary>Opens the specified file with the requested flags and mode.</summary>
+ /// <param name="path">The path to the file.</param>
+ /// <param name="flags">The flags with which to open the file.</param>
+ /// <param name="mode">The mode for opening the file.</param>
+ /// <returns>A SafeFileHandle for the opened file.</returns>
+ internal static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int mode)
+ {
+ Debug.Assert(path != null);
+
+ // 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);
+
+ // Make sure it's not a directory; we do this after opening it once we have a file descriptor
+ // to avoid race conditions.
+ Interop.Sys.FileStatus status;
+ if (Interop.Sys.FStat(handle, out status) != 0)
+ {
+ handle.Dispose();
+ throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), path);
+ }
+ if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR)
+ {
+ handle.Dispose();
+ throw Interop.GetExceptionForIoErrno(Interop.Error.EACCES.Info(), path, isDirectory: true);
+ }
+
+ return handle;
+ }
+
+ /// <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
+ /// file descriptor on error with Marshal.GetLastWin32Error() set to the error code.
+ /// </param>
+ /// <returns>The created SafeFileHandle.</returns>
+ internal static SafeFileHandle Open(Func<SafeFileHandle> fdFunc)
+ {
+ SafeFileHandle handle = Interop.CheckIo(fdFunc());
+
+ Debug.Assert(!handle.IsInvalid, "File descriptor is invalid");
+ return handle;
+ }
+
+ [System.Security.SecurityCritical]
+ protected override bool ReleaseHandle()
+ {
+ // When the SafeFileHandle was opened, we likely issued an flock on the created descriptor in order to add
+ // an advisory lock. This lock should be removed via closing the file descriptor, but close can be
+ // interrupted, and we don't retry closes. As such, we could end up leaving the file locked,
+ // which could prevent subsequent usage of the file until this process dies. To avoid that, we proactively
+ // try to release the lock before we close the handle. (If it's not locked, there's no behavioral
+ // problem trying to unlock it.)
+ Interop.Sys.FLock(handle, Interop.Sys.LockOperations.LOCK_UN); // ignore any errors
+
+ // Close the descriptor. Although close is documented to potentially fail with EINTR, we never want
+ // to retry, as the descriptor could actually have been closed, been subsequently reassigned, and
+ // be in use elsewhere in the process. Instead, we simply check whether the call was successful.
+ int result = Interop.Sys.Close(handle);
+#if DEBUG
+ if (result != 0)
+ {
+ Debug.Fail(string.Format(
+ "Close failed with result {0} and error {1}",
+ result, Interop.Sys.GetLastErrorInfo()));
+ }
+#endif
+ return result == 0;
+ }
+
+ public override bool IsInvalid
+ {
+ [System.Security.SecurityCritical]
+ get
+ {
+ long h = (long)handle;
+ return h < 0 || h > int.MaxValue;
+ }
+ }
+ }
+}