/// <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); }
private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { // FileStream performs most of the general argument validation. We can assume here that the arguments // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.) // Store the arguments _mode = mode; _options = options; if (_useAsyncIO) { _asyncState = new AsyncState(); } // Translate the arguments into arguments for an open call. Interop.Sys.OpenFlags openFlags = PreOpenConfigurationFromOptions(mode, _access, options); // FileShare currently ignored // If the file gets created a new, we'll select the permissions for it. Most utilities by default use 666 (read and // write for all). However, on Windows it's possible to write out a file and then execute it. To maintain that similarity, // we use 766, so that in addition the user has execute privileges. No matter what we choose, it'll be subject to the umask // applied by the system, such that the actual permissions will typically be less than what we select here. const Interop.Sys.Permissions openPermissions = Interop.Sys.Permissions.S_IRWXU | Interop.Sys.Permissions.S_IRGRP | Interop.Sys.Permissions.S_IWGRP | Interop.Sys.Permissions.S_IROTH | Interop.Sys.Permissions.S_IWOTH; // Open the file and store the safe handle. return(SafeFileHandle.Open(_path, openFlags, (int)openPermissions)); }
internal static SafeFileHandle Open(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) { // Translate the arguments into arguments for an open call. Interop.Sys.OpenFlags openFlags = PreOpenConfigurationFromOptions(mode, access, share, options); // If the file gets created a new, we'll select the permissions for it. Most Unix utilities by default use 666 (read and // write for all), so we do the same (even though this doesn't match Windows, where by default it's possible to write out // a file and then execute it). No matter what we choose, it'll be subject to the umask applied by the system, such that the // actual permissions will typically be less than what we select here. const Interop.Sys.Permissions OpenPermissions = Interop.Sys.Permissions.S_IRUSR | Interop.Sys.Permissions.S_IWUSR | Interop.Sys.Permissions.S_IRGRP | Interop.Sys.Permissions.S_IWGRP | Interop.Sys.Permissions.S_IROTH | Interop.Sys.Permissions.S_IWOTH; SafeFileHandle safeFileHandle = Open(fullPath, openFlags, (int)OpenPermissions); try { safeFileHandle.Init(fullPath, mode, access, share, options, preallocationSize); return(safeFileHandle); } catch (Exception) { safeFileHandle.Dispose(); throw; } }
private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { // FileStream performs most of the general argument validation. We can assume here that the arguments // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.) // Store the arguments _mode = mode; _options = options; if (_useAsyncIO) { _asyncState = new AsyncState(); } // Translate the arguments into arguments for an open call. Interop.Sys.OpenFlags openFlags = PreOpenConfigurationFromOptions(mode, _access, share, options); // If the file gets created a new, we'll select the permissions for it. Most Unix utilities by default use 666 (read and // write for all), so we do the same (even though this doesn't match Windows, where by default it's possible to write out // a file and then execute it). No matter what we choose, it'll be subject to the umask applied by the system, such that the // actual permissions will typically be less than what we select here. const Interop.Sys.Permissions OpenPermissions = Interop.Sys.Permissions.S_IRUSR | Interop.Sys.Permissions.S_IWUSR | Interop.Sys.Permissions.S_IRGRP | Interop.Sys.Permissions.S_IWGRP | Interop.Sys.Permissions.S_IROTH | Interop.Sys.Permissions.S_IWOTH; // Open the file and store the safe handle. return(SafeFileHandle.Open(_path, openFlags, (int)OpenPermissions)); }
internal static Interop.Sys.OpenFlags TranslateFlags(PipeDirection direction, PipeOptions options, HandleInheritability inheritability) { // Translate direction Interop.Sys.OpenFlags flags = direction == PipeDirection.InOut ? Interop.Sys.OpenFlags.O_RDWR : direction == PipeDirection.Out ? Interop.Sys.OpenFlags.O_WRONLY : Interop.Sys.OpenFlags.O_RDONLY; // Translate options if ((options & PipeOptions.WriteThrough) != 0) { flags |= Interop.Sys.OpenFlags.O_SYNC; } // Translate inheritability. if ((inheritability & HandleInheritability.Inheritable) == 0) { flags |= Interop.Sys.OpenFlags.O_CLOEXEC; } // PipeOptions.Asynchronous is ignored, at least for now. Asynchronous processing // is handling just by queueing a work item to do the work synchronously on a pool thread. return(flags); }
/// <summary>Translates the FileMode, FileAccess, and FileOptions values into flags to be passed when opening the file.</summary> /// <param name="mode">The FileMode provided to the stream's constructor.</param> /// <param name="access">The FileAccess provided to the stream's constructor</param> /// <param name="share">The FileShare provided to the stream's constructor</param> /// <param name="options">The FileOptions provided to the stream's constructor</param> /// <returns>The flags value to be passed to the open system call.</returns> private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mode, FileAccess access, FileShare share, FileOptions options) { // Translate FileMode. Most of the values map cleanly to one or more options for open. Interop.Sys.OpenFlags flags = default; switch (mode) { default: case FileMode.Open: // Open maps to the default behavior for open(...). No flags needed. case FileMode.Truncate: // We truncate the file after getting the lock break; case FileMode.Append: // Append is the same as OpenOrCreate, except that we'll also separately jump to the end later case FileMode.OpenOrCreate: case FileMode.Create: // We truncate the file after getting the lock flags |= Interop.Sys.OpenFlags.O_CREAT; break; case FileMode.CreateNew: flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL); break; } // Translate FileAccess. All possible values map cleanly to corresponding values for open. switch (access) { case FileAccess.Read: flags |= Interop.Sys.OpenFlags.O_RDONLY; break; case FileAccess.ReadWrite: flags |= Interop.Sys.OpenFlags.O_RDWR; break; case FileAccess.Write: flags |= Interop.Sys.OpenFlags.O_WRONLY; break; } // Handle Inheritable, other FileShare flags are handled by Init if ((share & FileShare.Inheritable) == 0) { flags |= Interop.Sys.OpenFlags.O_CLOEXEC; } // Translate some FileOptions; some just aren't supported, and others will be handled after calling open. // - Asynchronous: Handled in ctor, setting _useAsync and SafeFileHandle.IsAsync to true // - DeleteOnClose: Doesn't have a Unix equivalent, but we approximate it in Dispose // - Encrypted: No equivalent on Unix and is ignored // - RandomAccess: Implemented after open if posix_fadvise is available // - SequentialScan: Implemented after open if posix_fadvise is available // - WriteThrough: Handled here if ((options & FileOptions.WriteThrough) != 0) { flags |= Interop.Sys.OpenFlags.O_SYNC; } return(flags); }
/// <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 SafePipeHandle Open(string path, Interop.Sys.OpenFlags flags, int mode) { // Ideally this would be a constrained execution region, but we don't have access to PrepareConstrainedRegions. SafePipeHandle handle = Interop.CheckIo(Interop.Sys.OpenPipe(path, flags, mode)); Debug.Assert(!handle.IsInvalid); return(handle); }
/// <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> private static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int mode) { Debug.Assert(path != null); SafeFileHandle handle = Interop.Sys.Open(path, flags, mode); handle._path = path; if (handle.IsInvalid) { Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); handle.Dispose(); // 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 !)) !)); 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. Interop.Sys.FileStatus status; if (Interop.Sys.FStat(handle, out status) != 0) { Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); handle.Dispose(); throw Interop.GetExceptionForIoErrno(error, 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); } if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFREG) { // we take advantage of the information provided by the fstat syscall // and for regular files (most common case) // avoid one extra sys call for determining whether file can be seeked handle._canSeek = NullableBool.True; Debug.Assert(Interop.Sys.LSeek(handle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0); } return(handle); }
static Exception?CreateOpenException(Interop.ErrorInfo error, Interop.Sys.OpenFlags flags, string path) { // If the destination path points to a directory, we throw to match Windows behaviour. if (error.Error == Interop.Error.EEXIST && DirectoryExists(path)) { return(new IOException(SR.Format(SR.Arg_FileIsDirectory_Name, path))); } return(null); // Let SafeFileHandle create the exception for this error. }
private static FileStream CreateSharedBackingObject( Interop.libc.MemoryMappedProtections protections, long capacity) { // The POSIX shared memory object name must begin with '/'. After that we just want something short and unique. string mapName = "/corefx_map_" + Guid.NewGuid().ToString("N"); // Determine the flags to use when creating the shared memory object Interop.Sys.OpenFlags flags = (protections & Interop.libc.MemoryMappedProtections.PROT_WRITE) != 0 ? Interop.Sys.OpenFlags.O_RDWR : Interop.Sys.OpenFlags.O_RDONLY; flags |= Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL; // CreateNew // Determine the permissions with which to create the file Interop.Sys.Permissions perms = default(Interop.Sys.Permissions); if ((protections & Interop.libc.MemoryMappedProtections.PROT_READ) != 0) { perms |= Interop.Sys.Permissions.S_IRUSR; } if ((protections & Interop.libc.MemoryMappedProtections.PROT_WRITE) != 0) { perms |= Interop.Sys.Permissions.S_IWUSR; } if ((protections & Interop.libc.MemoryMappedProtections.PROT_EXEC) != 0) { perms |= Interop.Sys.Permissions.S_IXUSR; } // Create the shared memory object. int fd; Interop.CheckIo(fd = Interop.Sys.ShmOpen(mapName, flags, (int)perms), mapName); SafeFileHandle fileHandle = new SafeFileHandle((IntPtr)fd, ownsHandle: true); try { // Unlink the shared memory object immediatley so that it'll go away once all handles // to it are closed (as with opened then unlinked files, it'll remain usable via // the open handles even though it's unlinked and can't be opened anew via its name). Interop.CheckIo(Interop.Sys.ShmUnlink(mapName)); // Give it the right capacity. We do this directly with ftruncate rather // than via FileStream.SetLength after the FileStream is created because, on some systems, // lseek fails on shared memory objects, causing the FileStream to think it's unseekable, // causing it to preemptively throw from SetLength. Interop.CheckIo(Interop.libc.ftruncate(fd, capacity)); // Wrap the file descriptor in a stream and return it. return(new FileStream(fileHandle, TranslateProtectionsToFileAccess(protections))); } catch { fileHandle.Dispose(); throw; } }
/// <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); // SafeFileHandle wraps a file descriptor rather than a pointer, and a file descriptor is always 4 bytes // rather than being pointer sized, which means we can't utilize the runtime's ability to marshal safe handles. // Ideally this would be a constrained execution region, but we don't have access to PrepareConstrainedRegions. // We still use a finally block to house the code that opens the file and stores the handle in hopes // of making it as non-interruptable as possible. The SafeFileHandle is also allocated first to avoid // the allocation after getting the file descriptor but before storing it. SafeFileHandle handle = new SafeFileHandle(ownsHandle: true); try { } finally { // 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. int fd; while (Interop.CheckIo(fd = Interop.Sys.Open(path, flags, mode), path, isDirectory: enoentDueToDirectory, errorRewriter: e => (e.Error == Interop.Error.EISDIR) ? Interop.Error.EACCES.Info() : e)) { ; } Debug.Assert(fd >= 0); handle.SetHandle((IntPtr)fd); Debug.Assert(!handle.IsInvalid); // 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(fd, 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 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); SafeFileHandle handle = Interop.Sys.Open(path, flags, mode); 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(Path.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. 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>Initializes a stream for reading or writing a Unix file.</summary> /// <param name="path">The path to the file.</param> /// <param name="mode">How the file should be opened.</param> /// <param name="access">Whether the file will be read, written, or both.</param> /// <param name="share">What other access to the file should be allowed. This is currently ignored.</param> /// <param name="bufferSize">The size of the buffer to use when buffering.</param> /// <param name="options">Additional options for working with the file.</param> internal UnixFileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, FileStream parent) : base(parent) { // FileStream performs most of the general argument validation. We can assume here that the arguments // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.) // Store the arguments _path = path; _access = access; _mode = mode; _options = options; _bufferLength = bufferSize; _useAsyncIO = (options & FileOptions.Asynchronous) != 0; // Translate the arguments into arguments for an open call Interop.Sys.OpenFlags openFlags = PreOpenConfigurationFromOptions(mode, access, options); // FileShare currently ignored Interop.Sys.Permissions openPermissions = Interop.Sys.Permissions.S_IRWXU; // creator has read/write/execute permissions; no permissions for anyone else // Open the file and store the safe handle. Subsequent code in this method expects the safe handle to be initialized. _fileHandle = SafeFileHandle.Open(path, openFlags, (int)openPermissions); _fileHandle.IsAsync = _useAsyncIO; // Lock the file if requested via FileShare. This is only advisory locking. FileShare.None implies an exclusive // lock on the file and all other modes use a shared lock. While this is not as granular as Windows, not mandatory, // and not atomic with file opening, it's better than nothing. try { Interop.Sys.LockOperations lockOperation = (share == FileShare.None) ? Interop.Sys.LockOperations.LOCK_EX : Interop.Sys.LockOperations.LOCK_SH; SysCall <Interop.Sys.LockOperations, int>((fd, op, _) => Interop.Sys.FLock(fd, op), lockOperation | Interop.Sys.LockOperations.LOCK_NB); } catch { _fileHandle.Dispose(); throw; } // Perform additional configurations on the stream based on the provided FileOptions PostOpenConfigureStreamFromOptions(); // Jump to the end of the file if opened as Append. if (_mode == FileMode.Append) { _appendStart = SeekCore(0, SeekOrigin.End); } }
/// <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 SafePipeHandle Open(string path, Interop.Sys.OpenFlags flags, int mode) { // SafePipeHandle wraps a file descriptor rather than a pointer, and a file descriptor is always 4 bytes // rather than being pointer sized, which means we can't utilize the runtime's ability to marshal safe handles. // Ideally this would be a constrained execution region, but we don't have access to PrepareConstrainedRegions. // We still use a finally block to house the code that opens the file and stores the handle in hopes // of making it as non-interruptable as possible. The SafePipeHandle is also allocated first to avoid // the allocation after getting the file descriptor but before storing it. SafePipeHandle handle = new SafePipeHandle(); try { } finally { int fd; while (Interop.CheckIo(fd = Interop.Sys.Open(path, flags, mode))) { ; } Debug.Assert(fd >= 0); handle.SetHandle((IntPtr)fd); } return(handle); }
/// <summary>Translates the FileMode, FileAccess, and FileOptions values into flags to be passed when opening the file.</summary> /// <param name="mode">The FileMode provided to the stream's constructor.</param> /// <param name="access">The FileAccess provided to the stream's constructor</param> /// <param name="options">The FileOptions provided to the stream's constructor</param> /// <returns>The flags value to be passed to the open system call.</returns> private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mode, FileAccess access, FileOptions options) { // Translate FileMode. Most of the values map cleanly to one or more options for open. Interop.Sys.OpenFlags flags = default(Interop.Sys.OpenFlags); switch (mode) { default: case FileMode.Open: // Open maps to the default behavior for open(...). No flags needed. break; case FileMode.Append: // Append is the same as OpenOrCreate, except that we'll also separately jump to the end later case FileMode.OpenOrCreate: flags |= Interop.Sys.OpenFlags.O_CREAT; break; case FileMode.Create: flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_TRUNC); break; case FileMode.CreateNew: flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL); break; case FileMode.Truncate: flags |= Interop.Sys.OpenFlags.O_TRUNC; break; } // Translate FileAccess. All possible values map cleanly to corresponding values for open. switch (access) { case FileAccess.Read: flags |= Interop.Sys.OpenFlags.O_RDONLY; break; case FileAccess.ReadWrite: flags |= Interop.Sys.OpenFlags.O_RDWR; break; case FileAccess.Write: flags |= Interop.Sys.OpenFlags.O_WRONLY; break; } // Translate some FileOptions; some just aren't supported, and others will be handled after calling open. switch (options) { case FileOptions.Asynchronous: // Handled in ctor, setting _useAsync and SafeFileHandle.IsAsync to true case FileOptions.DeleteOnClose: // DeleteOnClose doesn't have a Unix equivalent, but we approximate it in Dispose case FileOptions.Encrypted: // Encrypted does not have an equivalent on Unix and is ignored. case FileOptions.RandomAccess: // Implemented after open if posix_fadvise is available case FileOptions.SequentialScan: // Implemented after open if posix_fadvise is available break; case FileOptions.WriteThrough: flags |= Interop.Sys.OpenFlags.O_SYNC; break; } return(flags); }
// ----------------------------- // ---- PAL layer ends here ---- // ----------------------------- private static FileStream CreateSharedBackingObjectUsingMemory( Interop.Sys.MemoryMappedProtections protections, long capacity) { // The POSIX shared memory object name must begin with '/'. After that we just want something short and unique. string mapName = "/corefx_map_" + Guid.NewGuid().ToString("N"); // Determine the flags to use when creating the shared memory object Interop.Sys.OpenFlags flags = (protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) != 0 ? Interop.Sys.OpenFlags.O_RDWR : Interop.Sys.OpenFlags.O_RDONLY; flags |= Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL; // CreateNew // Determine the permissions with which to create the file Interop.Sys.Permissions perms = default(Interop.Sys.Permissions); if ((protections & Interop.Sys.MemoryMappedProtections.PROT_READ) != 0) { perms |= Interop.Sys.Permissions.S_IRUSR; } if ((protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) != 0) { perms |= Interop.Sys.Permissions.S_IWUSR; } if ((protections & Interop.Sys.MemoryMappedProtections.PROT_EXEC) != 0) { perms |= Interop.Sys.Permissions.S_IXUSR; } // Create the shared memory object. SafeFileHandle fd = Interop.Sys.ShmOpen(mapName, flags, (int)perms); if (fd.IsInvalid) { Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); if (errorInfo.Error == Interop.Error.ENOTSUP) { // If ShmOpen is not supported, fall back to file backing object. // Note that the System.Native shim will force this failure on platforms where // the result of native shm_open does not work well with our subsequent call // to mmap. return(null); } throw Interop.GetExceptionForIoErrno(errorInfo); } try { // Unlink the shared memory object immediately so that it'll go away once all handles // to it are closed (as with opened then unlinked files, it'll remain usable via // the open handles even though it's unlinked and can't be opened anew via its name). Interop.CheckIo(Interop.Sys.ShmUnlink(mapName)); // Give it the right capacity. We do this directly with ftruncate rather // than via FileStream.SetLength after the FileStream is created because, on some systems, // lseek fails on shared memory objects, causing the FileStream to think it's unseekable, // causing it to preemptively throw from SetLength. Interop.CheckIo(Interop.Sys.FTruncate(fd, capacity)); // Wrap the file descriptor in a stream and return it. return(new FileStream(fd, TranslateProtectionsToFileAccess(protections))); } catch { fd.Dispose(); throw; } }
private static FileStream?CreateSharedBackingObjectUsingMemory( Interop.Sys.MemoryMappedProtections protections, long capacity, HandleInheritability inheritability) { // Determine the flags to use when creating the shared memory object Interop.Sys.OpenFlags flags = (protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) != 0 ? Interop.Sys.OpenFlags.O_RDWR : Interop.Sys.OpenFlags.O_RDONLY; flags |= Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL; // CreateNew // Determine the permissions with which to create the file Interop.Sys.Permissions perms = default(Interop.Sys.Permissions); if ((protections & Interop.Sys.MemoryMappedProtections.PROT_READ) != 0) { perms |= Interop.Sys.Permissions.S_IRUSR; } if ((protections & Interop.Sys.MemoryMappedProtections.PROT_WRITE) != 0) { perms |= Interop.Sys.Permissions.S_IWUSR; } if ((protections & Interop.Sys.MemoryMappedProtections.PROT_EXEC) != 0) { perms |= Interop.Sys.Permissions.S_IXUSR; } string mapName; SafeFileHandle fd; do { mapName = GenerateMapName(); fd = Interop.Sys.ShmOpen(mapName, flags, (int)perms); // Create the shared memory object. if (fd.IsInvalid) { Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); fd.Dispose(); if (errorInfo.Error == Interop.Error.ENOTSUP) { // If ShmOpen is not supported, fall back to file backing object. // Note that the System.Native shim will force this failure on platforms where // the result of native shm_open does not work well with our subsequent call to mmap. return(null); } else if (errorInfo.Error != Interop.Error.EEXIST) // map with same name already existed { throw Interop.GetExceptionForIoErrno(errorInfo); } } } while (fd.IsInvalid); try { // Unlink the shared memory object immediately so that it'll go away once all handles // to it are closed (as with opened then unlinked files, it'll remain usable via // the open handles even though it's unlinked and can't be opened anew via its name). Interop.CheckIo(Interop.Sys.ShmUnlink(mapName)); // Give it the right capacity. We do this directly with ftruncate rather // than via FileStream.SetLength after the FileStream is created because, on some systems, // lseek fails on shared memory objects, causing the FileStream to think it's unseekable, // causing it to preemptively throw from SetLength. Interop.CheckIo(Interop.Sys.FTruncate(fd, capacity)); // shm_open sets CLOEXEC implicitly. If the inheritability requested is Inheritable, remove CLOEXEC. if (inheritability == HandleInheritability.Inheritable && Interop.Sys.Fcntl.SetFD(fd, 0) == -1) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo()); } // Wrap the file descriptor in a stream and return it. return(new FileStream(fd, TranslateProtectionsToFileAccess(protections))); } catch { fd.Dispose(); throw; }