DoxigAlpha

DeleteFile

Function parameters

Parameters

#
sub_path_w:[]const u16

Type definitions in this namespace

Types

#
GetFinalPathNameByHandleFormat
Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
TransferType
https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/buffer-descriptions-for-i-o-control-codes
PEB
Process Environment Block
PEB_LDR_DATA
The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process.
LDR_DATA_TABLE_ENTRY
Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
PF
Processor feature enumeration.
KUSER_SHARED_DATA
Shared Kernel User Data

Functions in this namespace

Functions

#
CreatePipe
A Zig wrapper around `NtCreateNamedPipeFile` and `NtCreateFile` syscalls.
DeviceIoControl
A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
RtlGenRandom
Call RtlGenRandom() instead of CryptGetRandom() on Windows
ReadFile
If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into
GetCurrentDirectory
The result is a slice of `buffer`, indexed from 0.
CreateSymbolicLink
Needs either:
SetFilePointerEx_BEGIN
The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_BEGIN`.
SetFilePointerEx_CURRENT
The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_CURRENT`.
SetFilePointerEx_END
The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_END`.
SetFilePointerEx_CURRENT_get
The SetFilePointerEx function with parameters to get the current offset.
GetFinalPathNameByHandle
Returns canonical (normalized) path of handle.
WSASocketW
Microsoft requires WSAStartup to be called to initialize, or else
fromSysTime
A file time is a 64-bit value that represents the number of 100-nanosecond
nanoSecondsToFileTime
Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME.
eqlIgnoreCaseWTF16
Compares two WTF16 strings using the equivalent functionality of
eqlIgnoreCaseWtf8
Compares two WTF-8 strings using the equivalent functionality of
removeDotDirsSanitized
Removes '.' and '..' path components from a "sanitized relative path".
normalizePath
Normalizes a Windows path with the following steps:
cStrToPrefixedFileW
Same as `sliceToPrefixedFileW` but accepts a pointer
sliceToPrefixedFileW
Same as `wToPrefixedFileW` but accepts a WTF-8 encoded path.
wToPrefixedFileW
Converts the `path` to WTF16, null-terminated.
getNamespacePrefix
If `T` is `u16`, then `path` should be encoded as WTF-16LE.
getUnprefixedPathType
Get the path type of a path that is known to not have any namespace prefixes
ntToWin32Namespace
Similar to `RtlNtPathNameToDosPathName` but does not do any heap allocation.
loadWinsockExtensionFunction
Loads a Winsock extension function in runtime specified by a GUID.
unexpectedError
Call this when you made a windows DLL call or something that does SetLastError
unexpectedStatus
Call this when you made a windows NtDll call
CTL_CODE
https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes
FileInformationIterator
Helper for iterating a byte buffer of FILE_*_INFORMATION structures (from
ProcessBaseAddress
Returns the base address of the process loaded into memory.

Error sets in this namespace

Error Sets

#
RemoveDotDirsError
The error type for `removeDotDirsSanitized`

= @as(HANDLE, @ptrFromInt(maxInt(usize)))

Values

#
self_process_handle
= @as(HANDLE, @ptrFromInt(maxInt(usize)))
STD_INPUT_HANDLE
The standard input device.
STD_OUTPUT_HANDLE
The standard output device.
STD_ERROR_HANDLE
The standard error device.
TCHAR
= @compileError("Deprecated: choose between `CHAR` or `WCHAR` directly instead.")
LPTSTR
= @compileError("Deprecated: choose between `LPSTR` or `LPWSTR` directly instead.")
LPCTSTR
= @compileError("Deprecated: choose between `LPCSTR` or `LPCWSTR` directly instead.")
PTSTR
= @compileError("Deprecated: choose between `PSTR` or `PWSTR` directly instead.")
PCTSTR
= @compileError("Deprecated: choose between `PCSTR` or `PCWSTR` directly instead.")
TRUE
= 1
INVALID_HANDLE_VALUE
= @as(HANDLE, @ptrFromInt(maxInt(usize)))
INVALID_FILE_ATTRIBUTES
= @as(DWORD, maxInt(DWORD))
reparse_tag_name_surrogate_bit
"If this bit is set, the file or directory represents another named entity in the system."
FILE_NAME_NORMALIZED
Return the normalized drive name.
FILE_NAME_OPENED
Return the opened file name (not normalized).
VOLUME_NAME_DOS
Return the path with the drive letter.
VOLUME_NAME_GUID
Return the path with a volume GUID path instead of the drive name.
VOLUME_NAME_NONE
Return the path with no drive information.
VOLUME_NAME_NT
Return the path with the volume device path.
PIPE_ACCESS_DUPLEX
= 0x00000003
PIPE_TYPE_BYTE
= 0x00000000
PIPE_TYPE_MESSAGE
= 0x00000004
PIPE_READMODE_BYTE
= 0x00000000
PIPE_WAIT
= 0x00000000
PIPE_NOWAIT
= 0x00000001
GENERIC_READ
= 0x80000000
GENERIC_WRITE
= 0x40000000
GENERIC_EXECUTE
= 0x20000000
GENERIC_ALL
= 0x10000000
FILE_SHARE_DELETE
= 0x00000004
FILE_SHARE_READ
= 0x00000001
FILE_SHARE_WRITE
= 0x00000002
DELETE
= 0x00010000
READ_CONTROL
= 0x00020000
WRITE_DAC
= 0x00040000
WRITE_OWNER
= 0x00080000
SYNCHRONIZE
= 0x00100000
STANDARD_RIGHTS_READ
= READ_CONTROL
STANDARD_RIGHTS_WRITE
= READ_CONTROL
STANDARD_RIGHTS_REQUIRED
= DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER
MAXIMUM_ALLOWED
= 0x02000000
FILE_READ_DATA
= 0x00000001
FILE_WRITE_DATA
= 0x00000002
FILE_ADD_FILE
= 0x00000002
FILE_APPEND_DATA
= 0x00000004
FILE_READ_EA
= 0x00000008
FILE_WRITE_EA
= 0x00000010
FILE_EXECUTE
= 0x00000020
FILE_TRAVERSE
= 0x00000020
FILE_DELETE_CHILD
= 0x00000040
FILE_WRITE_THROUGH
= 0x00000002
FILE_RANDOM_ACCESS
= 0x00000800
FILE_ALL_ACCESS
= STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1ff
FILE_GENERIC_READ
= STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE
FILE_GENERIC_WRITE
= STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE
FILE_GENERIC_EXECUTE
= STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE
MEM_IMAGE
= 0x1000000
MEM_MAPPED
= 0x40000
MEM_PRIVATE
= 0x20000
STARTF_USEHOTKEY
= 0x00000200
STARTF_USEPOSITION
= 0x00000004
STARTF_USESIZE
= 0x00000002
INFINITE
= 4294967295
WAIT_ABANDONED
= 0x00000080
WAIT_ABANDONED_0
= WAIT_ABANDONED + 0
WAIT_OBJECT_0
= 0x00000000
WAIT_TIMEOUT
= 0x00000102
WAIT_FAILED
= 0xFFFFFFFF
HEAP_NO_SERIALIZE
= 0x00000001
MEM_COMMIT
= 0x1000
MEM_RESERVE
= 0x2000
MEM_FREE
= 0x10000
MEM_RESET
= 0x80000
MEM_RESET_UNDO
= 0x1000000
MEM_LARGE_PAGES
= 0x20000000
MEM_PHYSICAL
= 0x400000
MEM_TOP_DOWN
= 0x100000
MEM_WRITE_WATCH
= 0x200000
PAGE_GUARD
= 0x100
MEM_DECOMMIT
= 0x4000
MEM_RELEASE
= 0x8000
FOLDERID_LocalAppData
= GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}")
KF_FLAG_ALIAS_ONLY
= -2147483648
S_OK
= 0
S_FALSE
= 0x00000001
E_NOTIMPL
= @as(c_long, @bitCast(@as(c_ulong, 0x80004001)))
E_NOINTERFACE
= @as(c_long, @bitCast(@as(c_ulong, 0x80004002)))
E_POINTER
= @as(c_long, @bitCast(@as(c_ulong, 0x80004003)))
E_ABORT
= @as(c_long, @bitCast(@as(c_ulong, 0x80004004)))
E_FAIL
= @as(c_long, @bitCast(@as(c_ulong, 0x80004005)))
E_UNEXPECTED
= @as(c_long, @bitCast(@as(c_ulong, 0x8000FFFF)))
E_ACCESSDENIED
= @as(c_long, @bitCast(@as(c_ulong, 0x80070005)))
E_HANDLE
= @as(c_long, @bitCast(@as(c_ulong, 0x80070006)))
E_OUTOFMEMORY
= @as(c_long, @bitCast(@as(c_ulong, 0x8007000E)))
E_INVALIDARG
= @as(c_long, @bitCast(@as(c_ulong, 0x80070057)))
TLS_OUT_OF_INDEXES
= 4294967295
SECTION_ALL_ACCESS
= STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_EXTEND_SIZE
SEC_64K_PAGES
= 0x80000
SEC_FILE
= 0x800000
SEC_IMAGE
= 0x1000000
SEC_RESERVE
= 0x4000000
SEC_COMMIT
= 0x8000000
SEC_IMAGE_NO_EXECUTE
= SEC_IMAGE | SEC_NOCACHE
SEC_NOCACHE
= 0x10000000
SEC_WRITECOMBINE
= 0x40000000
SEC_LARGE_PAGES
= 0x80000000
HKEY_CLASSES_ROOT
= @ptrFromInt(0x80000000)
HKEY_CURRENT_USER
= @ptrFromInt(0x80000001)
HKEY_LOCAL_MACHINE
= @ptrFromInt(0x80000002)
HKEY_USERS
= @ptrFromInt(0x80000003)
HKEY_PERFORMANCE_DATA
= @ptrFromInt(0x80000004)
HKEY_PERFORMANCE_TEXT
= @ptrFromInt(0x80000050)
HKEY_PERFORMANCE_NLSTEXT
= @ptrFromInt(0x80000060)
HKEY_CURRENT_CONFIG
= @ptrFromInt(0x80000005)
HKEY_DYN_DATA
= @ptrFromInt(0x80000006)
HKEY_CURRENT_USER_LOCAL_SETTINGS
= @ptrFromInt(0x80000007)
KEY_ALL_ACCESS
Combines the STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY,
KEY_CREATE_LINK
Reserved for system use.
KEY_CREATE_SUB_KEY
Required to create a subkey of a registry key.
KEY_ENUMERATE_SUB_KEYS
Required to enumerate the subkeys of a registry key.
KEY_EXECUTE
Equivalent to KEY_READ.
KEY_NOTIFY
Required to request change notifications for a registry key or for subkeys of a registry key.
KEY_QUERY_VALUE
Required to query the values of a registry key.
KEY_READ
Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values.
KEY_SET_VALUE
Required to create, delete, or set a registry value.
KEY_WOW64_32KEY
Indicates that an application on 64-bit Windows should operate on the 32-bit registry view.
KEY_WOW64_64KEY
Indicates that an application on 64-bit Windows should operate on the 64-bit registry view.
KEY_WRITE
Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights.
REG_OPTION_OPEN_LINK
Open symbolic link.
RTL_REGISTRY_ABSOLUTE
Path is a full path
RTL_REGISTRY_SERVICES
\Registry\Machine\System\CurrentControlSet\Services
RTL_REGISTRY_CONTROL
\Registry\Machine\System\CurrentControlSet\Control
RTL_REGISTRY_WINDOWS_NT
\Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
RTL_REGISTRY_DEVICEMAP
\Registry\Machine\Hardware\DeviceMap
RTL_REGISTRY_USER
\Registry\User\CurrentUser
RTL_REGISTRY_HANDLE
Low order bits are registry handle
RTL_REGISTRY_OPTIONAL
Indicates the key node is optional
RTL_QUERY_REGISTRY_SUBKEY
Name is a subkey and remainder of table or until next subkey are value
RTL_QUERY_REGISTRY_TOPKEY
Reset current key to original key for this and all following table entries.
RTL_QUERY_REGISTRY_REQUIRED
Fail if no match found for this table entry.
RTL_QUERY_REGISTRY_NOVALUE
Used to mark a table entry that has no value name, just wants a call out, not
RTL_QUERY_REGISTRY_NOEXPAND
Used to suppress the expansion of REG_MULTI_SZ into multiple callouts or
RTL_QUERY_REGISTRY_DIRECT
QueryRoutine field ignored.
RTL_QUERY_REGISTRY_DELETE
Used to delete value keys after they are queried.
RTL_QUERY_REGISTRY_TYPECHECK
Use this flag with the RTL_QUERY_REGISTRY_DIRECT flag to verify that the REG_XXX type
FILE_ACTION_ADDED
= 0x00000001
INIT_ONCE_STATIC_INIT
= RTL_RUN_ONCE_INIT
RTL_RUN_ONCE_INIT
= RTL_RUN_ONCE{ .Ptr = null }
PATH_MAX_WIDE
> The maximum path of 32,767 characters is approximate, because the "\\?\"
NAME_MAX
> [Each file name component can be] up to the value returned in the
OBJ_INHERIT
= 0x00000002
OBJ_PERMANENT
= 0x00000010
OBJ_EXCLUSIVE
= 0x00000020
OBJ_OPENIF
= 0x00000080
OBJ_OPENLINK
= 0x00000100
OBJ_KERNEL_HANDLE
= 0x00000200
IOCTL_MOUNTMGR_QUERY_POINTS
= CTL_CODE(MOUNTMGRCONTROLTYPE, 2, .METHOD_BUFFERED, FILE_ANY_ACCESS)
IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH
= CTL_CODE(MOUNTMGRCONTROLTYPE, 12, .METHOD_BUFFERED, FILE_ANY_ACCESS)
SRWLOCK_INIT
= SRWLOCK{}
CONDITION_VARIABLE_INIT
= CONDITION_VARIABLE{}
SharedUserData
Read-only user-mode address for the shared data.
TH32CS_SNAPPROCESS
= 0x00000002
TH32CS_SNAPTHREAD
= 0x00000004
TH32CS_SNAPMODULE
= 0x00000008
TH32CS_SNAPALL
= TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE
TH32CS_INHERIT
= 0x80000000

Source

Implementation

#
pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
    const create_options_flags: ULONG = if (options.remove_dir)
        FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
    else
        FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?

    const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));
    var nt_name = UNICODE_STRING{
        .Length = path_len_bytes,
        .MaximumLength = path_len_bytes,
        // The Windows API makes this mutable, but it will not mutate here.
        .Buffer = @constCast(sub_path_w.ptr),
    };

    if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
        // Windows does not recognize this, but it does work with empty string.
        nt_name.Length = 0;
    }
    if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
        // Can't remove the parent directory with an open handle.
        return error.FileBusy;
    }

    var attr = OBJECT_ATTRIBUTES{
        .Length = @sizeOf(OBJECT_ATTRIBUTES),
        .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir,
        .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
        .ObjectName = &nt_name,
        .SecurityDescriptor = null,
        .SecurityQualityOfService = null,
    };
    var io: IO_STATUS_BLOCK = undefined;
    var tmp_handle: HANDLE = undefined;
    var rc = ntdll.NtCreateFile(
        &tmp_handle,
        SYNCHRONIZE | DELETE,
        &attr,
        &io,
        null,
        0,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        FILE_OPEN,
        create_options_flags,
        null,
        0,
    );
    switch (rc) {
        .SUCCESS => {},
        .OBJECT_NAME_INVALID => unreachable,
        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
        .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
        .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
        .INVALID_PARAMETER => unreachable,
        .FILE_IS_A_DIRECTORY => return error.IsDir,
        .NOT_A_DIRECTORY => return error.NotDir,
        .SHARING_VIOLATION => return error.FileBusy,
        .ACCESS_DENIED => return error.AccessDenied,
        .DELETE_PENDING => return,
        else => return unexpectedStatus(rc),
    }
    defer CloseHandle(tmp_handle);

    // FileDispositionInformationEx (and therefore FILE_DISPOSITION_POSIX_SEMANTICS and FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE)
    // are only supported on NTFS filesystems, so the version check on its own is only a partial solution. To support non-NTFS filesystems
    // like FAT32, we need to fallback to FileDispositionInformation if the usage of FileDispositionInformationEx gives
    // us INVALID_PARAMETER.
    // The same reasoning for win10_rs5 as in os.renameatW() applies (FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5).
    var need_fallback = true;
    if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs5)) {
        // Deletion with posix semantics if the filesystem supports it.
        var info = FILE_DISPOSITION_INFORMATION_EX{
            .Flags = FILE_DISPOSITION_DELETE |
                FILE_DISPOSITION_POSIX_SEMANTICS |
                FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
        };

        rc = ntdll.NtSetInformationFile(
            tmp_handle,
            &io,
            &info,
            @sizeOf(FILE_DISPOSITION_INFORMATION_EX),
            .FileDispositionInformationEx,
        );
        switch (rc) {
            .SUCCESS => return,
            // INVALID_PARAMETER here means that the filesystem does not support FileDispositionInformationEx
            .INVALID_PARAMETER => {},
            // For all other statuses, fall down to the switch below to handle them.
            else => need_fallback = false,
        }
    }
    if (need_fallback) {
        // Deletion with file pending semantics, which requires waiting or moving
        // files to get them removed (from here).
        var file_dispo = FILE_DISPOSITION_INFORMATION{
            .DeleteFile = TRUE,
        };

        rc = ntdll.NtSetInformationFile(
            tmp_handle,
            &io,
            &file_dispo,
            @sizeOf(FILE_DISPOSITION_INFORMATION),
            .FileDispositionInformation,
        );
    }
    switch (rc) {
        .SUCCESS => {},
        .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
        .INVALID_PARAMETER => unreachable,
        .CANNOT_DELETE => return error.AccessDenied,
        .MEDIA_WRITE_PROTECTED => return error.AccessDenied,
        .ACCESS_DENIED => return error.AccessDenied,
        else => return unexpectedStatus(rc),
    }
}