[wip] use NtDll APIs on Windows to implement std.fs.Dir

master
Andrew Kelley 2019-10-21 14:18:01 -04:00
parent 5b1a492012
commit ef67c49785
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
10 changed files with 566 additions and 106 deletions

View File

@ -353,18 +353,13 @@ pub fn deleteTree(full_path: []const u8) !void {
return dir.deleteTree(path.basename(full_path));
} else {
return Dir.posix_cwd.deleteTree(full_path);
return Dir.cwd().deleteTree(full_path);
}
}
pub const Dir = struct {
fd: os.fd_t,
/// An open handle to the current working directory.
/// Closing this directory is safety-checked illegal behavior.
/// Not available on Windows.
pub const posix_cwd = Dir{ .fd = os.AT_FDCWD };
pub const Entry = struct {
name: []const u8,
kind: Kind,
@ -386,12 +381,10 @@ pub const Dir = struct {
.macosx, .ios, .freebsd, .netbsd => struct {
dir: Dir,
seek: i64,
buf: [buffer_len]u8,
buf: [8192]u8, // TODO align(@alignOf(os.dirent)),
index: usize,
end_index: usize,
pub const buffer_len = 8192;
const Self = @This();
/// Memory such as file names referenced in this returned entry becomes invalid
@ -407,27 +400,24 @@ pub const Dir = struct {
fn nextDarwin(self: *Self) !?Entry {
start_over: while (true) {
if (self.index >= self.end_index) {
while (true) {
const rc = os.system.__getdirentries64(
self.dir.fd,
&self.buf,
self.buf.len,
&self.seek,
);
if (rc == 0) return null;
if (rc < 0) {
switch (os.errno(rc)) {
os.EBADF => unreachable,
os.EFAULT => unreachable,
os.ENOTDIR => unreachable,
os.EINVAL => unreachable,
else => |err| return os.unexpectedErrno(err),
}
const rc = os.system.__getdirentries64(
self.dir.fd,
&self.buf,
self.buf.len,
&self.seek,
);
if (rc == 0) return null;
if (rc < 0) {
switch (os.errno(rc)) {
os.EBADF => unreachable,
os.EFAULT => unreachable,
os.ENOTDIR => unreachable,
os.EINVAL => unreachable,
else => |err| return os.unexpectedErrno(err),
}
self.index = 0;
self.end_index = @intCast(usize, rc);
break;
}
self.index = 0;
self.end_index = @intCast(usize, rc);
}
const darwin_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);
const next_index = self.index + darwin_entry.d_reclen;
@ -460,26 +450,23 @@ pub const Dir = struct {
fn nextBsd(self: *Self) !?Entry {
start_over: while (true) {
if (self.index >= self.end_index) {
while (true) {
const rc = os.system.getdirentries(
self.dir.fd,
self.buf[0..].ptr,
self.buf.len,
&self.seek,
);
switch (os.errno(rc)) {
0 => {},
os.EBADF => unreachable,
os.EFAULT => unreachable,
os.ENOTDIR => unreachable,
os.EINVAL => unreachable,
else => |err| return os.unexpectedErrno(err),
}
if (rc == 0) return null;
self.index = 0;
self.end_index = @intCast(usize, rc);
break;
const rc = os.system.getdirentries(
self.dir.fd,
self.buf[0..].ptr,
self.buf.len,
&self.seek,
);
switch (os.errno(rc)) {
0 => {},
os.EBADF => unreachable,
os.EFAULT => unreachable,
os.ENOTDIR => unreachable,
os.EINVAL => unreachable,
else => |err| return os.unexpectedErrno(err),
}
if (rc == 0) return null;
self.index = 0;
self.end_index = @intCast(usize, rc);
}
const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);
const next_index = self.index + freebsd_entry.d_reclen;
@ -511,12 +498,10 @@ pub const Dir = struct {
},
.linux => struct {
dir: Dir,
buf: [buffer_len]u8,
buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
index: usize,
end_index: usize,
pub const buffer_len = 8192;
const Self = @This();
/// Memory such as file names referenced in this returned entry becomes invalid
@ -524,21 +509,18 @@ pub const Dir = struct {
pub fn next(self: *Self) !?Entry {
start_over: while (true) {
if (self.index >= self.end_index) {
while (true) {
const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
switch (os.linux.getErrno(rc)) {
0 => {},
os.EBADF => unreachable,
os.EFAULT => unreachable,
os.ENOTDIR => unreachable,
os.EINVAL => unreachable,
else => |err| return os.unexpectedErrno(err),
}
if (rc == 0) return null;
self.index = 0;
self.end_index = rc;
break;
const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
switch (os.linux.getErrno(rc)) {
0 => {},
os.EBADF => unreachable,
os.EFAULT => unreachable,
os.ENOTDIR => unreachable,
os.EINVAL => unreachable,
else => |err| return os.unexpectedErrno(err),
}
if (rc == 0) return null;
self.index = 0;
self.end_index = rc;
}
const linux_entry = @ptrCast(*align(1) os.dirent64, &self.buf[self.index]);
const next_index = self.index + linux_entry.d_reclen;
@ -570,13 +552,117 @@ pub const Dir = struct {
},
.windows => struct {
dir: Dir,
find_file_data: os.windows.WIN32_FIND_DATAW,
buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),
index: usize,
end_index: usize,
first: bool,
name_data: [256]u8,
const Self = @This();
pub fn next(self: *Self) !?Entry {
start_over: while (true) {
const w = os.windows;
if (self.index >= self.end_index) {
var io: w.IO_STATUS_BLOCK = undefined;
//var mask_buf = [2]u16{ 'a', 0 };
//var mask = w.UNICODE_STRING{
// .Length = 2,
// .MaximumLength = 2,
// .Buffer = &mask_buf,
//};
const rc = w.ntdll.NtQueryDirectoryFile(
self.dir.fd,
null,
null,
null,
&io,
&self.buf,
self.buf.len,
.FileBothDirectoryInformation,
w.FALSE,
null,
if (self.first) w.BOOLEAN(w.TRUE) else w.BOOLEAN(w.FALSE),
);
self.first = false;
if (io.Information == 0) return null;
self.index = 0;
self.end_index = io.Information;
switch (rc) {
w.STATUS.SUCCESS => {},
else => return w.unexpectedStatus(rc),
}
}
const aligned_ptr = @alignCast(@alignOf(w.FILE_BOTH_DIR_INFORMATION), &self.buf[self.index]);
const dir_info = @ptrCast(*w.FILE_BOTH_DIR_INFORMATION, aligned_ptr);
if (dir_info.NextEntryOffset != 0) {
self.index += dir_info.NextEntryOffset;
} else {
self.index = self.buf.len;
}
const name_utf16le = @ptrCast([*]u16, &dir_info.FileName)[0 .. dir_info.FileNameLength / 2];
if (mem.eql(u16, name_utf16le, [_]u16{'.'}) or mem.eql(u16, name_utf16le, [_]u16{ '.', '.' }))
continue;
// Trust that Windows gives us valid UTF-16LE
const name_utf8_len = std.unicode.utf16leToUtf8(self.name_data[0..], name_utf16le) catch unreachable;
const name_utf8 = self.name_data[0..name_utf8_len];
const kind = blk: {
const attrs = dir_info.FileAttributes;
if (attrs & w.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
if (attrs & w.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
break :blk Entry.Kind.File;
};
return Entry{
.name = name_utf8,
.kind = kind,
};
}
}
},
else => @compileError("unimplemented"),
};
pub fn iterate(self: Dir) Iterator {
switch (builtin.os) {
.macosx, .ios, .freebsd, .netbsd => return Iterator{
.dir = self,
.seek = 0,
.index = 0,
.end_index = 0,
.buf = undefined,
},
.linux => return Iterator{
.dir = self,
.index = 0,
.end_index = 0,
.buf = undefined,
},
.windows => return Iterator{
.dir = self,
.index = 0,
.end_index = 0,
.first = true,
.buf = undefined,
.name_data = undefined,
},
else => @compileError("unimplemented"),
}
}
/// Returns an open handle to the current working directory.
/// Closing the returned `Dir` is checked illegal behavior.
/// On POSIX targets, this function is comptime-callable.
pub fn cwd() Dir {
if (os.windows.is_the_target) {
return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
} else {
return Dir{ .fd = os.AT_FDCWD };
}
}
pub const OpenError = error{
FileNotFound,
NotDir,
@ -594,18 +680,15 @@ pub const Dir = struct {
/// Call `close` to free the directory handle.
pub fn open(dir_path: []const u8) OpenError!Dir {
return posix_cwd.openDir(dir_path);
return cwd().openDir(dir_path);
}
/// Same as `open` except the parameter is null-terminated.
pub fn openC(dir_path_c: [*]const u8) OpenError!Dir {
return posix_cwd.openDirC(dir_path_c);
return cwd().openDirC(dir_path_c);
}
pub fn close(self: *Dir) void {
if (os.windows.is_the_target) {
@panic("TODO");
}
os.close(self.fd);
self.* = undefined;
}
@ -625,14 +708,25 @@ pub const Dir = struct {
/// Call `close` on the result when done.
pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {
std.debug.warn("openDir {}\n", sub_path);
if (os.windows.is_the_target) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openDirW(&sub_path_w);
}
const sub_path_c = try os.toPosixPath(sub_path);
return self.openDirC(&sub_path_c);
}
/// Call `close` on the result when done.
pub fn openDirC(self: Dir, sub_path: [*]const u8) OpenError!Dir {
/// Same as `openDir` except the parameter is null-terminated.
pub fn openDirC(self: Dir, sub_path_c: [*]const u8) OpenError!Dir {
if (os.windows.is_the_target) {
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
return self.openDirW(&sub_path_w);
}
const flags = os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC;
const fd = os.openatC(self.fd, sub_path, flags, 0) catch |err| switch (err) {
const fd = os.openatC(self.fd, sub_path_c, flags, 0) catch |err| switch (err) {
error.FileTooBig => unreachable, // can't happen for directories
error.IsDir => unreachable, // we're providing O_DIRECTORY
error.NoSpaceLeft => unreachable, // not providing O_CREAT
@ -642,6 +736,79 @@ pub const Dir = struct {
return Dir{ .fd = fd };
}
/// Same as `openDir` except the path parameter is UTF16LE, NT-prefixed.
/// This function is Windows-only.
pub fn openDirW(self: Dir, sub_path_w: [*]const u16) OpenError!Dir {
const w = os.windows;
var result = Dir{
.fd = undefined,
};
//var mask: ?[*]const u16 = undefined;
//var nt_name: w.UNICODE_STRING = undefined;
//if (w.ntdll.RtlDosPathNameToNtPathName_U(sub_path_w, &nt_name, null, null) == 0) {
// return error.FileNotFound;
//}
//defer w.ntdll.RtlFreeUnicodeString(&nt_name);
//if (mask) |m| {
// if (m[0] == 0) {
// return error.FileNotFound;
// } else {
// nt_name.Length = @intCast(u16, @ptrToInt(mask) - @ptrToInt(nt_name.Buffer));
// }
//} else {
// return error.FileNotFound;
//}
const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2);
std.debug.warn("path_len_bytes = {}\n", path_len_bytes);
var nt_name = w.UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
};
var attr = w.OBJECT_ATTRIBUTES{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
.RootDirectory = if (path.isAbsoluteW(sub_path_w)) null else self.fd,
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
.ObjectName = &nt_name,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
std.debug.warn("RootDirectory = {}\n", attr.RootDirectory);
var io: w.IO_STATUS_BLOCK = undefined;
const wide_slice = nt_name.Buffer[0 .. nt_name.Length / 2];
//const wide_slice2 = std.mem.toSliceConst(u16, mask.?);
var buf: [200]u8 = undefined;
//var buf2: [200]u8 = undefined;
const len = std.unicode.utf16leToUtf8(&buf, wide_slice) catch unreachable;
//const len2 = std.unicode.utf16leToUtf8(&buf2, wide_slice2) catch unreachable;
std.debug.warn("path: {}\n", buf[0..len]);
//std.debug.warn("path: {}\nmask: {}\n", buf[0..len], buf2[0..len2]);
const rc = w.ntdll.NtCreateFile(
&result.fd,
w.GENERIC_READ | w.SYNCHRONIZE,
&attr,
&io,
null,
0,
w.FILE_SHARE_READ | w.FILE_SHARE_WRITE,
w.FILE_OPEN,
w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT,
null,
0,
);
std.debug.warn("result.fd = {}\n", result.fd);
switch (rc) {
w.STATUS.SUCCESS => return result,
w.STATUS.OBJECT_NAME_INVALID => @panic("openDirW invalid object name"),
w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
w.STATUS.INVALID_PARAMETER => {
@panic("invalid parameter");
},
else => return w.unexpectedStatus(rc),
}
}
pub const DeleteFileError = os.UnlinkError;
/// Delete a file name and possibly the file it refers to, based on an open directory handle.
@ -677,6 +844,10 @@ pub const Dir = struct {
/// Returns `error.DirNotEmpty` if the directory is not empty.
/// To delete a directory recursively, see `deleteTree`.
pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
if (os.windows.is_the_target) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.deleteDirW(&sub_path_w);
}
const sub_path_c = try os.toPosixPath(sub_path);
return self.deleteDirC(&sub_path_c);
}
@ -689,24 +860,25 @@ pub const Dir = struct {
};
}
pub fn iterate(self: Dir) Iterator {
switch (builtin.os) {
.macosx, .ios, .freebsd, .netbsd => return Iterator{
.dir = self,
.seek = 0,
.index = 0,
.end_index = 0,
.buf = undefined,
},
.linux => return Iterator{
.dir = self,
.index = 0,
.end_index = 0,
.buf = undefined,
},
.windows => @panic("TODO"),
else => @compileError("unimplemented"),
}
/// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.
/// This function is Windows-only.
pub fn deleteDirW(self: Dir, sub_path_w: [*]const u16) DeleteDirError!void {
os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {
error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
else => |e| return e,
};
}
/// Read value of a symbolic link.
/// The return value is a slice of `buffer`, from index `0`.
pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
const sub_path_c = try os.toPosixPath(sub_path);
return self.readLinkC(&sub_path_c, buffer);
}
/// Same as `readLink`, except the `pathname` parameter is null-terminated.
pub fn readLinkC(self: Dir, sub_path_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
return os.readlinkatC(self.fd, sub_path_c, buffer);
}
pub const DeleteTreeError = error{
@ -953,7 +1125,6 @@ pub const Walker = struct {
/// Must call `Walker.deinit` when done.
/// `dir_path` must not end in a path separator.
/// The order of returned file system entries is undefined.
/// TODO: https://github.com/ziglang/zig/issues/2888
pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
assert(!mem.endsWith(u8, dir_path, path.sep_str));
@ -978,15 +1149,13 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
/// Read value of a symbolic link.
/// The return value is a slice of buffer, from index `0`.
/// TODO https://github.com/ziglang/zig/issues/2888
pub fn readLink(pathname: []const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
return os.readlink(pathname, buffer);
}
/// Same as `readLink`, except the `pathname` parameter is null-terminated.
/// TODO https://github.com/ziglang/zig/issues/2888
pub fn readLinkC(pathname: [*]const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
return os.readlinkC(pathname, buffer);
/// Same as `readLink`, except the parameter is null-terminated.
pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
return os.readlinkC(pathname_c, buffer);
}
pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError;

View File

@ -243,6 +243,7 @@ pub const File = struct {
switch (rc) {
windows.STATUS.SUCCESS => {},
windows.STATUS.BUFFER_OVERFLOW => {},
windows.STATUS.INVALID_PARAMETER => unreachable,
else => return windows.unexpectedStatus(rc),
}
return Stat{

View File

@ -136,6 +136,25 @@ pub fn isAbsolute(path: []const u8) bool {
}
}
pub fn isAbsoluteW(path_w: [*]const u16) bool {
if (path_w[0] == '/')
return true;
if (path_w[0] == '\\') {
return true;
}
if (path_w[0] == 0 or path_w[1] == 0 or path_w[2] == 0) {
return false;
}
if (path_w[1] == ':') {
if (path_w[2] == '/')
return true;
if (path_w[2] == '\\')
return true;
}
return false;
}
pub fn isAbsoluteWindows(path: []const u8) bool {
if (path[0] == '/')
return true;

View File

@ -127,6 +127,7 @@ pub fn OutStream(comptime WriteError: type) type {
};
}
/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
pub fn writeFile(path: []const u8, data: []const u8) !void {
var file = try File.openWrite(path);
defer file.close();
@ -134,11 +135,13 @@ pub fn writeFile(path: []const u8, data: []const u8) !void {
}
/// On success, caller owns returned buffer.
/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
return readFileAllocAligned(allocator, path, @alignOf(u8));
}
/// On success, caller owns returned buffer.
/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {
var file = try File.openRead(path);
defer file.close();
@ -1084,7 +1087,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
// safety. If it is bad, it will be caught anyway.
const TagInt = @TagType(TagType);
const tag = try self.deserializeInt(TagInt);
inline for (info.fields) |field_info| {
if (field_info.enum_field.?.value == tag) {
const name = field_info.name;

View File

@ -999,12 +999,20 @@ pub const UnlinkatError = UnlinkError || error{
/// Delete a file name and possibly the file it refers to, based on an open directory handle.
pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
if (windows.is_the_target) {
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
return unlinkatW(dirfd, &file_path_w, flags);
}
const file_path_c = try toPosixPath(file_path);
return unlinkatC(dirfd, &file_path_c, flags);
}
/// Same as `unlinkat` but `file_path` is a null-terminated string.
pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatError!void {
if (windows.is_the_target) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
return unlinkatW(dirfd, &file_path_w, flags);
}
switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
0 => return,
EACCES => return error.AccessDenied,
@ -1028,6 +1036,56 @@ pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatErro
}
}
/// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.
pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*]const u16, flags: u32) UnlinkatError!void {
const w = windows;
const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0;
const create_options_flags = if (want_rmdir_behavior)
w.ULONG(w.FILE_DELETE_ON_CLOSE)
else
w.ULONG(w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE);
var nt_name: w.UNICODE_STRING = undefined;
if (w.ntdll.RtlDosPathNameToNtPathName_U(sub_path_w, &nt_name, null, null) == 0) {
return error.FileNotFound;
}
defer w.ntdll.RtlFreeUnicodeString(&nt_name);
var attr = w.OBJECT_ATTRIBUTES{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
.RootDirectory = dirfd,
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
.ObjectName = &nt_name,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
var io: w.IO_STATUS_BLOCK = undefined;
var tmp_handle: w.HANDLE = undefined;
var rc = w.ntdll.NtCreateFile(
&tmp_handle,
w.SYNCHRONIZE | w.DELETE,
&attr,
&io,
null,
0,
w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE,
w.FILE_OPEN,
create_options_flags,
null,
0,
);
if (rc == w.STATUS.SUCCESS) {
rc = w.ntdll.NtClose(tmp_handle);
}
switch (rc) {
w.STATUS.SUCCESS => return,
w.STATUS.OBJECT_NAME_INVALID => unreachable,
w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
w.STATUS.INVALID_PARAMETER => unreachable,
else => return w.unexpectedStatus(rc),
}
}
const RenameError = error{
AccessDenied,
FileBusy,
@ -1287,6 +1345,27 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
}
}
pub fn readlinkatC(dirfd: fd_t, file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
if (windows.is_the_target) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
@compileError("TODO implement readlink for Windows");
}
const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
switch (errno(rc)) {
0 => return out_buffer[0..@bitCast(usize, rc)],
EACCES => return error.AccessDenied,
EFAULT => unreachable,
EINVAL => unreachable,
EIO => return error.FileSystem,
ELOOP => return error.SymLinkLoop,
ENAMETOOLONG => return error.NameTooLong,
ENOENT => return error.FileNotFound,
ENOMEM => return error.SystemResources,
ENOTDIR => return error.NotDir,
else => |err| return unexpectedErrno(err),
}
}
pub const SetIdError = error{
ResourceLimitReached,
InvalidUserId,

View File

@ -158,3 +158,6 @@ pub const EWOULDBLOCK = 140;
pub const EDQUOT = 10069;
pub const F_OK = 0;
/// Remove directory instead of unlinking file
pub const AT_REMOVEDIR = 0x200;

View File

@ -792,6 +792,25 @@ pub fn SetFileTime(
}
}
pub fn peb() *PEB {
switch (builtin.arch) {
.i386 => {
return asm (
\\ mov %%fs:0x18, %[ptr]
\\ mov %%ds:0x30(%[ptr]), %[ptr]
: [ptr] "=r" (-> *PEB)
);
},
.x86_64 => {
return asm (
\\ mov %%gs:0x60, %[ptr]
: [ptr] "=r" (-> *PEB)
);
},
else => @compileError("unsupported architecture"),
}
}
/// 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).
@ -844,8 +863,8 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16)
else => {},
}
}
const start_index = if (mem.startsWith(u8, s, "\\\\") or !std.fs.path.isAbsolute(s)) 0 else blk: {
const prefix = [_]u16{ '\\', '\\', '?', '\\' };
const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: {
const prefix = [_]u16{ '\\', '?', '?', '\\' };
mem.copy(u16, result[0..], prefix);
break :blk prefix.len;
};

View File

@ -300,6 +300,44 @@ pub const FILE_SHARE_DELETE = 0x00000004;
pub const FILE_SHARE_READ = 0x00000001;
pub const FILE_SHARE_WRITE = 0x00000002;
pub const DELETE = 0x00010000;
pub const READ_CONTROL = 0x00020000;
pub const WRITE_DAC = 0x00040000;
pub const WRITE_OWNER = 0x00080000;
pub const SYNCHRONIZE = 0x00100000;
pub const STANDARD_RIGHTS_REQUIRED = 0x000f0000;
// disposition for NtCreateFile
pub const FILE_SUPERSEDE = 0;
pub const FILE_OPEN = 1;
pub const FILE_CREATE = 2;
pub const FILE_OPEN_IF = 3;
pub const FILE_OVERWRITE = 4;
pub const FILE_OVERWRITE_IF = 5;
pub const FILE_MAXIMUM_DISPOSITION = 5;
// flags for NtCreateFile and NtOpenFile
pub const FILE_DIRECTORY_FILE = 0x00000001;
pub const FILE_WRITE_THROUGH = 0x00000002;
pub const FILE_SEQUENTIAL_ONLY = 0x00000004;
pub const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008;
pub const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010;
pub const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;
pub const FILE_NON_DIRECTORY_FILE = 0x00000040;
pub const FILE_CREATE_TREE_CONNECTION = 0x00000080;
pub const FILE_COMPLETE_IF_OPLOCKED = 0x00000100;
pub const FILE_NO_EA_KNOWLEDGE = 0x00000200;
pub const FILE_OPEN_FOR_RECOVERY = 0x00000400;
pub const FILE_RANDOM_ACCESS = 0x00000800;
pub const FILE_DELETE_ON_CLOSE = 0x00001000;
pub const FILE_OPEN_BY_FILE_ID = 0x00002000;
pub const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000;
pub const FILE_NO_COMPRESSION = 0x00008000;
pub const FILE_RESERVE_OPFILTER = 0x00100000;
pub const FILE_TRANSACTED_MODE = 0x00200000;
pub const FILE_OPEN_OFFLINE_FILE = 0x00400000;
pub const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000;
pub const CREATE_ALWAYS = 2;
pub const CREATE_NEW = 1;
pub const OPEN_ALWAYS = 4;
@ -720,15 +758,117 @@ pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_P
pub const OBJECT_ATTRIBUTES = extern struct {
Length: ULONG,
RootDirectory: HANDLE,
RootDirectory: ?HANDLE,
ObjectName: *UNICODE_STRING,
Attributes: ULONG,
SecurityDescriptor: ?*c_void,
SecurityQualityOfService: ?*c_void,
};
pub const OBJ_INHERIT = 0x00000002;
pub const OBJ_PERMANENT = 0x00000010;
pub const OBJ_EXCLUSIVE = 0x00000020;
pub const OBJ_CASE_INSENSITIVE = 0x00000040;
pub const OBJ_OPENIF = 0x00000080;
pub const OBJ_OPENLINK = 0x00000100;
pub const OBJ_KERNEL_HANDLE = 0x00000200;
pub const OBJ_VALID_ATTRIBUTES = 0x000003F2;
pub const UNICODE_STRING = extern struct {
Length: USHORT,
MaximumLength: USHORT,
Length: c_ushort,
MaximumLength: c_ushort,
Buffer: [*]WCHAR,
};
pub const PEB = extern struct {
Reserved1: [2]BYTE,
BeingDebugged: BYTE,
Reserved2: [1]BYTE,
Reserved3: [2]PVOID,
Ldr: *PEB_LDR_DATA,
ProcessParameters: *RTL_USER_PROCESS_PARAMETERS,
Reserved4: [3]PVOID,
AtlThunkSListPtr: PVOID,
Reserved5: PVOID,
Reserved6: ULONG,
Reserved7: PVOID,
Reserved8: ULONG,
AtlThunkSListPtr32: ULONG,
Reserved9: [45]PVOID,
Reserved10: [96]BYTE,
PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE,
Reserved11: [128]BYTE,
Reserved12: [1]PVOID,
SessionId: ULONG,
};
pub const PEB_LDR_DATA = extern struct {
Reserved1: [8]BYTE,
Reserved2: [3]PVOID,
InMemoryOrderModuleList: LIST_ENTRY,
};
pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
AllocationSize: ULONG,
Size: ULONG,
Flags: ULONG,
DebugFlags: ULONG,
ConsoleHandle: HANDLE,
ConsoleFlags: ULONG,
hStdInput: HANDLE,
hStdOutput: HANDLE,
hStdError: HANDLE,
CurrentDirectory: CURDIR,
DllPath: UNICODE_STRING,
ImagePathName: UNICODE_STRING,
CommandLine: UNICODE_STRING,
Environment: [*]WCHAR,
dwX: ULONG,
dwY: ULONG,
dwXSize: ULONG,
dwYSize: ULONG,
dwXCountChars: ULONG,
dwYCountChars: ULONG,
dwFillAttribute: ULONG,
dwFlags: ULONG,
dwShowWindow: ULONG,
WindowTitle: UNICODE_STRING,
Desktop: UNICODE_STRING,
ShellInfo: UNICODE_STRING,
RuntimeInfo: UNICODE_STRING,
DLCurrentDirectory: [0x20]RTL_DRIVE_LETTER_CURDIR,
};
pub const RTL_DRIVE_LETTER_CURDIR = extern struct {
Flags: c_ushort,
Length: c_ushort,
TimeStamp: ULONG,
DosPath: UNICODE_STRING,
};
pub const PPS_POST_PROCESS_INIT_ROUTINE = ?extern fn () void;
pub const FILE_BOTH_DIR_INFORMATION = extern struct {
NextEntryOffset: ULONG,
FileIndex: ULONG,
CreationTime: LARGE_INTEGER,
LastAccessTime: LARGE_INTEGER,
LastWriteTime: LARGE_INTEGER,
ChangeTime: LARGE_INTEGER,
EndOfFile: LARGE_INTEGER,
AllocationSize: LARGE_INTEGER,
FileAttributes: ULONG,
FileNameLength: ULONG,
EaSize: ULONG,
ShortNameLength: CHAR,
ShortName: [12]WCHAR,
FileName: [1]WCHAR,
};
pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION;
pub const IO_APC_ROUTINE = extern fn (PVOID, *IO_STATUS_BLOCK, ULONG) void;
pub const CURDIR = extern struct {
DosPath: UNICODE_STRING,
Handle: HANDLE,
};

View File

@ -13,12 +13,33 @@ pub extern "NtDll" stdcallcc fn NtCreateFile(
DesiredAccess: ACCESS_MASK,
ObjectAttributes: *OBJECT_ATTRIBUTES,
IoStatusBlock: *IO_STATUS_BLOCK,
AllocationSize: *LARGE_INTEGER,
AllocationSize: ?*LARGE_INTEGER,
FileAttributes: ULONG,
ShareAccess: ULONG,
CreateDisposition: ULONG,
CreateOptions: ULONG,
EaBuffer: *c_void,
EaBuffer: ?*c_void,
EaLength: ULONG,
) NTSTATUS;
pub extern "NtDll" stdcallcc fn NtClose(Handle: HANDLE) NTSTATUS;
pub extern "NtDll" stdcallcc fn RtlDosPathNameToNtPathName_U(
DosPathName: [*]const u16,
NtPathName: *UNICODE_STRING,
NtFileNamePart: ?*?[*]const u16,
DirectoryInfo: ?*CURDIR,
) BOOL;
pub extern "NtDll" stdcallcc fn RtlFreeUnicodeString(UnicodeString: *UNICODE_STRING) void;
pub extern "NtDll" stdcallcc fn NtQueryDirectoryFile(
FileHandle: HANDLE,
Event: ?HANDLE,
ApcRoutine: ?IO_APC_ROUTINE,
ApcContext: ?*c_void,
IoStatusBlock: *IO_STATUS_BLOCK,
FileInformation: *c_void,
Length: ULONG,
FileInformationClass: FILE_INFORMATION_CLASS,
ReturnSingleEntry: BOOLEAN,
FileName: ?*UNICODE_STRING,
RestartScan: BOOLEAN,
) NTSTATUS;

View File

@ -3,3 +3,9 @@ pub const SUCCESS = 0x00000000;
/// The data was too large to fit into the specified buffer.
pub const BUFFER_OVERFLOW = 0x80000005;
pub const INVALID_PARAMETER = 0xC000000D;
pub const ACCESS_DENIED = 0xC0000022;
pub const OBJECT_NAME_INVALID = 0xC0000033;
pub const OBJECT_NAME_NOT_FOUND = 0xC0000034;
pub const OBJECT_PATH_SYNTAX_BAD = 0xC000003B;