Skip to content

Commit

Permalink
Allow to specify file preallocation size (#51111)
Browse files Browse the repository at this point in the history
Co-authored-by: Carlos Sanchez <1175054+carlossanlop@users.noreply.github.com>
Co-authored-by: David Cantú <dacantu@microsoft.com>
  • Loading branch information
3 people committed May 18, 2021
1 parent 29965d2 commit 4e4b8bf
Show file tree
Hide file tree
Showing 48 changed files with 958 additions and 114 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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);
}
}
2 changes: 2 additions & 0 deletions src/libraries/Common/src/Interop/Windows/Interop.Errors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ internal static partial class Errors
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;
Expand All @@ -43,6 +44,7 @@ internal static partial class Errors
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,19 @@ internal unsafe struct OBJECT_ATTRIBUTES
/// 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;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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
}
}
145 changes: 135 additions & 10 deletions src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtCreateFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,47 @@
// 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))
{
Expand All @@ -48,14 +56,15 @@ internal static partial class NtDll
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,
Expand All @@ -67,6 +76,122 @@ internal static partial class NtDll
}
}

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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ internal struct IO_STATUS_BLOCK
}

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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@
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"
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/Native/Unix/Common/pal_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#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
Expand Down
1 change: 1 addition & 0 deletions src/libraries/Native/Unix/System.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_FTruncate)
DllImportEntry(SystemNative_Poll)
DllImportEntry(SystemNative_PosixFAdvise)
DllImportEntry(SystemNative_PosixFAllocate)
DllImportEntry(SystemNative_Read)
DllImportEntry(SystemNative_ReadLink)
DllImportEntry(SystemNative_Rename)
Expand Down
Loading

0 comments on commit 4e4b8bf

Please sign in to comment.