/// <summary>Initializes a stream for reading or writing a Unix file.</summary> /// <param name="mode">How the file should be opened.</param> /// <param name="share">What other access to the file should be allowed. This is currently ignored.</param> /// <param name="originalPath">The original path specified for the FileStream.</param> private void Init(FileMode mode, FileShare share, string originalPath) { _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. Interop.Sys.LockOperations lockOperation = (share == FileShare.None) ? Interop.Sys.LockOperations.LOCK_EX : Interop.Sys.LockOperations.LOCK_SH; if (Interop.Sys.FLock(_fileHandle, lockOperation | Interop.Sys.LockOperations.LOCK_NB) < 0) { // The only error we care about is EWOULDBLOCK, which indicates that the file is currently locked by someone // else and we would block trying to access it. Other errors, such as ENOTSUP (locking isn't supported) or // EACCES (the file system doesn't allow us to lock), will only hamper FileStream's usage without providing value, // given again that this is only advisory / best-effort. Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); if (errorInfo.Error == Interop.Error.EWOULDBLOCK) { throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false); } } // These provide hints around how the file will be accessed. Specifying both RandomAccess // and Sequential together doesn't make sense as they are two competing options on the same spectrum, // so if both are specified, we prefer RandomAccess (behavior on Windows is unspecified if both are provided). Interop.Sys.FileAdvice fadv = (_options & FileOptions.RandomAccess) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_RANDOM : (_options & FileOptions.SequentialScan) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_SEQUENTIAL : 0; if (fadv != 0) { CheckFileCall(Interop.Sys.PosixFAdvise(_fileHandle, 0, 0, fadv), ignoreNotSupported: true); // just a hint. } if (_mode == FileMode.Append) { // Jump to the end of the file if opened as Append. _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End); } else if (mode == FileMode.Create || mode == FileMode.Truncate) { // Truncate the file now if the file mode requires it. This ensures that the file only will be truncated // if opened successfully. if (Interop.Sys.FTruncate(_fileHandle, 0) < 0) { Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); if (errorInfo.Error != Interop.Error.EBADF && errorInfo.Error != Interop.Error.EINVAL) { // 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); } } } }
/// <summary>Performs additional configuration of the opened stream based on provided options.</summary> partial void PostOpenConfigureStreamFromOptions() { // Support additional options after the file has been opened. // These provide hints around how the file will be accessed. Interop.Sys.FileAdvice fadv = _options == FileOptions.RandomAccess ? Interop.Sys.FileAdvice.POSIX_FADV_RANDOM : _options == FileOptions.SequentialScan ? Interop.Sys.FileAdvice.POSIX_FADV_SEQUENTIAL : 0; if (fadv != 0) { SysCall <Interop.Sys.FileAdvice, int>((fd, advice, _) => Interop.Sys.PosixFAdvise(fd, 0, 0, advice), fadv); } }
/// <summary>Initializes a stream for reading or writing a Unix file.</summary> /// <param name="mode">How the file should be opened.</param> /// <param name="share">What other access to the file should be allowed. This is currently ignored.</param> /// <param name="originalPath">The original path specified for the FileStream.</param> /// <param name="options">Options, passed via arguments as we have no guarantee that _options field was already set.</param> /// <param name="preallocationSize">passed to posix_fallocate</param> private void Init(FileMode mode, FileShare share, string originalPath, FileOptions options, long preallocationSize) { // 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(); } _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. Interop.Sys.LockOperations lockOperation = (share == FileShare.None) ? Interop.Sys.LockOperations.LOCK_EX : Interop.Sys.LockOperations.LOCK_SH; if (Interop.Sys.FLock(_fileHandle, lockOperation | Interop.Sys.LockOperations.LOCK_NB) < 0) { // The only error we care about is EWOULDBLOCK, which indicates that the file is currently locked by someone // else and we would block trying to access it. Other errors, such as ENOTSUP (locking isn't supported) or // EACCES (the file system doesn't allow us to lock), will only hamper FileStream's usage without providing value, // given again that this is only advisory / best-effort. Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); if (errorInfo.Error == Interop.Error.EWOULDBLOCK) { throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false); } } // These provide hints around how the file will be accessed. Specifying both RandomAccess // and Sequential together doesn't make sense as they are two competing options on the same spectrum, // so if both are specified, we prefer RandomAccess (behavior on Windows is unspecified if both are provided). Interop.Sys.FileAdvice fadv = (options & FileOptions.RandomAccess) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_RANDOM : (options & FileOptions.SequentialScan) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_SEQUENTIAL : 0; if (fadv != 0) { CheckFileCall(Interop.Sys.PosixFAdvise(_fileHandle, 0, 0, fadv), ignoreNotSupported: true); // just a hint. } if (mode == FileMode.Append) { // Jump to the end of the file if opened as Append. _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End); } else if (mode == FileMode.Create || mode == FileMode.Truncate) { // Truncate the file now if the file mode requires it. This ensures that the file only will be truncated // if opened successfully. if (Interop.Sys.FTruncate(_fileHandle, 0) < 0) { Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); if (errorInfo.Error != Interop.Error.EBADF && errorInfo.Error != Interop.Error.EINVAL) { // 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); } } } // If preallocationSize has been provided for a creatable and writeable file if (FileStreamHelpers.ShouldPreallocate(preallocationSize, _access, mode)) { int fallocateResult = Interop.Sys.PosixFAllocate(_fileHandle, 0, preallocationSize); if (fallocateResult != 0) { _fileHandle.Dispose(); Interop.Sys.Unlink(_path !); // remove the file to mimic Windows behaviour (atomic operation) if (fallocateResult == -1) { throw new IOException(SR.Format(SR.IO_DiskFull_Path_AllocationSize, _path, preallocationSize)); } Debug.Assert(fallocateResult == -2); throw new IOException(SR.Format(SR.IO_FileTooLarge_Path_AllocationSize, _path, preallocationSize)); } } }