private static unsafe void Preallocate(string fullPath, long preallocationSize, SafeFileHandle fileHandle) { // preallocationSize must be ignored for non-seekable files, unsupported file systems // and other failures other than ERROR_DISK_FULL and ERROR_FILE_TOO_LARGE if (!FileStreamHelpers.TrySetFileLength(fileHandle, preallocationSize, out int errorCode) && errorCode == Interop.Errors.ERROR_DISK_FULL || errorCode == Interop.Errors.ERROR_FILE_TOO_LARGE) { // Windows does not modify the file size if the request can't be satisfied in atomic way. // posix_fallocate (Unix) implementation might consume all available disk space and fail after that. // To ensure that the behaviour is the same for every OS (no incomplete or empty file), we close the handle and delete the file. fileHandle.Dispose(); Interop.Kernel32.DeleteFile(fullPath); throw new IOException(SR.Format(errorCode == Interop.Errors.ERROR_DISK_FULL ? SR.IO_DiskFull_Path_AllocationSize : SR.IO_FileTooLarge_Path_AllocationSize, fullPath, preallocationSize)); } }
internal static unsafe SafeFileHandle Open(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) { using (DisableMediaInsertionPrompt.Create()) { // we don't use NtCreateFile as there is no public and reliable way // of converting DOS to NT file paths (RtlDosPathNameToRelativeNtPathName_U_WithStatus is not documented) SafeFileHandle fileHandle = CreateFile(fullPath, mode, access, share, options); if (FileStreamHelpers.ShouldPreallocate(preallocationSize, access, mode)) { Preallocate(fullPath, preallocationSize, fileHandle); } fileHandle.InitThreadPoolBindingIfNeeded(); return(fileHandle); } }
private void Init(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize) { IsAsync = (options & FileOptions.Asynchronous) != 0; // 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(this, 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) { FileStreamHelpers.CheckFileCall(Interop.Sys.PosixFAdvise(this, 0, 0, fadv), path, ignoreNotSupported: true); // just a hint. } 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(this, 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(this, 0, preallocationSize); if (fallocateResult != 0) { Dispose(); Interop.Sys.Unlink(path !); // remove the file to mimic Windows behaviour (atomic operation) Debug.Assert(fallocateResult == -1 || fallocateResult == -2); throw new IOException(SR.Format( fallocateResult == -1 ? SR.IO_DiskFull_Path_AllocationSize : SR.IO_FileTooLarge_Path_AllocationSize, path, preallocationSize)); } } }