2019-05-24 16:36:09 -07:00
|
|
|
// This file contains thin wrappers around Windows-specific APIs, with these
|
|
|
|
// specific goals in mind:
|
|
|
|
// * Convert "errno"-style error codes into Zig errors.
|
|
|
|
// * When null-terminated or UTF16LE byte buffers are required, provide APIs which accept
|
|
|
|
// slices as well as APIs which accept null-terminated UTF16LE byte buffers.
|
|
|
|
|
2019-05-26 10:17:34 -07:00
|
|
|
const builtin = @import("builtin");
|
2019-03-02 13:46:04 -08:00
|
|
|
const std = @import("../std.zig");
|
2019-05-26 21:48:56 -07:00
|
|
|
const mem = std.mem;
|
2018-07-16 21:01:36 -07:00
|
|
|
const assert = std.debug.assert;
|
2019-05-26 21:48:56 -07:00
|
|
|
const math = std.math;
|
2018-10-26 11:59:58 -07:00
|
|
|
const maxInt = std.math.maxInt;
|
2018-07-20 20:37:37 -07:00
|
|
|
|
2019-05-23 21:13:13 -07:00
|
|
|
pub const advapi32 = @import("windows/advapi32.zig");
|
|
|
|
pub const kernel32 = @import("windows/kernel32.zig");
|
|
|
|
pub const ntdll = @import("windows/ntdll.zig");
|
|
|
|
pub const ole32 = @import("windows/ole32.zig");
|
2020-01-31 14:48:08 -08:00
|
|
|
pub const psapi = @import("windows/psapi.zig");
|
2019-05-23 21:13:13 -07:00
|
|
|
pub const shell32 = @import("windows/shell32.zig");
|
2019-11-12 22:30:26 -08:00
|
|
|
pub const ws2_32 = @import("windows/ws2_32.zig");
|
2018-06-11 22:55:08 -07:00
|
|
|
|
2019-05-29 15:55:42 -07:00
|
|
|
pub usingnamespace @import("windows/bits.zig");
|
2019-05-23 21:13:13 -07:00
|
|
|
|
2019-10-21 16:17:33 -07:00
|
|
|
pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
|
|
|
|
|
2019-05-24 15:27:18 -07:00
|
|
|
pub const CreateFileError = error{
|
2019-05-23 21:13:13 -07:00
|
|
|
SharingViolation,
|
|
|
|
PathAlreadyExists,
|
|
|
|
|
|
|
|
/// When any of the path components can not be found or the file component can not
|
|
|
|
/// be found. Some operating systems distinguish between path components not found and
|
|
|
|
/// file components not found, but they are collapsed into FileNotFound to gain
|
|
|
|
/// consistency across operating systems.
|
|
|
|
FileNotFound,
|
|
|
|
|
|
|
|
AccessDenied,
|
|
|
|
PipeBusy,
|
|
|
|
NameTooLong,
|
|
|
|
|
|
|
|
/// On Windows, file paths must be valid Unicode.
|
|
|
|
InvalidUtf8,
|
|
|
|
|
|
|
|
/// On Windows, file paths cannot contain these characters:
|
2019-08-31 21:30:35 -07:00
|
|
|
/// '/', '*', '?', '"', '<', '>', '|'
|
2019-05-23 21:13:13 -07:00
|
|
|
BadPathName,
|
|
|
|
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn CreateFile(
|
|
|
|
file_path: []const u8,
|
|
|
|
desired_access: DWORD,
|
|
|
|
share_mode: DWORD,
|
2019-05-27 12:34:46 -07:00
|
|
|
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
|
2019-05-23 21:13:13 -07:00
|
|
|
creation_disposition: DWORD,
|
|
|
|
flags_and_attrs: DWORD,
|
2019-05-27 12:34:46 -07:00
|
|
|
hTemplateFile: ?HANDLE,
|
2019-05-24 16:36:09 -07:00
|
|
|
) CreateFileError!HANDLE {
|
2019-05-23 21:13:13 -07:00
|
|
|
const file_path_w = try sliceToPrefixedFileW(file_path);
|
2019-05-27 12:34:46 -07:00
|
|
|
return CreateFileW(&file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
|
2019-05-23 21:13:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn CreateFileW(
|
2019-12-27 21:40:59 -08:00
|
|
|
file_path_w: [*:0]const u16,
|
2019-05-23 21:13:13 -07:00
|
|
|
desired_access: DWORD,
|
|
|
|
share_mode: DWORD,
|
2019-05-27 12:34:46 -07:00
|
|
|
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
|
2019-05-23 21:13:13 -07:00
|
|
|
creation_disposition: DWORD,
|
|
|
|
flags_and_attrs: DWORD,
|
2019-05-27 12:34:46 -07:00
|
|
|
hTemplateFile: ?HANDLE,
|
2019-05-24 15:27:18 -07:00
|
|
|
) CreateFileError!HANDLE {
|
2019-05-27 12:34:46 -07:00
|
|
|
const result = kernel32.CreateFileW(file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
|
2019-05-23 21:13:13 -07:00
|
|
|
|
|
|
|
if (result == INVALID_HANDLE_VALUE) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.SHARING_VIOLATION => return error.SharingViolation,
|
|
|
|
.ALREADY_EXISTS => return error.PathAlreadyExists,
|
|
|
|
.FILE_EXISTS => return error.PathAlreadyExists,
|
|
|
|
.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.ACCESS_DENIED => return error.AccessDenied,
|
|
|
|
.PIPE_BUSY => return error.PipeBusy,
|
|
|
|
.FILENAME_EXCED_RANGE => return error.NameTooLong,
|
2019-05-25 10:07:44 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
2019-05-23 21:13:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
zig build system: correctly handle multiple output artifacts
Previously the zig build system incorrectly assumed that the only build
artifact was a binary. Now, when you enable the cache, only the output
dir is printed to stdout, and the zig build system iterates over the
files in that directory, copying them to the output directory.
To support this change:
* Add `std.os.renameat`, `std.os.renameatZ`, and `std.os.renameatW`.
* Fix `std.os.linux.renameat` not compiling due to typos.
* Deprecate `std.fs.updateFile` and `std.fs.updateFileMode`.
* Add `std.fs.Dir.updateFile`, which supports using open directory
handles for both the source and destination paths, as well as an
options parameter which allows overriding the mode.
* Update `std.fs.AtomicFile` to support operating based on an open
directory handle. Instead of `std.fs.AtomicFile.init`, use
`std.fs.Dir.atomicFile`.
* `std.fs.AtomicFile` deinit() better handles the situation when the
rename fails but the temporary file still exists, by still
attempting to remove the temporary file.
* `std.fs.Dir.openFileWindows` is moved to `std.os.windows.OpenFileW`.
* `std.os.RenameError` gains the error codes `NoDevice`,
`SharingViolation`, and `PipeBusy` which have been observed from
Windows.
Closes #4733
2020-03-13 18:06:07 -07:00
|
|
|
pub const OpenError = error{
|
|
|
|
IsDir,
|
|
|
|
FileNotFound,
|
|
|
|
NoDevice,
|
|
|
|
SharingViolation,
|
|
|
|
AccessDenied,
|
|
|
|
PipeBusy,
|
|
|
|
PathAlreadyExists,
|
|
|
|
Unexpected,
|
|
|
|
NameTooLong,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// TODO rename to CreateFileW
|
|
|
|
/// TODO actually we don't need the path parameter to be null terminated
|
|
|
|
pub fn OpenFileW(
|
|
|
|
dir: ?HANDLE,
|
|
|
|
sub_path_w: [*:0]const u16,
|
|
|
|
sa: ?*SECURITY_ATTRIBUTES,
|
|
|
|
access_mask: ACCESS_MASK,
|
|
|
|
creation: ULONG,
|
|
|
|
) OpenError!HANDLE {
|
|
|
|
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
|
|
|
return error.IsDir;
|
|
|
|
}
|
|
|
|
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
|
|
|
|
return error.IsDir;
|
|
|
|
}
|
|
|
|
|
|
|
|
var result: HANDLE = undefined;
|
|
|
|
|
|
|
|
const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) {
|
|
|
|
error.Overflow => return error.NameTooLong,
|
|
|
|
};
|
|
|
|
var nt_name = UNICODE_STRING{
|
|
|
|
.Length = path_len_bytes,
|
|
|
|
.MaximumLength = path_len_bytes,
|
|
|
|
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
|
|
|
|
};
|
|
|
|
var attr = OBJECT_ATTRIBUTES{
|
|
|
|
.Length = @sizeOf(OBJECT_ATTRIBUTES),
|
|
|
|
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
|
|
|
|
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
|
|
|
.ObjectName = &nt_name,
|
|
|
|
.SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null,
|
|
|
|
.SecurityQualityOfService = null,
|
|
|
|
};
|
|
|
|
var io: IO_STATUS_BLOCK = undefined;
|
|
|
|
const rc = ntdll.NtCreateFile(
|
|
|
|
&result,
|
|
|
|
access_mask,
|
|
|
|
&attr,
|
|
|
|
&io,
|
|
|
|
null,
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
|
|
creation,
|
|
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
|
|
null,
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
switch (rc) {
|
|
|
|
.SUCCESS => return result,
|
|
|
|
.OBJECT_NAME_INVALID => unreachable,
|
|
|
|
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
|
|
|
|
.INVALID_PARAMETER => unreachable,
|
|
|
|
.SHARING_VIOLATION => return error.SharingViolation,
|
|
|
|
.ACCESS_DENIED => return error.AccessDenied,
|
|
|
|
.PIPE_BUSY => return error.PipeBusy,
|
|
|
|
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
|
|
|
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
|
|
|
else => return unexpectedStatus(rc),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 21:13:13 -07:00
|
|
|
pub const CreatePipeError = error{Unexpected};
|
|
|
|
|
2019-05-26 23:00:39 -07:00
|
|
|
pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void {
|
2019-05-23 21:13:13 -07:00
|
|
|
if (kernel32.CreatePipe(rd, wr, sattr, 0) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-18 21:38:23 -08:00
|
|
|
pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE {
|
|
|
|
const nameW = try sliceToPrefixedFileW(name);
|
|
|
|
return CreateEventExW(attributes, &nameW, flags, desired_access);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16, flags: DWORD, desired_access: DWORD) !HANDLE {
|
|
|
|
const handle = kernel32.CreateEventExW(attributes, nameW, flags, desired_access);
|
|
|
|
if (handle) |h| {
|
|
|
|
return h;
|
|
|
|
} else {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-12 23:22:33 -08:00
|
|
|
pub fn DeviceIoControl(
|
|
|
|
h: HANDLE,
|
|
|
|
ioControlCode: DWORD,
|
|
|
|
in: ?[]const u8,
|
|
|
|
out: ?[]u8,
|
|
|
|
overlapped: ?*OVERLAPPED,
|
|
|
|
) !DWORD {
|
|
|
|
var bytes: DWORD = undefined;
|
|
|
|
if (kernel32.DeviceIoControl(
|
|
|
|
h,
|
|
|
|
ioControlCode,
|
|
|
|
if (in) |i| i.ptr else null,
|
|
|
|
if (in) |i| @intCast(u32, i.len) else 0,
|
|
|
|
if (out) |o| o.ptr else null,
|
|
|
|
if (out) |o| @intCast(u32, o.len) else 0,
|
|
|
|
&bytes,
|
|
|
|
overlapped,
|
|
|
|
) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.IO_PENDING => if (overlapped == null) unreachable,
|
2019-11-12 23:22:33 -08:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
|
|
|
|
var bytes: DWORD = undefined;
|
2019-11-18 21:40:24 -08:00
|
|
|
if (kernel32.GetOverlappedResult(h, overlapped, &bytes, @boolToInt(wait)) == 0) {
|
2019-11-12 23:22:33 -08:00
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.IO_INCOMPLETE => if (!wait) return error.WouldBlock else unreachable,
|
2019-11-12 23:22:33 -08:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
2019-05-23 21:13:13 -07:00
|
|
|
pub const SetHandleInformationError = error{Unexpected};
|
|
|
|
|
2019-05-26 23:00:39 -07:00
|
|
|
pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void {
|
|
|
|
if (kernel32.SetHandleInformation(h, mask, flags) == 0) {
|
2019-05-23 21:13:13 -07:00
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-24 15:27:18 -07:00
|
|
|
pub const RtlGenRandomError = error{Unexpected};
|
|
|
|
|
|
|
|
/// Call RtlGenRandom() instead of CryptGetRandom() on Windows
|
|
|
|
/// https://github.com/rust-lang-nursery/rand/issues/111
|
|
|
|
/// https://bugzilla.mozilla.org/show_bug.cgi?id=504270
|
|
|
|
pub fn RtlGenRandom(output: []u8) RtlGenRandomError!void {
|
2019-08-06 11:32:22 -07:00
|
|
|
var total_read: usize = 0;
|
|
|
|
var buff: []u8 = output[0..];
|
2019-08-07 14:19:25 -07:00
|
|
|
const max_read_size: ULONG = maxInt(ULONG);
|
2019-08-06 11:32:22 -07:00
|
|
|
|
|
|
|
while (total_read < output.len) {
|
2019-08-07 14:19:25 -07:00
|
|
|
const to_read: ULONG = math.min(buff.len, max_read_size);
|
2019-08-06 11:32:22 -07:00
|
|
|
|
|
|
|
if (advapi32.RtlGenRandom(buff.ptr, to_read) == 0) {
|
|
|
|
return unexpectedError(kernel32.GetLastError());
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
2019-08-06 11:32:22 -07:00
|
|
|
|
2019-08-07 14:19:25 -07:00
|
|
|
total_read += to_read;
|
2019-08-06 11:32:22 -07:00
|
|
|
buff = buff[to_read..];
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const WaitForSingleObjectError = error{
|
|
|
|
WaitAbandoned,
|
|
|
|
WaitTimeOut,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
2020-01-11 10:50:28 -08:00
|
|
|
pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void {
|
|
|
|
return WaitForSingleObjectEx(handle, milliseconds, false);
|
|
|
|
}
|
|
|
|
|
2019-11-18 21:31:24 -08:00
|
|
|
pub fn WaitForSingleObjectEx(handle: HANDLE, milliseconds: DWORD, alertable: bool) WaitForSingleObjectError!void {
|
|
|
|
switch (kernel32.WaitForSingleObjectEx(handle, milliseconds, @boolToInt(alertable))) {
|
2019-05-24 15:27:18 -07:00
|
|
|
WAIT_ABANDONED => return error.WaitAbandoned,
|
|
|
|
WAIT_OBJECT_0 => return,
|
|
|
|
WAIT_TIMEOUT => return error.WaitTimeOut,
|
|
|
|
WAIT_FAILED => switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
},
|
|
|
|
else => return error.Unexpected,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-18 21:37:29 -08:00
|
|
|
pub fn WaitForMultipleObjectsEx(handles: []const HANDLE, waitAll: bool, milliseconds: DWORD, alertable: bool) !u32 {
|
|
|
|
assert(handles.len < MAXIMUM_WAIT_OBJECTS);
|
|
|
|
const nCount: DWORD = @intCast(DWORD, handles.len);
|
|
|
|
switch (kernel32.WaitForMultipleObjectsEx(
|
|
|
|
nCount,
|
|
|
|
handles.ptr,
|
|
|
|
@boolToInt(waitAll),
|
|
|
|
milliseconds,
|
|
|
|
@boolToInt(alertable),
|
|
|
|
)) {
|
|
|
|
WAIT_OBJECT_0...WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS => |n| {
|
|
|
|
const handle_index = n - WAIT_OBJECT_0;
|
|
|
|
assert(handle_index < nCount);
|
|
|
|
return handle_index;
|
|
|
|
},
|
|
|
|
WAIT_ABANDONED_0...WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS => |n| {
|
|
|
|
const handle_index = n - WAIT_ABANDONED_0;
|
|
|
|
assert(handle_index < nCount);
|
|
|
|
return error.WaitAbandoned;
|
|
|
|
},
|
|
|
|
WAIT_TIMEOUT => return error.WaitTimeOut,
|
|
|
|
WAIT_FAILED => switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
2019-05-24 15:27:18 -07:00
|
|
|
},
|
|
|
|
else => return error.Unexpected,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const FindFirstFileError = error{
|
|
|
|
FileNotFound,
|
2019-05-26 22:35:58 -07:00
|
|
|
InvalidUtf8,
|
|
|
|
BadPathName,
|
|
|
|
NameTooLong,
|
2019-05-24 15:27:18 -07:00
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn FindFirstFile(dir_path: []const u8, find_file_data: *WIN32_FIND_DATAW) FindFirstFileError!HANDLE {
|
2019-11-25 10:51:09 -08:00
|
|
|
const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, [_]u16{ '\\', '*' });
|
2019-05-24 15:27:18 -07:00
|
|
|
const handle = kernel32.FindFirstFileW(&dir_path_w, find_file_data);
|
|
|
|
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.PATH_NOT_FOUND => return error.FileNotFound,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const FindNextFileError = error{Unexpected};
|
|
|
|
|
|
|
|
/// Returns `true` if there was another file, `false` otherwise.
|
|
|
|
pub fn FindNextFile(handle: HANDLE, find_file_data: *WIN32_FIND_DATAW) FindNextFileError!bool {
|
|
|
|
if (kernel32.FindNextFileW(handle, find_file_data) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.NO_MORE_FILES => return false,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const CreateIoCompletionPortError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn CreateIoCompletionPort(
|
|
|
|
file_handle: HANDLE,
|
|
|
|
existing_completion_port: ?HANDLE,
|
|
|
|
completion_key: usize,
|
|
|
|
concurrent_thread_count: DWORD,
|
|
|
|
) CreateIoCompletionPortError!HANDLE {
|
|
|
|
const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.INVALID_PARAMETER => unreachable,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const PostQueuedCompletionStatusError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn PostQueuedCompletionStatus(
|
|
|
|
completion_port: HANDLE,
|
|
|
|
bytes_transferred_count: DWORD,
|
|
|
|
completion_key: usize,
|
|
|
|
lpOverlapped: ?*OVERLAPPED,
|
|
|
|
) PostQueuedCompletionStatusError!void {
|
|
|
|
if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2019-05-26 22:35:58 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetQueuedCompletionStatusResult = enum {
|
|
|
|
Normal,
|
|
|
|
Aborted,
|
|
|
|
Cancelled,
|
|
|
|
EOF,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn GetQueuedCompletionStatus(
|
|
|
|
completion_port: HANDLE,
|
|
|
|
bytes_transferred_count: *DWORD,
|
|
|
|
lpCompletionKey: *usize,
|
|
|
|
lpOverlapped: *?*OVERLAPPED,
|
|
|
|
dwMilliseconds: DWORD,
|
|
|
|
) GetQueuedCompletionStatusResult {
|
|
|
|
if (kernel32.GetQueuedCompletionStatus(
|
|
|
|
completion_port,
|
|
|
|
bytes_transferred_count,
|
|
|
|
lpCompletionKey,
|
|
|
|
lpOverlapped,
|
|
|
|
dwMilliseconds,
|
|
|
|
) == FALSE) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
|
|
|
|
.OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Cancelled,
|
|
|
|
.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| {
|
|
|
|
if (std.debug.runtime_safety) {
|
2019-12-08 20:46:50 -08:00
|
|
|
std.debug.panic("unexpected error: {}\n", .{err});
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return GetQueuedCompletionStatusResult.Normal;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn CloseHandle(hObject: HANDLE) void {
|
2020-03-03 12:01:08 -08:00
|
|
|
assert(ntdll.NtClose(hObject) == .SUCCESS);
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
|
2019-05-24 19:52:07 -07:00
|
|
|
pub fn FindClose(hFindFile: HANDLE) void {
|
|
|
|
assert(kernel32.FindClose(hFindFile) != 0);
|
|
|
|
}
|
|
|
|
|
2020-02-06 14:56:40 -08:00
|
|
|
pub const ReadFileError = error{
|
|
|
|
OperationAborted,
|
|
|
|
BrokenPipe,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into
|
|
|
|
/// multiple non-atomic reads.
|
|
|
|
pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usize {
|
|
|
|
if (std.event.Loop.instance) |loop| {
|
|
|
|
// TODO support async ReadFile with no offset
|
|
|
|
const off = offset.?;
|
|
|
|
var resume_node = std.event.Loop.ResumeNode.Basic{
|
|
|
|
.base = .{
|
|
|
|
.id = .Basic,
|
|
|
|
.handle = @frame(),
|
|
|
|
.overlapped = OVERLAPPED{
|
|
|
|
.Internal = 0,
|
|
|
|
.InternalHigh = 0,
|
|
|
|
.Offset = @truncate(u32, off),
|
|
|
|
.OffsetHigh = @truncate(u32, off >> 32),
|
|
|
|
.hEvent = null,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
// TODO only call create io completion port once per fd
|
|
|
|
_ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined) catch undefined;
|
|
|
|
loop.beginOneEvent();
|
|
|
|
suspend {
|
|
|
|
// TODO handle buffer bigger than DWORD can hold
|
|
|
|
_ = windows.kernel32.ReadFile(fd, buffer.ptr, @intCast(windows.DWORD, buffer.len), null, &resume_node.base.overlapped);
|
|
|
|
}
|
|
|
|
var bytes_transferred: windows.DWORD = undefined;
|
|
|
|
if (windows.kernel32.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {
|
|
|
|
switch (windows.kernel32.GetLastError()) {
|
|
|
|
.IO_PENDING => unreachable,
|
|
|
|
.OPERATION_ABORTED => return error.OperationAborted,
|
|
|
|
.BROKEN_PIPE => return error.BrokenPipe,
|
|
|
|
.HANDLE_EOF => return @as(usize, bytes_transferred),
|
|
|
|
else => |err| return windows.unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return @as(usize, bytes_transferred);
|
|
|
|
} else {
|
|
|
|
var index: usize = 0;
|
|
|
|
while (index < buffer.len) {
|
|
|
|
const want_read_count = @intCast(DWORD, math.min(@as(DWORD, maxInt(DWORD)), buffer.len - index));
|
|
|
|
var amt_read: DWORD = undefined;
|
|
|
|
var overlapped_data: OVERLAPPED = undefined;
|
|
|
|
const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
|
|
|
|
overlapped_data = .{
|
|
|
|
.Internal = 0,
|
|
|
|
.InternalHigh = 0,
|
|
|
|
.Offset = @truncate(u32, off + index),
|
|
|
|
.OffsetHigh = @truncate(u32, (off + index) >> 32),
|
|
|
|
.hEvent = null,
|
|
|
|
};
|
|
|
|
break :blk &overlapped_data;
|
|
|
|
} else null;
|
|
|
|
if (kernel32.ReadFile(in_hFile, buffer.ptr + index, want_read_count, &amt_read, overlapped) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
.OPERATION_ABORTED => continue,
|
|
|
|
.BROKEN_PIPE => return index,
|
2020-03-11 14:39:53 -07:00
|
|
|
.HANDLE_EOF => return index,
|
2020-02-06 14:56:40 -08:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
2020-02-06 14:56:40 -08:00
|
|
|
if (amt_read == 0) return index;
|
|
|
|
index += amt_read;
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
2020-02-06 14:56:40 -08:00
|
|
|
return index;
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const WriteFileError = error{
|
|
|
|
SystemResources,
|
|
|
|
OperationAborted,
|
|
|
|
BrokenPipe,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
2020-03-02 23:03:22 -08:00
|
|
|
pub fn WriteFile(handle: HANDLE, bytes: []const u8, offset: ?u64) WriteFileError!usize {
|
2020-02-06 14:56:40 -08:00
|
|
|
if (std.event.Loop.instance) |loop| {
|
|
|
|
// TODO support async WriteFile with no offset
|
|
|
|
const off = offset.?;
|
|
|
|
var resume_node = std.event.Loop.ResumeNode.Basic{
|
|
|
|
.base = .{
|
|
|
|
.id = .Basic,
|
|
|
|
.handle = @frame(),
|
|
|
|
.overlapped = OVERLAPPED{
|
|
|
|
.Internal = 0,
|
|
|
|
.InternalHigh = 0,
|
|
|
|
.Offset = @truncate(u32, off),
|
|
|
|
.OffsetHigh = @truncate(u32, off >> 32),
|
|
|
|
.hEvent = null,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
// TODO only call create io completion port once per fd
|
|
|
|
_ = CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined);
|
|
|
|
loop.beginOneEvent();
|
|
|
|
suspend {
|
2020-03-02 23:03:22 -08:00
|
|
|
const adjusted_len = math.cast(windows.DWORD, bytes.len) catch maxInt(windows.DWORD);
|
|
|
|
_ = kernel32.WriteFile(fd, bytes.ptr, adjusted_len, null, &resume_node.base.overlapped);
|
2020-02-06 14:56:40 -08:00
|
|
|
}
|
|
|
|
var bytes_transferred: windows.DWORD = undefined;
|
|
|
|
if (kernel32.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, FALSE) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
.IO_PENDING => unreachable,
|
|
|
|
.INVALID_USER_BUFFER => return error.SystemResources,
|
|
|
|
.NOT_ENOUGH_MEMORY => return error.SystemResources,
|
|
|
|
.OPERATION_ABORTED => return error.OperationAborted,
|
|
|
|
.NOT_ENOUGH_QUOTA => return error.SystemResources,
|
|
|
|
.BROKEN_PIPE => return error.BrokenPipe,
|
|
|
|
else => |err| return windows.unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
2020-03-02 23:03:22 -08:00
|
|
|
return bytes_transferred;
|
2020-02-06 14:56:40 -08:00
|
|
|
} else {
|
|
|
|
var bytes_written: DWORD = undefined;
|
|
|
|
var overlapped_data: OVERLAPPED = undefined;
|
|
|
|
const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
|
|
|
|
overlapped_data = .{
|
|
|
|
.Internal = 0,
|
|
|
|
.InternalHigh = 0,
|
|
|
|
.Offset = @truncate(u32, off),
|
|
|
|
.OffsetHigh = @truncate(u32, off >> 32),
|
|
|
|
.hEvent = null,
|
|
|
|
};
|
|
|
|
break :blk &overlapped_data;
|
|
|
|
} else null;
|
2020-03-02 23:03:22 -08:00
|
|
|
const adjusted_len = math.cast(u32, bytes.len) catch maxInt(u32);
|
|
|
|
if (kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, overlapped) == 0) {
|
2020-02-06 14:56:40 -08:00
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
.INVALID_USER_BUFFER => return error.SystemResources,
|
|
|
|
.NOT_ENOUGH_MEMORY => return error.SystemResources,
|
|
|
|
.OPERATION_ABORTED => return error.OperationAborted,
|
|
|
|
.NOT_ENOUGH_QUOTA => return error.SystemResources,
|
2020-03-02 23:03:22 -08:00
|
|
|
.IO_PENDING => unreachable,
|
2020-02-06 14:56:40 -08:00
|
|
|
.BROKEN_PIPE => return error.BrokenPipe,
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
2020-03-02 23:03:22 -08:00
|
|
|
return bytes_written;
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetCurrentDirectoryError = error{
|
|
|
|
NameTooLong,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
2019-05-26 21:48:56 -07:00
|
|
|
/// The result is a slice of `buffer`, indexed from 0.
|
2019-05-24 15:27:18 -07:00
|
|
|
pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
|
|
|
|
var utf16le_buf: [PATH_MAX_WIDE]u16 = undefined;
|
|
|
|
const result = kernel32.GetCurrentDirectoryW(utf16le_buf.len, &utf16le_buf);
|
|
|
|
if (result == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(result <= utf16le_buf.len);
|
|
|
|
const utf16le_slice = utf16le_buf[0..result];
|
|
|
|
// Trust that Windows gives us valid UTF-16LE.
|
|
|
|
var end_index: usize = 0;
|
2019-05-26 21:48:56 -07:00
|
|
|
var it = std.unicode.Utf16LeIterator.init(utf16le_slice);
|
2019-05-24 15:27:18 -07:00
|
|
|
while (it.nextCodepoint() catch unreachable) |codepoint| {
|
2019-05-26 21:48:56 -07:00
|
|
|
const seq_len = std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
|
|
|
|
if (end_index + seq_len >= buffer.len)
|
2019-05-24 15:27:18 -07:00
|
|
|
return error.NameTooLong;
|
2019-05-26 21:48:56 -07:00
|
|
|
end_index += std.unicode.utf8Encode(codepoint, buffer[end_index..]) catch unreachable;
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
2019-05-26 21:48:56 -07:00
|
|
|
return buffer[0..end_index];
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub const CreateSymbolicLinkError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn CreateSymbolicLink(
|
|
|
|
sym_link_path: []const u8,
|
|
|
|
target_path: []const u8,
|
|
|
|
flags: DWORD,
|
|
|
|
) CreateSymbolicLinkError!void {
|
|
|
|
const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path);
|
|
|
|
const target_path_w = try sliceToPrefixedFileW(target_path);
|
|
|
|
return CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn CreateSymbolicLinkW(
|
2019-12-27 21:40:59 -08:00
|
|
|
sym_link_path: [*:0]const u16,
|
|
|
|
target_path: [*:0]const u16,
|
2019-05-24 15:27:18 -07:00
|
|
|
flags: DWORD,
|
|
|
|
) CreateSymbolicLinkError!void {
|
|
|
|
if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2019-05-26 23:00:39 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const DeleteFileError = error{
|
|
|
|
FileNotFound,
|
|
|
|
AccessDenied,
|
|
|
|
NameTooLong,
|
2019-06-26 11:32:19 -07:00
|
|
|
FileBusy,
|
2019-05-24 15:27:18 -07:00
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn DeleteFile(filename: []const u8) DeleteFileError!void {
|
|
|
|
const filename_w = try sliceToPrefixedFileW(filename);
|
|
|
|
return DeleteFileW(&filename_w);
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:40:59 -08:00
|
|
|
pub fn DeleteFileW(filename: [*:0]const u16) DeleteFileError!void {
|
2019-05-26 22:35:58 -07:00
|
|
|
if (kernel32.DeleteFileW(filename) == 0) {
|
2019-05-24 15:27:18 -07:00
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.ACCESS_DENIED => return error.AccessDenied,
|
|
|
|
.FILENAME_EXCED_RANGE => return error.NameTooLong,
|
|
|
|
.INVALID_PARAMETER => return error.NameTooLong,
|
|
|
|
.SHARING_VIOLATION => return error.FileBusy,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const MoveFileError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
|
|
|
|
const old_path_w = try sliceToPrefixedFileW(old_path);
|
|
|
|
const new_path_w = try sliceToPrefixedFileW(new_path);
|
|
|
|
return MoveFileExW(&old_path_w, &new_path_w, flags);
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:40:59 -08:00
|
|
|
pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void {
|
2019-05-24 15:27:18 -07:00
|
|
|
if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const CreateDirectoryError = error{
|
2020-03-03 12:01:08 -08:00
|
|
|
NameTooLong,
|
2019-05-24 15:27:18 -07:00
|
|
|
PathAlreadyExists,
|
|
|
|
FileNotFound,
|
2020-03-03 12:01:08 -08:00
|
|
|
NoDevice,
|
|
|
|
AccessDenied,
|
2020-03-07 16:13:05 -08:00
|
|
|
InvalidUtf8,
|
|
|
|
BadPathName,
|
2019-05-24 15:27:18 -07:00
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
2020-03-03 12:01:08 -08:00
|
|
|
/// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`.
|
|
|
|
pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE {
|
2019-05-24 15:27:18 -07:00
|
|
|
const pathname_w = try sliceToPrefixedFileW(pathname);
|
2020-03-03 12:01:08 -08:00
|
|
|
return CreateDirectoryW(dir, &pathname_w, sa);
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
|
2020-03-03 12:01:08 -08:00
|
|
|
/// Same as `CreateDirectory` except takes a WTF-16 encoded path.
|
|
|
|
pub fn CreateDirectoryW(
|
|
|
|
dir: ?HANDLE,
|
|
|
|
sub_path_w: [*:0]const u16,
|
|
|
|
sa: ?*SECURITY_ATTRIBUTES,
|
|
|
|
) CreateDirectoryError!HANDLE {
|
|
|
|
const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) {
|
|
|
|
error.Overflow => return error.NameTooLong,
|
|
|
|
};
|
|
|
|
var nt_name = UNICODE_STRING{
|
|
|
|
.Length = path_len_bytes,
|
|
|
|
.MaximumLength = path_len_bytes,
|
|
|
|
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
|
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
var attr = OBJECT_ATTRIBUTES{
|
|
|
|
.Length = @sizeOf(OBJECT_ATTRIBUTES),
|
|
|
|
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
|
|
|
|
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
|
|
|
.ObjectName = &nt_name,
|
|
|
|
.SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null,
|
|
|
|
.SecurityQualityOfService = null,
|
|
|
|
};
|
|
|
|
var io: IO_STATUS_BLOCK = undefined;
|
|
|
|
var result_handle: HANDLE = undefined;
|
|
|
|
const rc = ntdll.NtCreateFile(
|
|
|
|
&result_handle,
|
|
|
|
GENERIC_READ | SYNCHRONIZE,
|
|
|
|
&attr,
|
|
|
|
&io,
|
|
|
|
null,
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
FILE_SHARE_READ,
|
|
|
|
FILE_CREATE,
|
|
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
|
|
null,
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
switch (rc) {
|
|
|
|
.SUCCESS => return result_handle,
|
|
|
|
.OBJECT_NAME_INVALID => unreachable,
|
|
|
|
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
|
|
|
|
.INVALID_PARAMETER => unreachable,
|
|
|
|
.ACCESS_DENIED => return error.AccessDenied,
|
|
|
|
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
|
|
|
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
|
|
|
else => return unexpectedStatus(rc),
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const RemoveDirectoryError = error{
|
|
|
|
FileNotFound,
|
|
|
|
DirNotEmpty,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void {
|
|
|
|
const dir_path_w = try sliceToPrefixedFileW(dir_path);
|
|
|
|
return RemoveDirectoryW(&dir_path_w);
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:40:59 -08:00
|
|
|
pub fn RemoveDirectoryW(dir_path_w: [*:0]const u16) RemoveDirectoryError!void {
|
2019-05-24 15:27:18 -07:00
|
|
|
if (kernel32.RemoveDirectoryW(dir_path_w) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.DIR_NOT_EMPTY => return error.DirNotEmpty,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetStdHandleError = error{
|
|
|
|
NoStandardHandleAttached,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
2019-05-24 16:36:09 -07:00
|
|
|
pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE {
|
2019-05-24 15:27:18 -07:00
|
|
|
const handle = kernel32.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached;
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const SetFilePointerError = error{Unexpected};
|
|
|
|
|
|
|
|
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_BEGIN`.
|
|
|
|
pub fn SetFilePointerEx_BEGIN(handle: HANDLE, offset: u64) SetFilePointerError!void {
|
|
|
|
// "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
|
|
|
|
// is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
|
|
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
|
|
|
|
const ipos = @bitCast(LARGE_INTEGER, offset);
|
|
|
|
if (kernel32.SetFilePointerEx(handle, ipos, null, FILE_BEGIN) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.INVALID_PARAMETER => unreachable,
|
|
|
|
.INVALID_HANDLE => unreachable,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_CURRENT`.
|
|
|
|
pub fn SetFilePointerEx_CURRENT(handle: HANDLE, offset: i64) SetFilePointerError!void {
|
|
|
|
if (kernel32.SetFilePointerEx(handle, offset, null, FILE_CURRENT) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.INVALID_PARAMETER => unreachable,
|
|
|
|
.INVALID_HANDLE => unreachable,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_END`.
|
|
|
|
pub fn SetFilePointerEx_END(handle: HANDLE, offset: i64) SetFilePointerError!void {
|
|
|
|
if (kernel32.SetFilePointerEx(handle, offset, null, FILE_END) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.INVALID_PARAMETER => unreachable,
|
|
|
|
.INVALID_HANDLE => unreachable,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The SetFilePointerEx function with parameters to get the current offset.
|
|
|
|
pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
|
|
|
|
var result: LARGE_INTEGER = undefined;
|
|
|
|
if (kernel32.SetFilePointerEx(handle, 0, &result, FILE_CURRENT) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.INVALID_PARAMETER => unreachable,
|
|
|
|
.INVALID_HANDLE => unreachable,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Based on the docs for FILE_BEGIN, it seems that the returned signed integer
|
|
|
|
// should be interpreted as an unsigned integer.
|
|
|
|
return @bitCast(u64, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetFinalPathNameByHandleError = error{
|
|
|
|
FileNotFound,
|
|
|
|
SystemResources,
|
|
|
|
NameTooLong,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn GetFinalPathNameByHandleW(
|
|
|
|
hFile: HANDLE,
|
|
|
|
buf_ptr: [*]u16,
|
|
|
|
buf_len: DWORD,
|
|
|
|
flags: DWORD,
|
2019-12-27 21:40:59 -08:00
|
|
|
) GetFinalPathNameByHandleError![:0]u16 {
|
2019-05-26 22:35:58 -07:00
|
|
|
const rc = kernel32.GetFinalPathNameByHandleW(hFile, buf_ptr, buf_len, flags);
|
2019-05-24 15:27:18 -07:00
|
|
|
if (rc == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.NOT_ENOUGH_MEMORY => return error.SystemResources,
|
|
|
|
.FILENAME_EXCED_RANGE => return error.NameTooLong,
|
|
|
|
.INVALID_PARAMETER => unreachable,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
2019-12-27 21:40:59 -08:00
|
|
|
return buf_ptr[0..rc :0];
|
2019-05-24 15:27:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetFileSizeError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn GetFileSizeEx(hFile: HANDLE) GetFileSizeError!u64 {
|
|
|
|
var file_size: LARGE_INTEGER = undefined;
|
|
|
|
if (kernel32.GetFileSizeEx(hFile, &file_size) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return @bitCast(u64, file_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetFileAttributesError = error{
|
|
|
|
FileNotFound,
|
|
|
|
PermissionDenied,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn GetFileAttributes(filename: []const u8) GetFileAttributesError!DWORD {
|
|
|
|
const filename_w = try sliceToPrefixedFileW(filename);
|
|
|
|
return GetFileAttributesW(&filename_w);
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:40:59 -08:00
|
|
|
pub fn GetFileAttributesW(lpFileName: [*:0]const u16) GetFileAttributesError!DWORD {
|
2019-05-26 22:35:58 -07:00
|
|
|
const rc = kernel32.GetFileAttributesW(lpFileName);
|
2019-05-24 15:27:18 -07:00
|
|
|
if (rc == INVALID_FILE_ATTRIBUTES) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.ACCESS_DENIED => return error.PermissionDenied,
|
2019-05-24 15:27:18 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-11-13 19:37:08 -08:00
|
|
|
pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA {
|
|
|
|
var wsadata: ws2_32.WSADATA = undefined;
|
|
|
|
return switch (ws2_32.WSAStartup((@as(WORD, minorVersion) << 8) | majorVersion, &wsadata)) {
|
|
|
|
0 => wsadata,
|
2020-01-31 04:04:24 -08:00
|
|
|
else => |err| unexpectedWSAError(@intToEnum(WinsockError, err)),
|
2019-11-13 19:37:08 -08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn WSACleanup() !void {
|
|
|
|
return switch (ws2_32.WSACleanup()) {
|
|
|
|
0 => {},
|
|
|
|
ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
|
|
|
|
else => |err| return unexpectedWSAError(err),
|
|
|
|
},
|
|
|
|
else => unreachable,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-11-12 23:23:21 -08:00
|
|
|
pub fn WSASocketW(
|
|
|
|
af: i32,
|
|
|
|
socket_type: i32,
|
|
|
|
protocol: i32,
|
|
|
|
protocolInfo: ?*ws2_32.WSAPROTOCOL_INFOW,
|
|
|
|
g: ws2_32.GROUP,
|
|
|
|
dwFlags: DWORD,
|
|
|
|
) !ws2_32.SOCKET {
|
|
|
|
const rc = ws2_32.WSASocketW(af, socket_type, protocol, protocolInfo, g, dwFlags);
|
|
|
|
if (rc == ws2_32.INVALID_SOCKET) {
|
|
|
|
switch (ws2_32.WSAGetLastError()) {
|
2020-01-31 04:04:24 -08:00
|
|
|
.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
|
|
|
|
.WSAEMFILE => return error.ProcessFdQuotaExceeded,
|
|
|
|
.WSAENOBUFS => return error.SystemResources,
|
|
|
|
.WSAEPROTONOSUPPORT => return error.ProtocolNotSupported,
|
2019-11-12 23:23:21 -08:00
|
|
|
else => |err| return unexpectedWSAError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-11-18 21:40:44 -08:00
|
|
|
pub fn closesocket(s: ws2_32.SOCKET) !void {
|
|
|
|
switch (ws2_32.closesocket(s)) {
|
|
|
|
0 => {},
|
|
|
|
ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
|
|
|
|
else => |err| return unexpectedWSAError(err),
|
|
|
|
},
|
|
|
|
else => unreachable,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-13 21:34:13 -08:00
|
|
|
pub fn WSAIoctl(
|
|
|
|
s: ws2_32.SOCKET,
|
|
|
|
dwIoControlCode: DWORD,
|
|
|
|
inBuffer: ?[]const u8,
|
|
|
|
outBuffer: []u8,
|
|
|
|
overlapped: ?*ws2_32.WSAOVERLAPPED,
|
2019-11-21 19:49:24 -08:00
|
|
|
completionRoutine: ?ws2_32.WSAOVERLAPPED_COMPLETION_ROUTINE,
|
2019-11-13 21:34:13 -08:00
|
|
|
) !DWORD {
|
|
|
|
var bytes: DWORD = undefined;
|
|
|
|
switch (ws2_32.WSAIoctl(
|
|
|
|
s,
|
|
|
|
dwIoControlCode,
|
|
|
|
if (inBuffer) |i| i.ptr else null,
|
|
|
|
if (inBuffer) |i| @intCast(DWORD, i.len) else 0,
|
|
|
|
outBuffer.ptr,
|
|
|
|
@intCast(DWORD, outBuffer.len),
|
|
|
|
&bytes,
|
|
|
|
overlapped,
|
|
|
|
completionRoutine,
|
|
|
|
)) {
|
|
|
|
0 => {},
|
|
|
|
ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
|
|
|
|
else => |err| return unexpectedWSAError(err),
|
|
|
|
},
|
|
|
|
else => unreachable,
|
|
|
|
}
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
2019-05-24 19:52:07 -07:00
|
|
|
const GetModuleFileNameError = error{Unexpected};
|
|
|
|
|
2019-12-27 21:40:59 -08:00
|
|
|
pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![:0]u16 {
|
2019-05-24 19:52:07 -07:00
|
|
|
const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len);
|
|
|
|
if (rc == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
2019-12-27 21:40:59 -08:00
|
|
|
return buf_ptr[0..rc :0];
|
2019-05-24 19:52:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub const TerminateProcessError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) TerminateProcessError!void {
|
|
|
|
if (kernel32.TerminateProcess(hProcess, uExitCode) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-25 10:07:44 -07:00
|
|
|
pub const VirtualAllocError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn VirtualAlloc(addr: ?LPVOID, size: usize, alloc_type: DWORD, flProtect: DWORD) VirtualAllocError!LPVOID {
|
|
|
|
return kernel32.VirtualAlloc(addr, size, alloc_type, flProtect) orelse {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn VirtualFree(lpAddress: ?LPVOID, dwSize: usize, dwFreeType: DWORD) void {
|
|
|
|
assert(kernel32.VirtualFree(lpAddress, dwSize, dwFreeType) != 0);
|
|
|
|
}
|
|
|
|
|
2019-05-26 10:17:34 -07:00
|
|
|
pub const SetConsoleTextAttributeError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void {
|
|
|
|
if (kernel32.SetConsoleTextAttribute(hConsoleOutput, wAttributes) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetEnvironmentStringsError = error{OutOfMemory};
|
|
|
|
|
2019-12-27 21:40:59 -08:00
|
|
|
pub fn GetEnvironmentStringsW() GetEnvironmentStringsError![*:0]u16 {
|
2019-05-26 10:17:34 -07:00
|
|
|
return kernel32.GetEnvironmentStringsW() orelse return error.OutOfMemory;
|
|
|
|
}
|
|
|
|
|
2019-12-27 21:40:59 -08:00
|
|
|
pub fn FreeEnvironmentStringsW(penv: [*:0]u16) void {
|
2019-05-26 10:17:34 -07:00
|
|
|
assert(kernel32.FreeEnvironmentStringsW(penv) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const GetEnvironmentVariableError = error{
|
|
|
|
EnvironmentVariableNotFound,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
2019-12-27 21:40:59 -08:00
|
|
|
pub fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: [*]u16, nSize: DWORD) GetEnvironmentVariableError!DWORD {
|
2019-05-26 10:17:34 -07:00
|
|
|
const rc = kernel32.GetEnvironmentVariableW(lpName, lpBuffer, nSize);
|
|
|
|
if (rc == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.ENVVAR_NOT_FOUND => return error.EnvironmentVariableNotFound,
|
2019-05-26 10:17:34 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-05-26 10:37:34 -07:00
|
|
|
pub const CreateProcessError = error{
|
|
|
|
FileNotFound,
|
2019-06-18 00:40:37 -07:00
|
|
|
AccessDenied,
|
2019-05-26 10:37:34 -07:00
|
|
|
InvalidName,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn CreateProcessW(
|
|
|
|
lpApplicationName: ?LPWSTR,
|
|
|
|
lpCommandLine: LPWSTR,
|
|
|
|
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
|
|
|
|
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
|
|
|
|
bInheritHandles: BOOL,
|
|
|
|
dwCreationFlags: DWORD,
|
|
|
|
lpEnvironment: ?*c_void,
|
|
|
|
lpCurrentDirectory: ?LPWSTR,
|
|
|
|
lpStartupInfo: *STARTUPINFOW,
|
|
|
|
lpProcessInformation: *PROCESS_INFORMATION,
|
|
|
|
) CreateProcessError!void {
|
|
|
|
if (kernel32.CreateProcessW(
|
|
|
|
lpApplicationName,
|
|
|
|
lpCommandLine,
|
|
|
|
lpProcessAttributes,
|
|
|
|
lpThreadAttributes,
|
2019-05-26 23:00:39 -07:00
|
|
|
bInheritHandles,
|
2019-05-26 10:37:34 -07:00
|
|
|
dwCreationFlags,
|
|
|
|
lpEnvironment,
|
|
|
|
lpCurrentDirectory,
|
|
|
|
lpStartupInfo,
|
|
|
|
lpProcessInformation,
|
|
|
|
) == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.ACCESS_DENIED => return error.AccessDenied,
|
|
|
|
.INVALID_PARAMETER => unreachable,
|
|
|
|
.INVALID_NAME => return error.InvalidName,
|
2019-05-26 10:37:34 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-26 20:35:26 -07:00
|
|
|
pub const LoadLibraryError = error{
|
|
|
|
FileNotFound,
|
|
|
|
Unexpected,
|
|
|
|
};
|
|
|
|
|
2019-12-27 21:40:59 -08:00
|
|
|
pub fn LoadLibraryW(lpLibFileName: [*:0]const u16) LoadLibraryError!HMODULE {
|
2019-05-26 20:35:26 -07:00
|
|
|
return kernel32.LoadLibraryW(lpLibFileName) orelse {
|
|
|
|
switch (kernel32.GetLastError()) {
|
2020-01-31 01:46:09 -08:00
|
|
|
.FILE_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.PATH_NOT_FOUND => return error.FileNotFound,
|
|
|
|
.MOD_NOT_FOUND => return error.FileNotFound,
|
2019-05-26 20:35:26 -07:00
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn FreeLibrary(hModule: HMODULE) void {
|
|
|
|
assert(kernel32.FreeLibrary(hModule) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn QueryPerformanceFrequency() u64 {
|
|
|
|
// "On systems that run Windows XP or later, the function will always succeed"
|
|
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancefrequency
|
|
|
|
var result: LARGE_INTEGER = undefined;
|
|
|
|
assert(kernel32.QueryPerformanceFrequency(&result) != 0);
|
|
|
|
// The kernel treats this integer as unsigned.
|
|
|
|
return @bitCast(u64, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn QueryPerformanceCounter() u64 {
|
|
|
|
// "On systems that run Windows XP or later, the function will always succeed"
|
|
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancecounter
|
|
|
|
var result: LARGE_INTEGER = undefined;
|
|
|
|
assert(kernel32.QueryPerformanceCounter(&result) != 0);
|
|
|
|
// The kernel treats this integer as unsigned.
|
|
|
|
return @bitCast(u64, result);
|
|
|
|
}
|
|
|
|
|
2019-05-26 21:48:56 -07:00
|
|
|
pub fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) void {
|
|
|
|
assert(kernel32.InitOnceExecuteOnce(InitOnce, InitFn, Parameter, Context) != 0);
|
|
|
|
}
|
|
|
|
|
2019-05-26 22:35:58 -07:00
|
|
|
pub fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) void {
|
|
|
|
assert(kernel32.HeapFree(hHeap, dwFlags, lpMem) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn HeapDestroy(hHeap: HANDLE) void {
|
|
|
|
assert(kernel32.HeapDestroy(hHeap) != 0);
|
|
|
|
}
|
|
|
|
|
2019-07-14 00:06:20 -07:00
|
|
|
pub const GetFileInformationByHandleError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn GetFileInformationByHandle(
|
|
|
|
hFile: HANDLE,
|
|
|
|
) GetFileInformationByHandleError!BY_HANDLE_FILE_INFORMATION {
|
|
|
|
var info: BY_HANDLE_FILE_INFORMATION = undefined;
|
2019-07-15 14:54:50 -07:00
|
|
|
const rc = ntdll.GetFileInformationByHandle(hFile, &info);
|
2019-07-14 00:06:20 -07:00
|
|
|
if (rc == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2019-07-14 21:02:44 -07:00
|
|
|
pub const SetFileTimeError = error{Unexpected};
|
|
|
|
|
|
|
|
pub fn SetFileTime(
|
|
|
|
hFile: HANDLE,
|
|
|
|
lpCreationTime: ?*const FILETIME,
|
|
|
|
lpLastAccessTime: ?*const FILETIME,
|
|
|
|
lpLastWriteTime: ?*const FILETIME,
|
|
|
|
) SetFileTimeError!void {
|
|
|
|
const rc = kernel32.SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
|
|
|
|
if (rc == 0) {
|
|
|
|
switch (kernel32.GetLastError()) {
|
|
|
|
else => |err| return unexpectedError(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-06 00:17:14 -08:00
|
|
|
pub fn teb() *TEB {
|
|
|
|
return switch (builtin.arch) {
|
|
|
|
.i386 => asm volatile (
|
|
|
|
\\ movl %%fs:0x18, %[ptr]
|
|
|
|
: [ptr] "=r" (-> *TEB)
|
|
|
|
),
|
|
|
|
.x86_64 => asm volatile (
|
|
|
|
\\ movq %%gs:0x30, %[ptr]
|
|
|
|
: [ptr] "=r" (-> *TEB)
|
|
|
|
),
|
|
|
|
.aarch64 => asm volatile (
|
|
|
|
\\ mov %[ptr], x18
|
|
|
|
: [ptr] "=r" (-> *TEB)
|
|
|
|
),
|
|
|
|
else => @compileError("unsupported arch"),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-10-21 11:18:01 -07:00
|
|
|
pub fn peb() *PEB {
|
2020-03-06 00:17:14 -08:00
|
|
|
return teb().ProcessEnvironmentBlock;
|
2019-10-21 11:18:01 -07:00
|
|
|
}
|
|
|
|
|
2019-07-14 22:41:06 -07:00
|
|
|
/// A file time is a 64-bit value that represents the number of 100-nanosecond
|
|
|
|
/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated
|
|
|
|
/// Universal Time (UTC).
|
|
|
|
/// This function returns the number of nanoseconds since the canonical epoch,
|
|
|
|
/// which is the POSIX one (Jan 01, 1970 AD).
|
2019-07-15 14:54:50 -07:00
|
|
|
pub fn fromSysTime(hns: i64) i64 {
|
2019-07-14 22:41:06 -07:00
|
|
|
const adjusted_epoch = hns + std.time.epoch.windows * (std.time.ns_per_s / 100);
|
|
|
|
return adjusted_epoch * 100;
|
|
|
|
}
|
|
|
|
|
2019-07-15 14:54:50 -07:00
|
|
|
pub fn toSysTime(ns: i64) i64 {
|
|
|
|
const hns = @divFloor(ns, 100);
|
|
|
|
return hns - std.time.epoch.windows * (std.time.ns_per_s / 100);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn fileTimeToNanoSeconds(ft: FILETIME) i64 {
|
2019-11-06 20:25:57 -08:00
|
|
|
const hns = @bitCast(i64, (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime);
|
2019-07-15 14:54:50 -07:00
|
|
|
return fromSysTime(hns);
|
|
|
|
}
|
|
|
|
|
2019-07-14 22:41:06 -07:00
|
|
|
/// Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME.
|
|
|
|
pub fn nanoSecondsToFileTime(ns: i64) FILETIME {
|
2019-07-15 14:54:50 -07:00
|
|
|
const adjusted = @bitCast(u64, toSysTime(ns));
|
2019-07-14 21:02:44 -07:00
|
|
|
return FILETIME{
|
2019-07-15 14:54:50 -07:00
|
|
|
.dwHighDateTime = @truncate(u32, adjusted >> 32),
|
|
|
|
.dwLowDateTime = @truncate(u32, adjusted),
|
2019-07-14 21:02:44 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-11-24 21:30:28 -08:00
|
|
|
pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 {
|
2019-05-23 21:13:13 -07:00
|
|
|
return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
|
|
|
|
}
|
|
|
|
|
2019-11-24 21:30:28 -08:00
|
|
|
pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE:0]u16 {
|
|
|
|
return sliceToPrefixedSuffixedFileW(s, &[_]u16{});
|
2019-05-23 21:13:13 -07:00
|
|
|
}
|
|
|
|
|
2019-11-21 15:51:12 -08:00
|
|
|
/// Assumes an absolute path.
|
2019-11-24 21:30:28 -08:00
|
|
|
pub fn wToPrefixedFileW(s: []const u16) ![PATH_MAX_WIDE:0]u16 {
|
2019-11-21 15:51:12 -08:00
|
|
|
// TODO https://github.com/ziglang/zig/issues/2765
|
2019-11-24 21:30:28 -08:00
|
|
|
var result: [PATH_MAX_WIDE:0]u16 = undefined;
|
2019-11-21 15:51:12 -08:00
|
|
|
|
2019-11-29 18:55:27 -08:00
|
|
|
const start_index = if (mem.startsWith(u16, s, &[_]u16{ '\\', '?' })) 0 else blk: {
|
2019-11-21 15:51:12 -08:00
|
|
|
const prefix = [_]u16{ '\\', '?', '?', '\\' };
|
2019-11-29 18:55:27 -08:00
|
|
|
mem.copy(u16, result[0..], &prefix);
|
2019-11-21 15:51:12 -08:00
|
|
|
break :blk prefix.len;
|
|
|
|
};
|
|
|
|
const end_index = start_index + s.len;
|
|
|
|
if (end_index + 1 > result.len) return error.NameTooLong;
|
|
|
|
mem.copy(u16, result[start_index..], s);
|
|
|
|
result[end_index] = 0;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-11-24 21:30:28 -08:00
|
|
|
pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len:0]u16 {
|
2019-07-15 14:54:50 -07:00
|
|
|
// TODO https://github.com/ziglang/zig/issues/2765
|
2019-11-24 21:30:28 -08:00
|
|
|
var result: [PATH_MAX_WIDE + suffix.len:0]u16 = undefined;
|
2019-05-23 21:13:13 -07:00
|
|
|
// > File I/O functions in the Windows API convert "/" to "\" as part of
|
|
|
|
// > converting the name to an NT-style name, except when using the "\\?\"
|
|
|
|
// > prefix as detailed in the following sections.
|
|
|
|
// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
|
2019-08-31 21:30:35 -07:00
|
|
|
// Because we want the larger maximum path length for absolute paths, we
|
|
|
|
// disallow forward slashes in zig std lib file functions on Windows.
|
2019-05-23 21:13:13 -07:00
|
|
|
for (s) |byte| {
|
|
|
|
switch (byte) {
|
2019-08-31 21:30:35 -07:00
|
|
|
'/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
|
2019-05-23 21:13:13 -07:00
|
|
|
else => {},
|
|
|
|
}
|
|
|
|
}
|
2019-10-21 11:18:01 -07:00
|
|
|
const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: {
|
|
|
|
const prefix = [_]u16{ '\\', '?', '?', '\\' };
|
2019-11-29 18:55:27 -08:00
|
|
|
mem.copy(u16, result[0..], &prefix);
|
2019-05-23 21:13:13 -07:00
|
|
|
break :blk prefix.len;
|
|
|
|
};
|
|
|
|
const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
|
|
|
|
if (end_index + suffix.len > result.len) return error.NameTooLong;
|
|
|
|
mem.copy(u16, result[end_index..], suffix);
|
2019-11-24 22:10:30 -08:00
|
|
|
result[end_index + suffix.len] = 0;
|
2019-07-15 16:50:56 -07:00
|
|
|
return result;
|
2019-05-23 21:13:13 -07:00
|
|
|
}
|
|
|
|
|
2019-06-05 13:30:01 -07:00
|
|
|
inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID {
|
2019-06-05 12:42:36 -07:00
|
|
|
return (s << 10) | p;
|
|
|
|
}
|
|
|
|
|
2019-05-23 21:13:13 -07:00
|
|
|
/// Call this when you made a windows DLL call or something that does SetLastError
|
|
|
|
/// and you get an unexpected error.
|
2020-01-31 01:46:09 -08:00
|
|
|
pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
|
2019-05-23 21:13:13 -07:00
|
|
|
if (std.os.unexpected_error_tracing) {
|
2019-06-05 15:18:56 -07:00
|
|
|
// 614 is the length of the longest windows error desciption
|
|
|
|
var buf_u16: [614]u16 = undefined;
|
|
|
|
var buf_u8: [614]u8 = undefined;
|
2020-03-18 18:25:35 -07:00
|
|
|
const len = kernel32.FormatMessageW(
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
|
|
null,
|
|
|
|
err,
|
|
|
|
MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT),
|
|
|
|
&buf_u16,
|
|
|
|
buf_u16.len / @sizeOf(TCHAR),
|
|
|
|
null,
|
|
|
|
);
|
2019-06-05 15:18:56 -07:00
|
|
|
_ = std.unicode.utf16leToUtf8(&buf_u8, buf_u16[0..len]) catch unreachable;
|
2020-01-31 01:46:09 -08:00
|
|
|
std.debug.warn("error.Unexpected: GetLastError({}): {}\n", .{ @enumToInt(err), buf_u8[0..len] });
|
2019-05-23 21:13:13 -07:00
|
|
|
std.debug.dumpCurrentStackTrace(null);
|
|
|
|
}
|
|
|
|
return error.Unexpected;
|
|
|
|
}
|
2019-07-15 14:54:50 -07:00
|
|
|
|
2020-01-31 04:04:24 -08:00
|
|
|
pub fn unexpectedWSAError(err: WinsockError) std.os.UnexpectedError {
|
|
|
|
return unexpectedError(@intToEnum(Win32Error, @enumToInt(err)));
|
2019-11-12 23:23:21 -08:00
|
|
|
}
|
|
|
|
|
2019-07-15 14:54:50 -07:00
|
|
|
/// Call this when you made a windows NtDll call
|
|
|
|
/// and you get an unexpected status.
|
|
|
|
pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {
|
|
|
|
if (std.os.unexpected_error_tracing) {
|
2020-01-31 00:47:00 -08:00
|
|
|
std.debug.warn("error.Unexpected NTSTATUS=0x{x}\n", .{@enumToInt(status)});
|
2019-07-15 14:54:50 -07:00
|
|
|
std.debug.dumpCurrentStackTrace(null);
|
|
|
|
}
|
|
|
|
return error.Unexpected;
|
|
|
|
}
|