--- /dev/null
+// 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.InteropServices;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ /// <summary>
+ /// Returns -1 on ENOSPC, -2 on EFBIG. On success or ignorable error, 0 is returned.
+ /// </summary>
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PosixFAllocate", SetLastError = false)]
+ internal static extern int PosixFAllocate(SafeFileHandle fd, long offset, long length);
+ }
+}
internal const int ERROR_FILE_EXISTS = 0x50;
internal const int ERROR_INVALID_PARAMETER = 0x57;
internal const int ERROR_BROKEN_PIPE = 0x6D;
+ internal const int ERROR_DISK_FULL = 0x70;
internal const int ERROR_SEM_TIMEOUT = 0x79;
internal const int ERROR_CALL_NOT_IMPLEMENTED = 0x78;
internal const int ERROR_INSUFFICIENT_BUFFER = 0x7A;
internal const int ERROR_ENVVAR_NOT_FOUND = 0xCB;
internal const int ERROR_FILENAME_EXCED_RANGE = 0xCE;
internal const int ERROR_EXE_MACHINE_TYPE_MISMATCH = 0xD8;
+ internal const int ERROR_FILE_TOO_LARGE = 0xDF;
internal const int ERROR_PIPE_BUSY = 0xE7;
internal const int ERROR_NO_DATA = 0xE8;
internal const int ERROR_PIPE_NOT_CONNECTED = 0xE9;
/// Optional quality of service to be applied to the object. Used to indicate
/// security impersonation level and context tracking mode (dynamic or static).
/// </summary>
- public void* SecurityQualityOfService;
+ public SECURITY_QUALITY_OF_SERVICE* SecurityQualityOfService;
/// <summary>
/// Equivalent of InitializeObjectAttributes macro with the exception that you can directly set SQOS.
/// </summary>
- public unsafe OBJECT_ATTRIBUTES(UNICODE_STRING* objectName, ObjectAttributes attributes, IntPtr rootDirectory)
+ public unsafe OBJECT_ATTRIBUTES(UNICODE_STRING* objectName, ObjectAttributes attributes, IntPtr rootDirectory, SECURITY_QUALITY_OF_SERVICE* securityQualityOfService = null)
{
Length = (uint)sizeof(OBJECT_ATTRIBUTES);
RootDirectory = rootDirectory;
ObjectName = objectName;
Attributes = attributes;
SecurityDescriptor = null;
- SecurityQualityOfService = null;
+ SecurityQualityOfService = securityQualityOfService;
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+internal static partial class Interop
+{
+ /// <summary>
+ /// <a href="https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_quality_of_service">SECURITY_QUALITY_OF_SERVICE</a> structure.
+ /// Used to support client impersonation. Client specifies this to a server to allow
+ /// it to impersonate the client.
+ /// </summary>
+ internal unsafe struct SECURITY_QUALITY_OF_SERVICE
+ {
+ public uint Length;
+ public ImpersonationLevel ImpersonationLevel;
+ public ContextTrackingMode ContextTrackingMode;
+ public BOOLEAN EffectiveOnly;
+
+ public unsafe SECURITY_QUALITY_OF_SERVICE(ImpersonationLevel impersonationLevel, ContextTrackingMode contextTrackingMode, bool effectiveOnly)
+ {
+ Length = (uint)sizeof(SECURITY_QUALITY_OF_SERVICE);
+ ImpersonationLevel = impersonationLevel;
+ ContextTrackingMode = contextTrackingMode;
+ EffectiveOnly = effectiveOnly ? BOOLEAN.TRUE : BOOLEAN.FALSE;
+ }
+ }
+
+ /// <summary>
+ /// <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa379572.aspx">SECURITY_IMPERSONATION_LEVEL</a> enumeration values.
+ /// [SECURITY_IMPERSONATION_LEVEL]
+ /// </summary>
+ public enum ImpersonationLevel : uint
+ {
+ /// <summary>
+ /// The server process cannot obtain identification information about the client and cannot impersonate the client.
+ /// [SecurityAnonymous]
+ /// </summary>
+ Anonymous,
+
+ /// <summary>
+ /// The server process can obtain identification information about the client, but cannot impersonate the client.
+ /// [SecurityIdentification]
+ /// </summary>
+ Identification,
+
+ /// <summary>
+ /// The server process can impersonate the client's security context on it's local system.
+ /// [SecurityImpersonation]
+ /// </summary>
+ Impersonation,
+
+ /// <summary>
+ /// The server process can impersonate the client's security context on remote systems.
+ /// [SecurityDelegation]
+ /// </summary>
+ Delegation
+ }
+
+ /// <summary>
+ /// <a href="https://msdn.microsoft.com/en-us/library/cc234317.aspx">SECURITY_CONTEXT_TRACKING_MODE</a>
+ /// </summary>
+ public enum ContextTrackingMode : byte
+ {
+ /// <summary>
+ /// The server is given a snapshot of the client's security context.
+ /// [SECURITY_STATIC_TRACKING]
+ /// </summary>
+ Static = 0x00,
+
+ /// <summary>
+ /// The server is continually updated with changes.
+ /// [SECURITY_DYNAMIC_TRACKING]
+ /// </summary>
+ Dynamic = 0x01
+ }
+}
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Diagnostics;
+using System.IO;
using System.Runtime.InteropServices;
internal static partial class Interop
{
internal static partial class NtDll
{
+ internal const uint NT_ERROR_STATUS_DISK_FULL = 0xC000007F;
+ internal const uint NT_ERROR_STATUS_FILE_TOO_LARGE = 0xC0000904;
+ internal const uint NT_STATUS_INVALID_PARAMETER = 0xC000000D;
+
// https://msdn.microsoft.com/en-us/library/bb432380.aspx
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff566424.aspx
[DllImport(Libraries.NtDll, CharSet = CharSet.Unicode, ExactSpelling = true)]
- private static extern unsafe int NtCreateFile(
+ private static extern unsafe uint NtCreateFile(
out IntPtr FileHandle,
DesiredAccess DesiredAccess,
ref OBJECT_ATTRIBUTES ObjectAttributes,
out IO_STATUS_BLOCK IoStatusBlock,
long* AllocationSize,
- System.IO.FileAttributes FileAttributes,
- System.IO.FileShare ShareAccess,
+ FileAttributes FileAttributes,
+ FileShare ShareAccess,
CreateDisposition CreateDisposition,
CreateOptions CreateOptions,
void* EaBuffer,
uint EaLength);
- internal static unsafe (int status, IntPtr handle) CreateFile(
+ internal static unsafe (uint status, IntPtr handle) CreateFile(
ReadOnlySpan<char> path,
IntPtr rootDirectory,
CreateDisposition createDisposition,
DesiredAccess desiredAccess = DesiredAccess.FILE_GENERIC_READ | DesiredAccess.SYNCHRONIZE,
- System.IO.FileShare shareAccess = System.IO.FileShare.ReadWrite | System.IO.FileShare.Delete,
- System.IO.FileAttributes fileAttributes = 0,
+ FileShare shareAccess = FileShare.ReadWrite | FileShare.Delete,
+ FileAttributes fileAttributes = 0,
CreateOptions createOptions = CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT,
ObjectAttributes objectAttributes = ObjectAttributes.OBJ_CASE_INSENSITIVE,
void* eaBuffer = null,
- uint eaLength = 0)
+ uint eaLength = 0,
+ long* preallocationSize = null,
+ SECURITY_QUALITY_OF_SERVICE* securityQualityOfService = null)
{
fixed (char* c = &MemoryMarshal.GetReference(path))
{
OBJECT_ATTRIBUTES attributes = new OBJECT_ATTRIBUTES(
&name,
objectAttributes,
- rootDirectory);
+ rootDirectory,
+ securityQualityOfService);
- int status = NtCreateFile(
+ uint status = NtCreateFile(
out IntPtr handle,
desiredAccess,
ref attributes,
out IO_STATUS_BLOCK statusBlock,
- AllocationSize: null,
+ AllocationSize: preallocationSize,
fileAttributes,
shareAccess,
createDisposition,
}
}
+ internal static unsafe (uint status, IntPtr handle) CreateFile(ReadOnlySpan<char> path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize)
+ {
+ // For mitigating local elevation of privilege attack through named pipes
+ // make sure we always call NtCreateFile with SECURITY_ANONYMOUS so that the
+ // named pipe server can't impersonate a high privileged client security context
+ SECURITY_QUALITY_OF_SERVICE securityQualityOfService = new SECURITY_QUALITY_OF_SERVICE(
+ ImpersonationLevel.Anonymous, // SECURITY_ANONYMOUS
+ ContextTrackingMode.Static,
+ effectiveOnly: false);
+
+ return CreateFile(
+ path: path,
+ rootDirectory: IntPtr.Zero,
+ createDisposition: GetCreateDisposition(mode),
+ desiredAccess: GetDesiredAccess(access, mode, options),
+ shareAccess: GetShareAccess(share),
+ fileAttributes: GetFileAttributes(options),
+ createOptions: GetCreateOptions(options),
+ objectAttributes: GetObjectAttributes(share),
+ preallocationSize: &preallocationSize,
+ securityQualityOfService: &securityQualityOfService);
+ }
+
+ private static CreateDisposition GetCreateDisposition(FileMode mode)
+ {
+ switch (mode)
+ {
+ case FileMode.CreateNew:
+ return CreateDisposition.FILE_CREATE;
+ case FileMode.Create:
+ return CreateDisposition.FILE_SUPERSEDE;
+ case FileMode.OpenOrCreate:
+ case FileMode.Append: // has extra handling in GetDesiredAccess
+ return CreateDisposition.FILE_OPEN_IF;
+ case FileMode.Truncate:
+ return CreateDisposition.FILE_OVERWRITE;
+ default:
+ Debug.Assert(mode == FileMode.Open); // the enum value is validated in FileStream ctor
+ return CreateDisposition.FILE_OPEN;
+ }
+ }
+
+ private static DesiredAccess GetDesiredAccess(FileAccess access, FileMode fileMode, FileOptions options)
+ {
+ DesiredAccess result = 0;
+
+ if ((access & FileAccess.Read) != 0)
+ {
+ result |= DesiredAccess.FILE_GENERIC_READ;
+ }
+ if ((access & FileAccess.Write) != 0)
+ {
+ result |= DesiredAccess.FILE_GENERIC_WRITE;
+ }
+ if (fileMode == FileMode.Append)
+ {
+ result |= DesiredAccess.FILE_APPEND_DATA;
+ }
+ if ((options & FileOptions.Asynchronous) == 0)
+ {
+ result |= DesiredAccess.SYNCHRONIZE; // required by FILE_SYNCHRONOUS_IO_NONALERT
+ }
+ if ((options & FileOptions.DeleteOnClose) != 0 || fileMode == FileMode.Create)
+ {
+ result |= DesiredAccess.DELETE; // required by FILE_DELETE_ON_CLOSE and FILE_SUPERSEDE (which deletes a file if it exists)
+ }
+
+ return result;
+ }
+
+ private static FileShare GetShareAccess(FileShare share)
+ => share & ~FileShare.Inheritable; // FileShare.Inheritable is handled in GetObjectAttributes
+
+ private static FileAttributes GetFileAttributes(FileOptions options)
+ => (options & FileOptions.Encrypted) != 0 ? FileAttributes.Encrypted : 0;
+
+ // FileOptions.Encrypted is handled in GetFileAttributes
+ private static CreateOptions GetCreateOptions(FileOptions options)
+ {
+ // Every directory is just a directory FILE.
+ // FileStream does not allow for opening directories on purpose.
+ // FILE_NON_DIRECTORY_FILE is used to ensure that
+ CreateOptions result = CreateOptions.FILE_NON_DIRECTORY_FILE;
+
+ if ((options & FileOptions.WriteThrough) != 0)
+ {
+ result |= CreateOptions.FILE_WRITE_THROUGH;
+ }
+ if ((options & FileOptions.RandomAccess) != 0)
+ {
+ result |= CreateOptions.FILE_RANDOM_ACCESS;
+ }
+ if ((options & FileOptions.SequentialScan) != 0)
+ {
+ result |= CreateOptions.FILE_SEQUENTIAL_ONLY;
+ }
+ if ((options & FileOptions.DeleteOnClose) != 0)
+ {
+ result |= CreateOptions.FILE_DELETE_ON_CLOSE; // has extra handling in GetDesiredAccess
+ }
+ if ((options & FileOptions.Asynchronous) == 0)
+ {
+ // it's async by default, so we need to disable it when async was not requested
+ result |= CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT; // has extra handling in GetDesiredAccess
+ }
+ if (((int)options & 0x20000000) != 0) // NoBuffering
+ {
+ result |= CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING;
+ }
+
+ return result;
+ }
+
+ private static ObjectAttributes GetObjectAttributes(FileShare share)
+ => (share & FileShare.Inheritable) != 0 ? ObjectAttributes.OBJ_INHERIT : 0;
+
/// <summary>
/// File creation disposition when calling directly to NT APIs.
/// </summary>
}
internal const uint FileModeInformation = 16;
- internal const uint FILE_SYNCHRONOUS_IO_ALERT = 0x00000010;
- internal const uint FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;
internal const int STATUS_INVALID_HANDLE = unchecked((int)0xC0000008);
}
Link="Common\Interop\Windows\Interop.OBJECT_ATTRIBUTES.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.SECURITY_QUALITY_OF_SERVICE.cs"
+ Link="Common\Interop\Windows\Interop.SECURITY_QUALITY_OF_SERVICE.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CopyFile.cs"
Link="Common\Interop\Windows\Interop.CopyFile.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CopyFileEx.cs"
#cmakedefine01 HAVE_STRLCAT
#cmakedefine01 HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP
#cmakedefine01 HAVE_POSIX_ADVISE
+#cmakedefine01 HAVE_POSIX_FALLOCATE
+#cmakedefine01 HAVE_POSIX_FALLOCATE64
#cmakedefine01 PRIORITY_REQUIRES_INT_WHO
#cmakedefine01 KEVENT_REQUIRES_INT_PARAMS
#cmakedefine01 HAVE_IOCTL
DllImportEntry(SystemNative_FTruncate)
DllImportEntry(SystemNative_Poll)
DllImportEntry(SystemNative_PosixFAdvise)
+ DllImportEntry(SystemNative_PosixFAllocate)
DllImportEntry(SystemNative_Read)
DllImportEntry(SystemNative_ReadLink)
DllImportEntry(SystemNative_Rename)
int fileDescriptor = ToFileDescriptor(fd);
int32_t result;
- while ((result =
+ while ((result =
#if defined(TARGET_OSX) && HAVE_F_FULLFSYNC
fcntl(fileDescriptor, F_FULLFSYNC)
#else
#endif
}
+int32_t SystemNative_PosixFAllocate(intptr_t fd, int64_t offset, int64_t length)
+{
+ assert_msg(offset == 0, "Invalid offset value", (int)offset);
+
+ int fileDescriptor = ToFileDescriptor(fd);
+ int32_t result;
+#if HAVE_POSIX_FALLOCATE64 // 64-bit Linux
+ while ((result = posix_fallocate64(fileDescriptor, (off64_t)offset, (off64_t)length)) == EINTR);
+#elif HAVE_POSIX_FALLOCATE // 32-bit Linux
+ while ((result = posix_fallocate(fileDescriptor, (off_t)offset, (off_t)length)) == EINTR);
+#elif defined(F_PREALLOCATE) // macOS
+ fstore_t fstore;
+ fstore.fst_flags = F_ALLOCATECONTIG; // ensure contiguous space
+ fstore.fst_posmode = F_PEOFPOSMODE; // allocate from the physical end of file, as offset MUST NOT be 0 for F_VOLPOSMODE
+ fstore.fst_offset = (off_t)offset;
+ fstore.fst_length = (off_t)length;
+ fstore.fst_bytesalloc = 0; // output size, can be > length
+
+ while ((result = fcntl(fileDescriptor, F_PREALLOCATE, &fstore)) == -1 && errno == EINTR);
+
+ if (result == -1)
+ {
+ // we have failed to allocate contiguous space, let's try non-contiguous
+ fstore.fst_flags = F_ALLOCATEALL; // all or nothing
+ while ((result = fcntl(fileDescriptor, F_PREALLOCATE, &fstore)) == -1 && errno == EINTR);
+ }
+#elif defined(F_ALLOCSP) || defined(F_ALLOCSP64) // FreeBSD
+ #if HAVE_FLOCK64
+ struct flock64 lockArgs;
+ int command = F_ALLOCSP64;
+ #else
+ struct flock lockArgs;
+ int command = F_ALLOCSP;
+ #endif
+
+ lockArgs.l_whence = SEEK_SET;
+ lockArgs.l_start = (off_t)offset;
+ lockArgs.l_len = (off_t)length;
+
+ while ((result = fcntl(fileDescriptor, command, &lockArgs)) == -1 && errno == EINTR);
+#endif
+
+#if defined(F_PREALLOCATE) || defined(F_ALLOCSP) || defined(F_ALLOCSP64)
+ // most of the Unixes implement posix_fallocate which does NOT set the last error
+ // fctnl does, but to mimic the posix_fallocate behaviour we just return error
+ if (result == -1)
+ {
+ result = errno;
+ }
+ else
+ {
+ // align the behaviour with what posix_fallocate does (change reported file size)
+ ftruncate(fileDescriptor, length);
+ }
+#endif
+
+ // error codes can be OS-specific, so this is why this handling is done here rather than in the managed layer
+ switch (result)
+ {
+ case ENOSPC: // there was not enough space
+ return -1;
+ case EFBIG: // the file was too large
+ return -2;
+ case ENODEV: // not a regular file
+ case ESPIPE: // a pipe
+ // We ignore it, as FileStream contract makes it clear that allocationSize is ignored for non-regular files.
+ return 0;
+ case EINVAL:
+ // We control the offset and length so they are correct.
+ assert_msg(length >= 0, "Invalid length value", (int)length);
+ // But if the underlying filesystem does not support the operation, we just ignore it and treat as a hint.
+ return 0;
+ default:
+ assert(result != EINTR); // it can't happen here as we retry the call on EINTR
+ assert(result != EBADF); // it can't happen here as this method is being called after a succesfull call to open (with write permissions) before returning the SafeFileHandle to the user
+ return 0;
+ }
+}
+
int32_t SystemNative_Read(intptr_t fd, void* buffer, int32_t bufferSize)
{
return Common_Read(fd, buffer, bufferSize);
#endif
}
// If we copied to a filesystem (eg EXFAT) that does not preserve POSIX ownership, all files appear
- // to be owned by root. If we aren't running as root, then we won't be an owner of our new file, and
+ // to be owned by root. If we aren't running as root, then we won't be an owner of our new file, and
// attempting to copy metadata to it will fail with EPERM. We have copied successfully, we just can't
// copy metadata. The best thing we can do is skip copying the metadata.
if (ret != 0 && errno != EPERM)
*/
PALEXPORT int32_t SystemNative_PosixFAdvise(intptr_t fd, int64_t offset, int64_t length, int32_t advice);
+/**
+ * Ensures that disk space is allocated.
+ *
+ * Returns -1 on ENOSPC, -2 on EFBIG. On success or ignorable error, 0 is returned.
+ */
+PALEXPORT int32_t SystemNative_PosixFAllocate(intptr_t fd, int64_t offset, int64_t length);
+
/**
* Reads the number of bytes specified into the provided buffer from the specified, opened file descriptor.
*
fcntl.h
HAVE_POSIX_ADVISE)
+check_symbol_exists(
+ posix_fallocate
+ fcntl.h
+ HAVE_POSIX_FALLOCATE)
+
+check_symbol_exists(
+ posix_fallocate64
+ fcntl.h
+ HAVE_POSIX_FALLOCATE64)
+
check_symbol_exists(
ioctl
sys/ioctl.h
-<Project Sdk="Microsoft.NET.Sdk">
+ï»ż<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPartialFacadeAssembly>true</IsPartialFacadeAssembly>
Link="Common\Interop\Windows\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.LongFileTime.cs"
Link="Common\Interop\Windows\Interop.LongFileTime.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.SECURITY_QUALITY_OF_SERVICE.cs"
+ Link="Common\Interop\Windows\Interop.SECURITY_QUALITY_OF_SERVICE.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.OBJECT_ATTRIBUTES.cs"
Link="Common\Interop\Windows\Interop.OBJECT_ATTRIBUTES.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
private unsafe IntPtr CreateRelativeDirectoryHandle(ReadOnlySpan<char> relativePath, string fullPath)
{
- (int status, IntPtr handle) = Interop.NtDll.CreateFile(
+ (uint status, IntPtr handle) = Interop.NtDll.CreateFile(
relativePath,
_directoryHandle,
Interop.NtDll.CreateDisposition.FILE_OPEN,
createOptions: Interop.NtDll.CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT | Interop.NtDll.CreateOptions.FILE_DIRECTORY_FILE
| Interop.NtDll.CreateOptions.FILE_OPEN_FOR_BACKUP_INTENT);
- switch ((uint)status)
+ switch (status)
{
case Interop.StatusOptions.STATUS_SUCCESS:
return handle;
// such as ERROR_ACCESS_DENIED. As we want to replicate Win32 handling/reporting and the mapping isn't documented,
// we should always do our logic on the converted code, not the NTSTATUS.
- int error = (int)Interop.NtDll.RtlNtStatusToDosError(status);
+ int error = (int)Interop.NtDll.RtlNtStatusToDosError((int)status);
if (ContinueOnDirectoryError(error, ignoreNotFound: true))
{
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using Xunit;
-
namespace System.IO.Tests
{
public class File_Open_str_fm : FileStream_ctor_str_fm
{
return File.Open(path, mode);
}
-
}
public class File_Open_str_fm_fa : FileStream_ctor_str_fm_fa
return reader.ReadToEnd();
}
}
-
}
}
}
-
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void Dispose_CallsVirtualDisposeTrueArg_ThrowsDuringFlushWriteBuffer_DisposeThrows()
{
base.Flush(flushToDisk);
}
}
-
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.Globalization;
-using System.IO;
using System.Tests;
-using Microsoft.DotNet.RemoteExecutor;
using Xunit;
namespace System.IO.Tests
}
}
-
[Fact]
public void NameReturnsUnknownForHandle()
{
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
using Xunit;
namespace System.IO.Tests
--- /dev/null
+ï»ż// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.IO.Tests
+{
+ [PlatformSpecific(~TestPlatforms.Browser)]
+ public partial class FileStream_ctor_options_as : FileStream_ctor_options_as_base
+ {
+ protected override long PreallocationSize => 10;
+
+ protected override long InitialLength => 10;
+
+ private long GetExpectedFileLength(long preallocationSize) => preallocationSize;
+
+ private long GetActualPreallocationSize(FileStream fileStream) => fileStream.Length;
+ }
+}
--- /dev/null
+ï»ż// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.IO.Tests
+{
+ public partial class FileStream_ctor_options_as : FileStream_ctor_options_as_base
+ {
+ protected override long PreallocationSize => 10;
+
+ protected override long InitialLength => 10;
+
+ private long GetExpectedFileLength(long preallocationSize) => preallocationSize;
+
+ private long GetActualPreallocationSize(FileStream fileStream)
+ {
+ // On Unix posix_fallocate modifies file length and we are using fstat to get it for verificaiton
+ Interop.Sys.FStat(fileStream.SafeFileHandle, out Interop.Sys.FileStatus fileStatus);
+ return fileStatus.Size;
+ }
+ }
+}
--- /dev/null
+ï»ż// 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.InteropServices;
+using System.Text;
+using Xunit;
+
+namespace System.IO.Tests
+{
+ public partial class FileStream_ctor_options_as : FileStream_ctor_options_as_base
+ {
+ protected override long PreallocationSize => 10;
+
+ protected override long InitialLength => 0; // Windows modifies AllocationSize, but not EndOfFile (file length)
+
+ private long GetExpectedFileLength(long preallocationSize) => 0; // Windows modifies AllocationSize, but not EndOfFile (file length)
+
+ private unsafe long GetActualPreallocationSize(FileStream fileStream)
+ {
+ Interop.Kernel32.FILE_STANDARD_INFO info;
+
+ Assert.True(Interop.Kernel32.GetFileInformationByHandleEx(fileStream.SafeFileHandle, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO)));
+
+ return info.AllocationSize;
+ }
+
+ [Theory]
+ [InlineData(@"\\?\")]
+ [InlineData(@"\??\")]
+ [InlineData("")]
+ public void ExtendedPathsAreSupported(string prefix)
+ {
+ const long preallocationSize = 123;
+
+ string filePath = prefix + Path.GetFullPath(GetPathToNonExistingFile());
+
+ using (var fs = new FileStream(filePath, GetOptions(FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize)))
+ {
+ Assert.True(GetActualPreallocationSize(fs) >= preallocationSize, $"Provided {preallocationSize}, actual: {GetActualPreallocationSize(fs)}");
+ }
+ }
+
+ [ConditionalTheory(nameof(IsFat32))]
+ [InlineData(FileMode.Create)]
+ [InlineData(FileMode.CreateNew)]
+ [InlineData(FileMode.OpenOrCreate)]
+ public void WhenFileIsTooLargeTheErrorMessageContainsAllDetails(FileMode mode)
+ {
+ const long tooMuch = uint.MaxValue + 1L; // more than FAT32 max size
+
+ string filePath = GetPathToNonExistingFile();
+ Assert.StartsWith(Path.GetTempPath(), filePath); // this is what IsFat32 method relies on
+
+ IOException ex = Assert.Throws<IOException>(() => new FileStream(filePath, GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, tooMuch)));
+ Assert.Contains(filePath, ex.Message);
+ Assert.Contains(tooMuch.ToString(), ex.Message);
+
+ Assert.False(File.Exists(filePath)); // ensure it was NOT created
+ }
+
+ public static bool IsFat32
+ {
+ get
+ {
+ string testDirectory = Path.GetTempPath(); // logic taken from FileCleanupTestBase, can't call the property here as it's not static
+
+ var volumeNameBufffer = new StringBuilder(250);
+ var fileSystemNameBuffer = new StringBuilder(250);
+
+ if (GetVolumeInformation(
+ Path.GetPathRoot(testDirectory),
+ volumeNameBufffer,
+ volumeNameBufffer.Capacity,
+ out uint _,
+ out uint _,
+ out uint _,
+ fileSystemNameBuffer,
+ fileSystemNameBuffer.Capacity
+ ))
+ {
+ return fileSystemNameBuffer.ToString().Equals("FAT32", StringComparison.OrdinalIgnoreCase);
+ }
+
+ return false;
+ }
+ }
+
+ [DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true)]
+ public extern static bool GetVolumeInformation(
+ string rootPathName,
+ StringBuilder volumeNameBuffer,
+ int volumeNameSize,
+ out uint volumeSerialNumber,
+ out uint maximumComponentLength,
+ out uint fileSystemFlags,
+ StringBuilder fileSystemNameBuffer,
+ int fileSystemNameSize);
+ }
+}
--- /dev/null
+ï»ż// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.IO.Tests
+{
+ public abstract class FileStream_ctor_options_as_base : FileStream_ctor_str_fm_fa_fs_buffer_fo
+ {
+ protected abstract long PreallocationSize { get; }
+
+ protected override FileStream CreateFileStream(string path, FileMode mode)
+ => new FileStream(path,
+ new FileStreamOptions
+ {
+ Mode = mode,
+ Access = mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite,
+ PreallocationSize = PreallocationSize
+ });
+
+ protected override FileStream CreateFileStream(string path, FileMode mode, FileAccess access)
+ => new FileStream(path,
+ new FileStreamOptions
+ {
+ Mode = mode,
+ Access = access,
+ PreallocationSize = PreallocationSize
+ });
+
+ protected FileStreamOptions GetOptions(FileMode mode, FileAccess access, FileShare share, FileOptions options, long preAllocationSize)
+ => new FileStreamOptions
+ {
+ Mode = mode,
+ Access = access,
+ Share = share,
+ Options = options,
+ PreallocationSize = preAllocationSize
+ };
+ }
+
+ public class FileStream_ctor_options_as_zero : FileStream_ctor_options_as_base
+ {
+ protected override long PreallocationSize => 0; // specifying 0 should have no effect
+
+ protected override long InitialLength => 0;
+ }
+
+ [CollectionDefinition("NoParallelTests", DisableParallelization = true)]
+ public partial class NoParallelTests { }
+
+ // Don't run in parallel as the WhenDiskIsFullTheErrorMessageContainsAllDetails test
+ // consumes entire available free space on the disk (only on Linux, this is how posix_fallocate works)
+ // and if we try to run other disk-writing test in the meantime we are going to get "No space left on device" exception.
+ [Collection("NoParallelTests")]
+ public partial class FileStream_ctor_options_as : FileStream_ctor_options_as_base
+ {
+ [Fact]
+ public void NegativePreallocationSizeThrows()
+ {
+ string filePath = GetPathToNonExistingFile();
+ ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(
+ () => new FileStream(filePath, GetOptions(FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.None, -1)));
+ }
+
+ [Theory]
+ [InlineData(FileMode.Create, 0L)]
+ [InlineData(FileMode.CreateNew, 0L)]
+ [InlineData(FileMode.OpenOrCreate, 0L)]
+ public void WhenFileIsCreatedWithoutPreallocationSizeSpecifiedThePreallocationSizeIsNotSet(FileMode mode, long preallocationSize)
+ {
+ using (var fs = new FileStream(GetPathToNonExistingFile(), GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize)))
+ {
+ Assert.Equal(0, GetActualPreallocationSize(fs));
+ Assert.Equal(0, fs.Length);
+ Assert.Equal(0, fs.Position);
+ }
+ }
+
+ [Theory]
+ [InlineData(FileMode.Open, 0L)]
+ [InlineData(FileMode.Open, 1L)]
+ [InlineData(FileMode.OpenOrCreate, 0L)]
+ [InlineData(FileMode.OpenOrCreate, 1L)]
+ [InlineData(FileMode.Append, 0L)]
+ [InlineData(FileMode.Append, 1L)]
+ public void WhenExistingFileIsBeingOpenedWithPreallocationSizeSpecifiedThePreallocationSizeIsNotChanged(FileMode mode, long preallocationSize)
+ {
+ const int initialSize = 1;
+ string filePath = GetPathToNonExistingFile();
+ File.WriteAllBytes(filePath, new byte[initialSize]);
+ long initialPreallocationSize;
+
+ using (var fs = new FileStream(filePath, GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, 0))) // preallocationSize NOT provided
+ {
+ initialPreallocationSize = GetActualPreallocationSize(fs); // just read it to ensure it's not being changed
+ }
+
+ using (var fs = new FileStream(filePath, GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize)))
+ {
+ Assert.Equal(initialPreallocationSize, GetActualPreallocationSize(fs)); // it has NOT been changed
+ Assert.Equal(initialSize, fs.Length);
+ Assert.Equal(mode == FileMode.Append ? initialSize : 0, fs.Position);
+ }
+ }
+
+ [Theory]
+ [InlineData(FileMode.Create)]
+ [InlineData(FileMode.CreateNew)]
+ [InlineData(FileMode.OpenOrCreate)]
+ public void WhenFileIsCreatedWithPreallocationSizeSpecifiedThePreallocationSizeIsSet(FileMode mode)
+ {
+ const long preallocationSize = 123;
+
+ using (var fs = new FileStream(GetPathToNonExistingFile(), GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize)))
+ {
+ // OS might allocate MORE than we have requested
+ Assert.True(GetActualPreallocationSize(fs) >= preallocationSize, $"Provided {preallocationSize}, actual: {GetActualPreallocationSize(fs)}");
+ Assert.Equal(GetExpectedFileLength(preallocationSize), fs.Length);
+ Assert.Equal(0, fs.Position);
+ }
+ }
+
+ [OuterLoop("Might allocate 1 TB file if there is enough space on the disk")]
+ // macOS fcntl doc does not mention ENOSPC error: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html
+ // But depending on the OS version, it might actually return it.
+ // Since we don't want to have unstable tests, it's better to not run it on macOS at all.
+ [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)]
+ [Theory]
+ [InlineData(FileMode.Create)]
+ [InlineData(FileMode.CreateNew)]
+ [InlineData(FileMode.OpenOrCreate)]
+ public void WhenDiskIsFullTheErrorMessageContainsAllDetails(FileMode mode)
+ {
+ const long tooMuch = 1024L * 1024L * 1024L * 1024L; // 1 TB
+
+ string filePath = GetPathToNonExistingFile();
+
+ IOException ex = Assert.Throws<IOException>(() => new FileStream(filePath, GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, tooMuch)));
+ Assert.Contains(filePath, ex.Message);
+ Assert.Contains(tooMuch.ToString(), ex.Message);
+
+ // ensure it was NOT created (provided OOTB by Windows, emulated on Unix)
+ bool exists = File.Exists(filePath);
+ if (exists)
+ {
+ File.Delete(filePath);
+ }
+ Assert.False(exists);
+ }
+
+ [Fact]
+ public void WhenFileIsTruncatedWithoutPreallocationSizeSpecifiedThePreallocationSizeIsNotSet()
+ {
+ const int initialSize = 10_000;
+
+ string filePath = GetPathToNonExistingFile();
+ File.WriteAllBytes(filePath, new byte[initialSize]);
+
+ using (var fs = new FileStream(filePath, GetOptions(FileMode.Truncate, FileAccess.Write, FileShare.None, FileOptions.None, 0)))
+ {
+ Assert.Equal(0, GetActualPreallocationSize(fs));
+ Assert.Equal(0, fs.Length);
+ Assert.Equal(0, fs.Position);
+ }
+ }
+
+ [Fact]
+ public void WhenFileIsTruncatedWithPreallocationSizeSpecifiedThePreallocationSizeIsSet()
+ {
+ const int initialSize = 10_000; // this must be more than 4kb which seems to be minimum allocation size on Windows
+ const long preallocationSize = 100;
+
+ string filePath = GetPathToNonExistingFile();
+ File.WriteAllBytes(filePath, new byte[initialSize]);
+
+ using (var fs = new FileStream(filePath, GetOptions(FileMode.Truncate, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize)))
+ {
+ Assert.True(GetActualPreallocationSize(fs) >= preallocationSize, $"Provided {preallocationSize}, actual: {GetActualPreallocationSize(fs)}");
+ // less than initial file size (file got truncated)
+ Assert.True(GetActualPreallocationSize(fs) < initialSize, $"initialSize {initialSize}, actual: {GetActualPreallocationSize(fs)}");
+ Assert.Equal(GetExpectedFileLength(preallocationSize), fs.Length);
+ Assert.Equal(0, fs.Position);
+ }
+ }
+
+ private string GetPathToNonExistingFile()
+ {
+ string filePath = GetTestFilePath();
+
+ if (File.Exists(filePath))
+ {
+ File.Delete(filePath);
+ }
+
+ return filePath;
+ }
+ }
+}
return new FileStream(handle, access, bufferSize);
}
-
[Theory,
InlineData(0),
InlineData(-1)]
return new FileStream(path, mode);
}
+ protected virtual long InitialLength => 0;
+
[Fact]
public void NullPathThrows()
{
Assert.Throws<DirectoryNotFoundException>(() => CreateFileStream(path, FileMode.Open));
}
-
public static TheoryData<string> StreamSpecifiers
{
get
using (FileStream fs = CreateFileStream(fileName, FileMode.Create))
{
// Ensure that the file was re-created
- Assert.Equal(0L, fs.Length);
+ Assert.Equal(InitialLength, fs.Length);
Assert.Equal(0L, fs.Position);
Assert.True(fs.CanRead);
Assert.True(fs.CanWrite);
using (FileStream fs = CreateFileStream(fileName, FileMode.Open))
{
// Ensure that the file was re-opened
- Assert.Equal(1L, fs.Length);
+ Assert.Equal(Math.Max(1L, InitialLength), fs.Length);
Assert.Equal(0L, fs.Position);
Assert.True(fs.CanRead);
Assert.True(fs.CanWrite);
using (FileStream fs = CreateFileStream(fileName, FileMode.OpenOrCreate))
{
// Ensure that the file was re-opened
- Assert.Equal(1L, fs.Length);
+ Assert.Equal(Math.Max(1L, InitialLength), fs.Length);
Assert.Equal(0L, fs.Position);
Assert.True(fs.CanRead);
Assert.True(fs.CanWrite);
using (FileStream fs = CreateFileStream(fileName, FileMode.Truncate))
{
// Ensure that the file was re-opened and truncated
- Assert.Equal(0L, fs.Length);
+ Assert.Equal(InitialLength, fs.Length);
Assert.Equal(0L, fs.Position);
Assert.True(fs.CanRead);
Assert.True(fs.CanWrite);
using (FileStream fs = CreateFileStream(fileName, FileMode.Append))
{
// Ensure that the file was re-opened and position set to end
- Assert.Equal(1L, fs.Length);
- Assert.Equal(1L, fs.Position);
+ Assert.Equal(Math.Max(1L, InitialLength), fs.Length);
+ Assert.Equal(fs.Length, fs.Position);
Assert.False(fs.CanRead);
Assert.True(fs.CanSeek);
Assert.True(fs.CanWrite);
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
using Xunit;
namespace System.IO.Tests
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
using Xunit;
namespace System.IO.Tests
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
using Xunit;
namespace System.IO.Tests
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
using Xunit;
namespace System.IO.Tests
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
using Xunit;
namespace System.IO.Tests
{
- [ActiveIssue("https://github.com/dotnet/runtime/issues/34583", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)]
public class FileStream_ctor_str_fm_fa_fs_buffer_async : FileStream_ctor_str_fm_fa_fs_buffer
{
protected sealed override FileStream CreateFileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
return new FileStream(path, mode, access, share, bufferSize, useAsync);
}
- [Fact]
- public void ValidUseAsync()
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void ValidUseAsync(bool isAsync)
{
- using (CreateFileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, c_DefaultBufferSize, true))
- { }
-
- using (CreateFileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, c_DefaultBufferSize, false))
- { }
+ using (FileStream fs = CreateFileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, c_DefaultBufferSize, isAsync))
+ {
+ Assert.Equal(isAsync, fs.IsAsync);
+ }
}
-
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
using Xunit;
namespace System.IO.Tests
using (FileStream fs = CreateFileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, c_DefaultBufferSize, option))
{
+ Assert.Equal((option & FileOptions.Asynchronous) != 0, fs.IsAsync);
+
// make sure we can write, seek, and read data with this option set
fs.Write(data, 0, data.Length);
fs.Position = 0;
}
Assert.False(File.Exists(path));
}
-
}
}
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
<Compile Remove="..\**\*.Windows.cs" />
<Compile Remove="..\**\*.Browser.cs" />
+ <Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs" Link="Interop\Unix\Interop.Libraries.cs" />
+ <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Stat.cs" Link="Interop\Unix\System.Native\Interop.Stat.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
<Compile Remove="..\**\*.Unix.cs" />
<Compile Remove="..\**\*.Browser.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs" Link="Interop\Windows\Interop.BOOL.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.FILE_STANDARD_INFO.cs" Link="Interop\Windows\Interop.FILE_STANDARD_INFO.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs" Link="Interop\Windows\Interop.Libraries.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetFileInformationByHandleEx.cs" Link="Interop\Windows\Interop.GetFileInformationByHandleEx.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsBrowser)' == 'true'">
<Compile Remove="..\**\*.Unix.cs" />
<Compile Remove="..\**\*.Windows.cs" />
- </ItemGroup>
+ </ItemGroup>
<ItemGroup>
<!-- Helpers -->
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GenericOperations.cs" Link="Interop\Windows\Interop.GenericOperations.cs" />
<Compile Include="FileInfo\GetSetAttributesCommon.cs" />
<Compile Include="FileInfo\IsReadOnly.cs" />
<Compile Include="FileInfo\Replace.cs" />
+ <Compile Include="FileStream\ctor_options_as.cs" />
<Compile Include="FileStream\Handle.cs" />
<Compile Include="Directory\GetLogicalDrives.cs" />
<Compile Include="FileStream\LockUnlock.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
<Compile Include="FileSystemTest.Unix.cs" />
+ <Compile Include="FileStream\ctor_options_as.Unix.cs" />
+ <Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs" Link="Interop\Unix\Interop.Libraries.cs" />
+ <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Stat.cs" Link="Interop\Unix\System.Native\Interop.Stat.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
<Compile Include="FileSystemTest.Windows.cs" />
+ <Compile Include="FileStream\ctor_options_as.Windows.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs" Link="Interop\Windows\Interop.BOOL.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.FILE_STANDARD_INFO.cs" Link="Interop\Windows\Interop.FILE_STANDARD_INFO.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs" Link="Interop\Windows\Interop.Libraries.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetFileInformationByHandleEx.cs" Link="Interop\Windows\Interop.GetFileInformationByHandleEx.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsBrowser)' == 'true'">
<Compile Include="FileSystemTest.Browser.cs" />
- </ItemGroup>
+ <Compile Include="FileStream\ctor_options_as.Browser.cs" />
+ </ItemGroup>
<ItemGroup>
<!-- Rewritten -->
<Compile Include="DirectoryInfo\GetSetAttributes.cs" />
<data name="IO_AlreadyExists_Name" xml:space="preserve">
<value>Cannot create '{0}' because a file or directory with the same name already exists.</value>
</data>
+ <data name="IO_DiskFull_Path_AllocationSize" xml:space="preserve">
+ <value>Failed to create '{0}' with allocation size '{1}' because the disk was full.</value>
+ </data>
+ <data name="IO_FileTooLarge_Path_AllocationSize" xml:space="preserve">
+ <value>Failed to create '{0}' with allocation size '{1}' because the file was too large.</value>
+ </data>
<data name="IO_BindHandleFailed" xml:space="preserve">
<value>BindHandle for ThreadPool failed on this handle.</value>
</data>
-ï»ż<Project>
+<Project>
<PropertyGroup>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>c5ed3c1d-b572-46f1-8f96-522a85ce1179</SharedGUID>
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileShare.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\HandleInheritability.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\InvalidDataException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IO\IOException.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CreateFile.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.CreateFile.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)Interop\Windows\NtDll\Interop.NtCreateFile.cs">
+ <Link>Common\Interop\Windows\NtDll\Interop.NtCreateFile.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)Interop\Windows\NtDll\Interop.RtlNtStatusToDosError.cs">
+ <Link>Common\Interop\Windows\NtDll\Interop.RtlNtStatusToDosError.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.DeleteFile.cs">
+ <Link>Common\Interop\Windows\Kernel32\Interop.DeleteFile.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CriticalSection.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.CriticalSection.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs">
+ <Link>Common\Interop\Windows\Interop.UNICODE_STRING.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.SECURITY_QUALITY_OF_SERVICE.cs">
+ <Link>Common\Interop\Windows\Interop.SECURITY_QUALITY_OF_SERVICE.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.OBJECT_ATTRIBUTES.cs">
+ <Link>Common\Interop\Windows\Interop.OBJECT_ATTRIBUTES.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SecurityOptions.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.SecurityOptions.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.PosixFAdvise.cs">
<Link>Common\Interop\Unix\System.Native\Interop.PosixFAdvise.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.PosixFAllocate.cs">
+ <Link>Common\Interop\Unix\System.Native\Interop.PosixFAllocate.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Read.cs">
<Link>Common\Interop\Unix\System.Native\Interop.Read.cs</Link>
</Compile>
public class FileStream : Stream
{
internal const int DefaultBufferSize = 4096;
- private const FileShare DefaultShare = FileShare.Read;
+ internal const FileShare DefaultShare = FileShare.Read;
private const bool DefaultIsAsync = false;
/// <summary>Caches whether Serialization Guard has been disabled for file writes</summary>
}
public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
+ : this(path, mode, access, share, bufferSize, options, 0)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="System.IO.FileStream" /> class with the specified path, creation mode, read/write and sharing permission, the access other FileStreams can have to the same file, the buffer size, additional file options and the allocation size.
+ /// </summary>
+ /// <param name="path">A relative or absolute path for the file that the current <see cref="System.IO.FileStream" /> instance will encapsulate.</param>
+ /// <param name="options">An object that describes optional <see cref="System.IO.FileStream" /> parameters to use.</param>
+ /// <exception cref="T:System.ArgumentNullException"><paramref name="path" /> is <see langword="null" />.</exception>
+ /// <exception cref="T:System.ArgumentException"><paramref name="path" /> is an empty string (""), contains only white space, or contains one or more invalid characters.
+ /// -or-
+ /// <paramref name="path" /> refers to a non-file device, such as <c>CON:</c>, <c>COM1:</c>, <c>LPT1:</c>, etc. in an NTFS environment.</exception>
+ /// <exception cref="T:System.NotSupportedException"><paramref name="path" /> refers to a non-file device, such as <c>CON:</c>, <c>COM1:</c>, <c>LPT1:</c>, etc. in a non-NTFS environment.</exception>
+ /// <exception cref="T:System.ArgumentOutOfRangeException"><see cref="System.IO.FileStreamOptions.PreallocationSize" /> is negative.
+ /// -or-
+ /// <see cref="System.IO.FileStreamOptions.Mode" />, <see cref="System.IO.FileStreamOptions.Access" />, or <see cref="System.IO.FileStreamOptions.Share" /> contain an invalid value.</exception>
+ /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found, such as when <see cref="System.IO.FileStreamOptions.Mode" /> is <see langword="FileMode.Truncate" /> or <see langword="FileMode.Open" />, and the file specified by <paramref name="path" /> does not exist. The file must already exist in these modes.</exception>
+ /// <exception cref="T:System.IO.IOException">An I/O error, such as specifying <see langword="FileMode.CreateNew" /> when the file specified by <paramref name="path" /> already exists, occurred.
+ /// -or-
+ /// The stream has been closed.
+ /// -or-
+ /// The disk was full (when <see cref="System.IO.FileStreamOptions.PreallocationSize" /> was provided and <paramref name="path" /> was pointing to a regular file).
+ /// -or-
+ /// The file was too large (when <see cref="System.IO.FileStreamOptions.PreallocationSize" /> was provided and <paramref name="path" /> was pointing to a regular file).</exception>
+ /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission.</exception>
+ /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
+ /// <exception cref="T:System.UnauthorizedAccessException">The <see cref="System.IO.FileStreamOptions.Access" /> requested is not permitted by the operating system for the specified <paramref name="path" />, such as when <see cref="System.IO.FileStreamOptions.Access" /> is <see cref="System.IO.FileAccess.Write" /> or <see cref="System.IO.FileAccess.ReadWrite" /> and the file or directory is set for read-only access.
+ /// -or-
+ /// <see cref="F:System.IO.FileOptions.Encrypted" /> is specified for <see cref="System.IO.FileStreamOptions.Options" /> , but file encryption is not supported on the current platform.</exception>
+ /// <exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. </exception>
+ public FileStream(string path, FileStreamOptions options)
+ : this(path, options.Mode, options.Access, options.Share, DefaultBufferSize, options.Options, options.PreallocationSize)
+ {
+ }
+
+ private FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize)
{
if (path == null)
{
{
throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
}
+ else if (preallocationSize < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(preallocationSize), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
// Write access validation
if ((access & FileAccess.Write) == 0)
SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch);
}
- _strategy = FileStreamHelpers.ChooseStrategy(this, path, mode, access, share, bufferSize, options);
+ _strategy = FileStreamHelpers.ChooseStrategy(this, path, mode, access, share, bufferSize, options, preallocationSize);
}
[Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")]
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.IO
+{
+ public sealed class FileStreamOptions
+ {
+ /// <summary>
+ /// One of the enumeration values that determines how to open or create the file.
+ /// </summary>
+ public FileMode Mode { get; set; }
+ /// <summary>
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by the <see cref="FileStream" /> object. This also determines the values returned by the <see cref="System.IO.FileStream.CanRead" /> and <see cref="System.IO.FileStream.CanWrite" /> properties of the <see cref="FileStream" /> object.
+ /// </summary>
+ public FileAccess Access { get; set; } = FileAccess.Read;
+ /// <summary>
+ /// A bitwise combination of the enumeration values that determines how the file will be shared by processes. The default value is <see cref="System.IO.FileShare.Read" />.
+ /// </summary>
+ public FileShare Share { get; set; } = FileStream.DefaultShare;
+ /// <summary>
+ /// A bitwise combination of the enumeration values that specifies additional file options. The default value is <see cref="System.IO.FileOptions.None" />, which indicates synchronous IO.
+ /// </summary>
+ public FileOptions Options { get; set; }
+ /// <summary>
+ /// The initial allocation size in bytes for the file. A positive value is effective only when a regular file is being created, overwritten, or replaced.
+ /// When the value is negative, the <see cref="FileStream" /> constructor throws an <see cref="ArgumentOutOfRangeException" />.
+ /// In other cases (including the default 0 value), it's ignored.
+ /// </summary>
+ public long PreallocationSize { get; set; }
+ }
+}
{
}
- internal AsyncWindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options)
- : base(path, mode, access, share, options)
+ internal AsyncWindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize)
+ : base(path, mode, access, share, options, preallocationSize)
{
}
private static FileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync)
=> new Net5CompatFileStreamStrategy(handle, access, bufferSize, isAsync);
- private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
- => new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize, options);
+ private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize)
+ => new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize, options, preallocationSize);
- internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options)
+ internal static SafeFileHandle OpenHandle(string path, 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);
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);
}
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
return EnableBufferingIfNeeded(strategy, bufferSize);
}
- private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
+ private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize)
{
if (UseNet5CompatStrategy)
{
- return new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize, options);
+ return new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize, options, preallocationSize);
}
WindowsFileStreamStrategy strategy = (options & FileOptions.Asynchronous) != 0
- ? new AsyncWindowsFileStreamStrategy(path, mode, access, share, options)
- : new SyncWindowsFileStreamStrategy(path, mode, access, share, options);
+ ? new AsyncWindowsFileStreamStrategy(path, mode, access, share, options, preallocationSize)
+ : new SyncWindowsFileStreamStrategy(path, mode, access, share, options, preallocationSize);
return EnableBufferingIfNeeded(strategy, bufferSize);
}
- // TODO: we might want to consider strategy.IsPipe here and never enable buffering for async pipes
internal static FileStreamStrategy EnableBufferingIfNeeded(WindowsFileStreamStrategy strategy, int bufferSize)
=> bufferSize == 1 ? strategy : new BufferedFileStreamStrategy(strategy, bufferSize);
- internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options)
- => CreateFileOpenHandle(path, mode, access, share, options);
+ internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize)
+ => CreateFileOpenHandle(path, mode, access, share, options, preallocationSize);
- private static unsafe SafeFileHandle CreateFileOpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options)
+ private static unsafe SafeFileHandle CreateFileOpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize)
{
- Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);
+ using (DisableMediaInsertionPrompt.Create())
+ {
+ Debug.Assert(path != null);
- int fAccess =
- ((access & FileAccess.Read) == FileAccess.Read ? Interop.Kernel32.GenericOperations.GENERIC_READ : 0) |
- ((access & FileAccess.Write) == FileAccess.Write ? Interop.Kernel32.GenericOperations.GENERIC_WRITE : 0);
+ if (ShouldPreallocate(preallocationSize, access, mode))
+ {
+ IntPtr fileHandle = NtCreateFile(path, mode, access, share, options, preallocationSize);
- // Our Inheritable bit was stolen from Windows, but should be set in
- // the security attributes class. Don't leave this bit set.
- share &= ~FileShare.Inheritable;
+ return ValidateFileHandle(new SafeFileHandle(fileHandle, ownsHandle: true), path, (options & FileOptions.Asynchronous) != 0);
+ }
- // Must use a valid Win32 constant here...
- if (mode == FileMode.Append)
- mode = FileMode.OpenOrCreate;
+ Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);
- int flagsAndAttributes = (int)options;
+ int fAccess =
+ ((access & FileAccess.Read) == FileAccess.Read ? Interop.Kernel32.GenericOperations.GENERIC_READ : 0) |
+ ((access & FileAccess.Write) == FileAccess.Write ? Interop.Kernel32.GenericOperations.GENERIC_WRITE : 0);
- // For mitigating local elevation of privilege attack through named pipes
- // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
- // named pipe server can't impersonate a high privileged client security context
- // (note that this is the effective default on CreateFile2)
- flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS);
+ // Our Inheritable bit was stolen from Windows, but should be set in
+ // the security attributes class. Don't leave this bit set.
+ share &= ~FileShare.Inheritable;
- using (DisableMediaInsertionPrompt.Create())
- {
- Debug.Assert(path != null);
- return ValidateFileHandle(
+ // Must use a valid Win32 constant here...
+ if (mode == FileMode.Append)
+ mode = FileMode.OpenOrCreate;
+
+ int flagsAndAttributes = (int)options;
+
+ // For mitigating local elevation of privilege attack through named pipes
+ // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
+ // named pipe server can't impersonate a high privileged client security context
+ // (note that this is the effective default on CreateFile2)
+ flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS);
+
+ SafeFileHandle safeFileHandle = ValidateFileHandle(
Interop.Kernel32.CreateFile(path, fAccess, share, &secAttrs, mode, flagsAndAttributes, IntPtr.Zero),
path,
(options & FileOptions.Asynchronous) != 0);
+
+ return safeFileHandle;
+ }
+ }
+
+ private static IntPtr NtCreateFile(string fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize)
+ {
+ uint ntStatus;
+ IntPtr fileHandle;
+
+ const string mandatoryNtPrefix = @"\??\";
+ if (fullPath.StartsWith(mandatoryNtPrefix, StringComparison.Ordinal))
+ {
+ (ntStatus, fileHandle) = Interop.NtDll.CreateFile(fullPath, mode, access, share, options, preallocationSize);
+ }
+ else
+ {
+ var vsb = new ValueStringBuilder(stackalloc char[1024]);
+ vsb.Append(mandatoryNtPrefix);
+
+ if (fullPath.StartsWith(@"\\?\", StringComparison.Ordinal)) // NtCreateFile does not support "\\?\" prefix, only "\??\"
+ {
+ vsb.Append(fullPath.AsSpan(4));
+ }
+ else
+ {
+ vsb.Append(fullPath);
+ }
+
+ (ntStatus, fileHandle) = Interop.NtDll.CreateFile(vsb.AsSpan(), mode, access, share, options, preallocationSize);
+ vsb.Dispose();
+ }
+
+ switch (ntStatus)
+ {
+ case 0:
+ return fileHandle;
+ case Interop.NtDll.NT_ERROR_STATUS_DISK_FULL:
+ throw new IOException(SR.Format(SR.IO_DiskFull_Path_AllocationSize, fullPath, preallocationSize));
+ // NtCreateFile has a bug and it reports STATUS_INVALID_PARAMETER for files
+ // that are too big for the current file system. Example: creating a 4GB+1 file on a FAT32 drive.
+ case Interop.NtDll.NT_STATUS_INVALID_PARAMETER:
+ case Interop.NtDll.NT_ERROR_STATUS_FILE_TOO_LARGE:
+ throw new IOException(SR.Format(SR.IO_FileTooLarge_Path_AllocationSize, fullPath, preallocationSize));
+ default:
+ int error = (int)Interop.NtDll.RtlNtStatusToDosError((int)ntStatus);
+ throw Win32Marshal.GetExceptionForWin32Error(error, fullPath);
}
}
}
// If either of these two flags are set, the file handle is synchronous (not overlapped)
- return (fileMode & (Interop.NtDll.FILE_SYNCHRONOUS_IO_ALERT | Interop.NtDll.FILE_SYNCHRONOUS_IO_NONALERT)) > 0;
+ return (fileMode & (uint)(Interop.NtDll.CreateOptions.FILE_SYNCHRONOUS_IO_ALERT | Interop.NtDll.CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT)) > 0;
}
internal static void VerifyHandleIsSync(SafeFileHandle handle)
internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync)
=> WrapIfDerivedType(fileStream, ChooseStrategyCore(handle, access, share, bufferSize, isAsync));
- internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
- => WrapIfDerivedType(fileStream, ChooseStrategyCore(path, mode, access, share, bufferSize, options));
+ internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize)
+ => WrapIfDerivedType(fileStream, ChooseStrategyCore(path, mode, access, share, bufferSize, options, preallocationSize));
private static FileStreamStrategy WrapIfDerivedType(FileStream fileStream, FileStreamStrategy strategy)
=> fileStream.GetType() == typeof(FileStream)
e is UnauthorizedAccessException ||
e is NotSupportedException ||
(e is ArgumentException && !(e is ArgumentNullException));
+
+ internal static bool ShouldPreallocate(long preallocationSize, FileAccess access, FileMode mode)
+ => preallocationSize > 0
+ && (access & FileAccess.Write) != 0
+ && mode != FileMode.Open && mode != FileMode.Append
+ && !OperatingSystem.IsBrowser(); // WASM limitation
}
}
/// <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>
- private void Init(FileMode mode, FileShare share, string originalPath, FileOptions options)
+ /// <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.)
}
}
}
+
+ // 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));
+ }
+ }
}
/// <summary>Initializes a stream from an already open file handle (file descriptor).</summary>
private PreAllocatedOverlapped? _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations
private CompletionSource? _currentOverlappedOwner; // async op currently using the preallocated overlapped
- private void Init(FileMode mode, FileShare share, string originalPath, FileOptions options)
+ private void Init(FileMode mode, FileShare share, string originalPath, FileOptions options, long preallocationSize)
{
FileStreamHelpers.ValidateFileTypeForNonExtendedPaths(_fileHandle, originalPath);
_fileHandle = handle;
}
- internal Net5CompatFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
+ internal Net5CompatFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize)
{
string fullPath = Path.GetFullPath(path);
if ((options & FileOptions.Asynchronous) != 0)
_useAsyncIO = true;
- _fileHandle = FileStreamHelpers.OpenHandle(fullPath, mode, access, share, options);
+ _fileHandle = FileStreamHelpers.OpenHandle(fullPath, mode, access, share, options, preallocationSize);
try
{
- Init(mode, share, path, options);
+ Init(mode, share, path, options, preallocationSize);
}
catch
{
{
}
- internal SyncWindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options)
- : base(path, mode, access, share, options)
+ internal SyncWindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize)
+ : base(path, mode, access, share, options, preallocationSize)
{
}
_fileHandle = handle;
}
- internal WindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options)
+ internal WindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options, long preallocationSize)
{
string fullPath = Path.GetFullPath(path);
_access = access;
_share = share;
- _fileHandle = FileStreamHelpers.OpenHandle(fullPath, mode, access, share, options);
+ _fileHandle = FileStreamHelpers.OpenHandle(fullPath, mode, access, share, options, preallocationSize);
try
{
Delete = 4,
Inheritable = 16,
}
+ public sealed class FileStreamOptions
+ {
+ public FileStreamOptions() { }
+ public System.IO.FileMode Mode { get; set; }
+ public System.IO.FileAccess Access { get; set; }
+ public System.IO.FileShare Share { get; set; }
+ public System.IO.FileOptions Options { get; set; }
+ public long PreallocationSize { get; set; }
+ }
public partial class FileStream : System.IO.Stream
{
public FileStream(Microsoft.Win32.SafeHandles.SafeFileHandle handle, System.IO.FileAccess access) { }
public FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, int bufferSize) { }
public FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, int bufferSize, bool useAsync) { }
public FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, int bufferSize, System.IO.FileOptions options) { }
+ public FileStream(string path, System.IO.FileStreamOptions options) { }
public override bool CanRead { get { throw null; } }
public override bool CanSeek { get { throw null; } }
public override bool CanWrite { get { throw null; } }
<Compile Include="System\Security\Principal\WindowsPrincipal.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.BOOLEAN.cs"
+ Link="Common\Interop\Windows\Advapi32\Interop.BOOLEAN.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.SECURITY_QUALITY_OF_SERVICE.cs"
+ Link="Common\Interop\Windows\Advapi32\Interop.SECURITY_QUALITY_OF_SERVICE.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
Link="Common\Interop\Windows\Advapi32\Interop.UNICODE_STRING.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.OBJECT_ATTRIBUTES.cs"