| File: src\runtime\src\libraries\System.Private.CoreLib\src\System\IO\Strategies\FileStreamHelpers.cs | Web Access |
| Project: System.Private.CoreLib.csproj (System.Private.CoreLib) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.Serialization; using Microsoft.Win32.SafeHandles; namespace System.IO.Strategies { internal static partial class FileStreamHelpers { internal const FileOptions NoBuffering = (FileOptions)0x20000000; internal const FileOptions BackupOrRestore = (FileOptions)0x02000000; // NOTE: any change to FileOptions enum needs to be matched here as it's used in the error validation private const FileOptions ValidFileOptions = FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | NoBuffering | BackupOrRestore; /// <summary>Caches whether Serialization Guard has been disabled for file writes</summary> private static int s_cachedSerializationSwitch; internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { FileStreamStrategy strategy = EnableBufferingIfNeeded(ChooseStrategyCore(handle, access, isAsync), bufferSize); return WrapIfDerivedType(fileStream, strategy); } internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize, UnixFileMode? unixCreateMode) { FileStreamStrategy strategy = EnableBufferingIfNeeded(ChooseStrategyCore(path, mode, access, share, options, preallocationSize, unixCreateMode), bufferSize); return WrapIfDerivedType(fileStream, strategy); } private static FileStreamStrategy EnableBufferingIfNeeded(FileStreamStrategy strategy, int bufferSize) => bufferSize > 1 ? new BufferedFileStreamStrategy(strategy, bufferSize) : strategy; private static FileStreamStrategy WrapIfDerivedType(FileStream fileStream, FileStreamStrategy strategy) => fileStream.GetType() == typeof(FileStream) ? strategy : new DerivedFileStreamStrategy(fileStream, strategy); internal static bool IsIoRelatedException(Exception e) => // These all derive from IOException // DirectoryNotFoundException // DriveNotFoundException // EndOfStreamException // FileLoadException // FileNotFoundException // PathTooLongException // PipeException e is IOException || // Note that SecurityException is only thrown on runtimes that support CAS // e is SecurityException || e is UnauthorizedAccessException || e is NotSupportedException || (e is ArgumentException && e is not ArgumentNullException); internal static void ValidateArguments(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize) { ArgumentException.ThrowIfNullOrEmpty(path); // don't include inheritable in our bounds check for share FileShare tempshare = share & ~FileShare.Inheritable; string? badArg = null; if (mode < FileMode.CreateNew || mode > FileMode.Append) { badArg = nameof(mode); } else if (access < FileAccess.Read || access > FileAccess.ReadWrite) { badArg = nameof(access); } else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete)) { badArg = nameof(share); } if (badArg != null) { throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum); } // NOTE: any change to FileOptions enum needs to be matched here in the error validation if (AreInvalid(options)) { throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum); } else if (bufferSize < 0) { ThrowHelper.ThrowArgumentOutOfRangeException_NeedNonNegNum(nameof(bufferSize)); } else if (preallocationSize < 0) { ThrowHelper.ThrowArgumentOutOfRangeException_NeedNonNegNum(nameof(preallocationSize)); } // Write access validation if ((access & FileAccess.Write) == 0) { if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append) { // No write access, mode and access disagree but flag access since mode comes first throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, mode, access), nameof(access)); } } if ((access & FileAccess.Read) != 0 && mode == FileMode.Append) { throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access)); } if (preallocationSize > 0) { ValidateArgumentsForPreallocation(mode, access); } SerializationGuard(access); } internal static void ValidateArgumentsForPreallocation(FileMode mode, FileAccess access) { // The user will be writing into the preallocated space. if ((access & FileAccess.Write) == 0) { throw new ArgumentException(SR.Argument_InvalidPreallocateAccess, nameof(access)); } // Only allow preallocation for newly created/overwritten files. // When we fail to preallocate, we'll remove the file. if (mode != FileMode.Create && mode != FileMode.CreateNew) { throw new ArgumentException(SR.Argument_InvalidPreallocateMode, nameof(mode)); } } internal static void SerializationGuard(FileAccess access) { if ((access & FileAccess.Write) == FileAccess.Write) { SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); } } internal static bool AreInvalid(FileOptions options) => options != FileOptions.None && (options & ~ValidFileOptions) != 0; } }