authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-21 14:18:01-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-21 14:18:01-04:00
logef67c497856fb97f7d2854991a72237ad4332810
tree02b133c51762e430b2718210733e16837318ddbb
parent5b1a492012241276a4b7539ca6664234f0629c79
signaturelock-open Commit is signed but in an unrecognized format.

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


10 files changed, 566 insertions(+), 106 deletions(-)

lib/std/fs.zig+267-98
...@@ -353,18 +353,13 @@ pub fn deleteTree(full_path: []const u8) !void {...@@ -353,18 +353,13 @@ pub fn deleteTree(full_path: []const u8) !void {
353353
354 return dir.deleteTree(path.basename(full_path));354 return dir.deleteTree(path.basename(full_path));
355 } else {355 } else {
356 return Dir.posix_cwd.deleteTree(full_path);356 return Dir.cwd().deleteTree(full_path);
357 }357 }
358}358}
359359
360pub const Dir = struct {360pub const Dir = struct {
361 fd: os.fd_t,361 fd: os.fd_t,
362362
363 /// An open handle to the current working directory.
364 /// Closing this directory is safety-checked illegal behavior.
365 /// Not available on Windows.
366 pub const posix_cwd = Dir{ .fd = os.AT_FDCWD };
367
368 pub const Entry = struct {363 pub const Entry = struct {
369 name: []const u8,364 name: []const u8,
370 kind: Kind,365 kind: Kind,
...@@ -386,12 +381,10 @@ pub const Dir = struct {...@@ -386,12 +381,10 @@ pub const Dir = struct {
386 .macosx, .ios, .freebsd, .netbsd => struct {381 .macosx, .ios, .freebsd, .netbsd => struct {
387 dir: Dir,382 dir: Dir,
388 seek: i64,383 seek: i64,
389 buf: [buffer_len]u8,384 buf: [8192]u8, // TODO align(@alignOf(os.dirent)),
390 index: usize,385 index: usize,
391 end_index: usize,386 end_index: usize,
392387
393 pub const buffer_len = 8192;
394
395 const Self = @This();388 const Self = @This();
396389
397 /// Memory such as file names referenced in this returned entry becomes invalid390 /// Memory such as file names referenced in this returned entry becomes invalid
...@@ -407,27 +400,24 @@ pub const Dir = struct {...@@ -407,27 +400,24 @@ pub const Dir = struct {
407 fn nextDarwin(self: *Self) !?Entry {400 fn nextDarwin(self: *Self) !?Entry {
408 start_over: while (true) {401 start_over: while (true) {
409 if (self.index >= self.end_index) {402 if (self.index >= self.end_index) {
410 while (true) {403 const rc = os.system.__getdirentries64(
411 const rc = os.system.__getdirentries64(404 self.dir.fd,
412 self.dir.fd,405 &self.buf,
413 &self.buf,406 self.buf.len,
414 self.buf.len,407 &self.seek,
415 &self.seek,408 );
416 );409 if (rc == 0) return null;
417 if (rc == 0) return null;410 if (rc < 0) {
418 if (rc < 0) {411 switch (os.errno(rc)) {
419 switch (os.errno(rc)) {412 os.EBADF => unreachable,
420 os.EBADF => unreachable,413 os.EFAULT => unreachable,
421 os.EFAULT => unreachable,414 os.ENOTDIR => unreachable,
422 os.ENOTDIR => unreachable,415 os.EINVAL => unreachable,
423 os.EINVAL => unreachable,416 else => |err| return os.unexpectedErrno(err),
424 else => |err| return os.unexpectedErrno(err),
425 }
426 }417 }
427 self.index = 0;
428 self.end_index = @intCast(usize, rc);
429 break;
430 }418 }
419 self.index = 0;
420 self.end_index = @intCast(usize, rc);
431 }421 }
432 const darwin_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);422 const darwin_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);
433 const next_index = self.index + darwin_entry.d_reclen;423 const next_index = self.index + darwin_entry.d_reclen;
...@@ -460,26 +450,23 @@ pub const Dir = struct {...@@ -460,26 +450,23 @@ pub const Dir = struct {
460 fn nextBsd(self: *Self) !?Entry {450 fn nextBsd(self: *Self) !?Entry {
461 start_over: while (true) {451 start_over: while (true) {
462 if (self.index >= self.end_index) {452 if (self.index >= self.end_index) {
463 while (true) {453 const rc = os.system.getdirentries(
464 const rc = os.system.getdirentries(454 self.dir.fd,
465 self.dir.fd,455 self.buf[0..].ptr,
466 self.buf[0..].ptr,456 self.buf.len,
467 self.buf.len,457 &self.seek,
468 &self.seek,458 );
469 );459 switch (os.errno(rc)) {
470 switch (os.errno(rc)) {460 0 => {},
471 0 => {},461 os.EBADF => unreachable,
472 os.EBADF => unreachable,462 os.EFAULT => unreachable,
473 os.EFAULT => unreachable,463 os.ENOTDIR => unreachable,
474 os.ENOTDIR => unreachable,464 os.EINVAL => unreachable,
475 os.EINVAL => unreachable,465 else => |err| return os.unexpectedErrno(err),
476 else => |err| return os.unexpectedErrno(err),
477 }
478 if (rc == 0) return null;
479 self.index = 0;
480 self.end_index = @intCast(usize, rc);
481 break;
482 }466 }
467 if (rc == 0) return null;
468 self.index = 0;
469 self.end_index = @intCast(usize, rc);
483 }470 }
484 const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);471 const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);
485 const next_index = self.index + freebsd_entry.d_reclen;472 const next_index = self.index + freebsd_entry.d_reclen;
...@@ -511,12 +498,10 @@ pub const Dir = struct {...@@ -511,12 +498,10 @@ pub const Dir = struct {
511 },498 },
512 .linux => struct {499 .linux => struct {
513 dir: Dir,500 dir: Dir,
514 buf: [buffer_len]u8,501 buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
515 index: usize,502 index: usize,
516 end_index: usize,503 end_index: usize,
517504
518 pub const buffer_len = 8192;
519
520 const Self = @This();505 const Self = @This();
521506
522 /// Memory such as file names referenced in this returned entry becomes invalid507 /// Memory such as file names referenced in this returned entry becomes invalid
...@@ -524,21 +509,18 @@ pub const Dir = struct {...@@ -524,21 +509,18 @@ pub const Dir = struct {
524 pub fn next(self: *Self) !?Entry {509 pub fn next(self: *Self) !?Entry {
525 start_over: while (true) {510 start_over: while (true) {
526 if (self.index >= self.end_index) {511 if (self.index >= self.end_index) {
527 while (true) {512 const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
528 const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len);513 switch (os.linux.getErrno(rc)) {
529 switch (os.linux.getErrno(rc)) {514 0 => {},
530 0 => {},515 os.EBADF => unreachable,
531 os.EBADF => unreachable,516 os.EFAULT => unreachable,
532 os.EFAULT => unreachable,517 os.ENOTDIR => unreachable,
533 os.ENOTDIR => unreachable,518 os.EINVAL => unreachable,
534 os.EINVAL => unreachable,519 else => |err| return os.unexpectedErrno(err),
535 else => |err| return os.unexpectedErrno(err),
536 }
537 if (rc == 0) return null;
538 self.index = 0;
539 self.end_index = rc;
540 break;
541 }520 }
521 if (rc == 0) return null;
522 self.index = 0;
523 self.end_index = rc;
542 }524 }
543 const linux_entry = @ptrCast(*align(1) os.dirent64, &self.buf[self.index]);525 const linux_entry = @ptrCast(*align(1) os.dirent64, &self.buf[self.index]);
544 const next_index = self.index + linux_entry.d_reclen;526 const next_index = self.index + linux_entry.d_reclen;
...@@ -570,13 +552,117 @@ pub const Dir = struct {...@@ -570,13 +552,117 @@ pub const Dir = struct {
570 },552 },
571 .windows => struct {553 .windows => struct {
572 dir: Dir,554 dir: Dir,
573 find_file_data: os.windows.WIN32_FIND_DATAW,555 buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),
556 index: usize,
557 end_index: usize,
574 first: bool,558 first: bool,
575 name_data: [256]u8,559 name_data: [256]u8,
560
561 const Self = @This();
562
563 pub fn next(self: *Self) !?Entry {
564 start_over: while (true) {
565 const w = os.windows;
566 if (self.index >= self.end_index) {
567 var io: w.IO_STATUS_BLOCK = undefined;
568 //var mask_buf = [2]u16{ 'a', 0 };
569 //var mask = w.UNICODE_STRING{
570 // .Length = 2,
571 // .MaximumLength = 2,
572 // .Buffer = &mask_buf,
573 //};
574 const rc = w.ntdll.NtQueryDirectoryFile(
575 self.dir.fd,
576 null,
577 null,
578 null,
579 &io,
580 &self.buf,
581 self.buf.len,
582 .FileBothDirectoryInformation,
583 w.FALSE,
584 null,
585 if (self.first) w.BOOLEAN(w.TRUE) else w.BOOLEAN(w.FALSE),
586 );
587 self.first = false;
588 if (io.Information == 0) return null;
589 self.index = 0;
590 self.end_index = io.Information;
591 switch (rc) {
592 w.STATUS.SUCCESS => {},
593 else => return w.unexpectedStatus(rc),
594 }
595 }
596
597 const aligned_ptr = @alignCast(@alignOf(w.FILE_BOTH_DIR_INFORMATION), &self.buf[self.index]);
598 const dir_info = @ptrCast(*w.FILE_BOTH_DIR_INFORMATION, aligned_ptr);
599 if (dir_info.NextEntryOffset != 0) {
600 self.index += dir_info.NextEntryOffset;
601 } else {
602 self.index = self.buf.len;
603 }
604
605 const name_utf16le = @ptrCast([*]u16, &dir_info.FileName)[0 .. dir_info.FileNameLength / 2];
606
607 if (mem.eql(u16, name_utf16le, [_]u16{'.'}) or mem.eql(u16, name_utf16le, [_]u16{ '.', '.' }))
608 continue;
609 // Trust that Windows gives us valid UTF-16LE
610 const name_utf8_len = std.unicode.utf16leToUtf8(self.name_data[0..], name_utf16le) catch unreachable;
611 const name_utf8 = self.name_data[0..name_utf8_len];
612 const kind = blk: {
613 const attrs = dir_info.FileAttributes;
614 if (attrs & w.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
615 if (attrs & w.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
616 break :blk Entry.Kind.File;
617 };
618 return Entry{
619 .name = name_utf8,
620 .kind = kind,
621 };
622 }
623 }
576 },624 },
577 else => @compileError("unimplemented"),625 else => @compileError("unimplemented"),
578 };626 };
579627
628 pub fn iterate(self: Dir) Iterator {
629 switch (builtin.os) {
630 .macosx, .ios, .freebsd, .netbsd => return Iterator{
631 .dir = self,
632 .seek = 0,
633 .index = 0,
634 .end_index = 0,
635 .buf = undefined,
636 },
637 .linux => return Iterator{
638 .dir = self,
639 .index = 0,
640 .end_index = 0,
641 .buf = undefined,
642 },
643 .windows => return Iterator{
644 .dir = self,
645 .index = 0,
646 .end_index = 0,
647 .first = true,
648 .buf = undefined,
649 .name_data = undefined,
650 },
651 else => @compileError("unimplemented"),
652 }
653 }
654
655 /// Returns an open handle to the current working directory.
656 /// Closing the returned `Dir` is checked illegal behavior.
657 /// On POSIX targets, this function is comptime-callable.
658 pub fn cwd() Dir {
659 if (os.windows.is_the_target) {
660 return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
661 } else {
662 return Dir{ .fd = os.AT_FDCWD };
663 }
664 }
665
580 pub const OpenError = error{666 pub const OpenError = error{
581 FileNotFound,667 FileNotFound,
582 NotDir,668 NotDir,
...@@ -594,18 +680,15 @@ pub const Dir = struct {...@@ -594,18 +680,15 @@ pub const Dir = struct {
594680
595 /// Call `close` to free the directory handle.681 /// Call `close` to free the directory handle.
596 pub fn open(dir_path: []const u8) OpenError!Dir {682 pub fn open(dir_path: []const u8) OpenError!Dir {
597 return posix_cwd.openDir(dir_path);683 return cwd().openDir(dir_path);
598 }684 }
599685
600 /// Same as `open` except the parameter is null-terminated.686 /// Same as `open` except the parameter is null-terminated.
601 pub fn openC(dir_path_c: [*]const u8) OpenError!Dir {687 pub fn openC(dir_path_c: [*]const u8) OpenError!Dir {
602 return posix_cwd.openDirC(dir_path_c);688 return cwd().openDirC(dir_path_c);
603 }689 }
604690
605 pub fn close(self: *Dir) void {691 pub fn close(self: *Dir) void {
606 if (os.windows.is_the_target) {
607 @panic("TODO");
608 }
609 os.close(self.fd);692 os.close(self.fd);
610 self.* = undefined;693 self.* = undefined;
611 }694 }
...@@ -625,14 +708,25 @@ pub const Dir = struct {...@@ -625,14 +708,25 @@ pub const Dir = struct {
625708
626 /// Call `close` on the result when done.709 /// Call `close` on the result when done.
627 pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {710 pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {
711 std.debug.warn("openDir {}\n", sub_path);
712 if (os.windows.is_the_target) {
713 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
714 return self.openDirW(&sub_path_w);
715 }
716
628 const sub_path_c = try os.toPosixPath(sub_path);717 const sub_path_c = try os.toPosixPath(sub_path);
629 return self.openDirC(&sub_path_c);718 return self.openDirC(&sub_path_c);
630 }719 }
631720
632 /// Call `close` on the result when done.721 /// Same as `openDir` except the parameter is null-terminated.
633 pub fn openDirC(self: Dir, sub_path: [*]const u8) OpenError!Dir {722 pub fn openDirC(self: Dir, sub_path_c: [*]const u8) OpenError!Dir {
723 if (os.windows.is_the_target) {
724 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
725 return self.openDirW(&sub_path_w);
726 }
727
634 const flags = os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC;728 const flags = os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC;
635 const fd = os.openatC(self.fd, sub_path, flags, 0) catch |err| switch (err) {729 const fd = os.openatC(self.fd, sub_path_c, flags, 0) catch |err| switch (err) {
636 error.FileTooBig => unreachable, // can't happen for directories730 error.FileTooBig => unreachable, // can't happen for directories
637 error.IsDir => unreachable, // we're providing O_DIRECTORY731 error.IsDir => unreachable, // we're providing O_DIRECTORY
638 error.NoSpaceLeft => unreachable, // not providing O_CREAT732 error.NoSpaceLeft => unreachable, // not providing O_CREAT
...@@ -642,6 +736,79 @@ pub const Dir = struct {...@@ -642,6 +736,79 @@ pub const Dir = struct {
642 return Dir{ .fd = fd };736 return Dir{ .fd = fd };
643 }737 }
644738
739 /// Same as `openDir` except the path parameter is UTF16LE, NT-prefixed.
740 /// This function is Windows-only.
741 pub fn openDirW(self: Dir, sub_path_w: [*]const u16) OpenError!Dir {
742 const w = os.windows;
743 var result = Dir{
744 .fd = undefined,
745 };
746 //var mask: ?[*]const u16 = undefined;
747 //var nt_name: w.UNICODE_STRING = undefined;
748 //if (w.ntdll.RtlDosPathNameToNtPathName_U(sub_path_w, &nt_name, null, null) == 0) {
749 // return error.FileNotFound;
750 //}
751 //defer w.ntdll.RtlFreeUnicodeString(&nt_name);
752 //if (mask) |m| {
753 // if (m[0] == 0) {
754 // return error.FileNotFound;
755 // } else {
756 // nt_name.Length = @intCast(u16, @ptrToInt(mask) - @ptrToInt(nt_name.Buffer));
757 // }
758 //} else {
759 // return error.FileNotFound;
760 //}
761
762 const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2);
763 std.debug.warn("path_len_bytes = {}\n", path_len_bytes);
764 var nt_name = w.UNICODE_STRING{
765 .Length = path_len_bytes,
766 .MaximumLength = path_len_bytes,
767 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
768 };
769 var attr = w.OBJECT_ATTRIBUTES{
770 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
771 .RootDirectory = if (path.isAbsoluteW(sub_path_w)) null else self.fd,
772 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
773 .ObjectName = &nt_name,
774 .SecurityDescriptor = null,
775 .SecurityQualityOfService = null,
776 };
777 std.debug.warn("RootDirectory = {}\n", attr.RootDirectory);
778 var io: w.IO_STATUS_BLOCK = undefined;
779 const wide_slice = nt_name.Buffer[0 .. nt_name.Length / 2];
780 //const wide_slice2 = std.mem.toSliceConst(u16, mask.?);
781 var buf: [200]u8 = undefined;
782 //var buf2: [200]u8 = undefined;
783 const len = std.unicode.utf16leToUtf8(&buf, wide_slice) catch unreachable;
784 //const len2 = std.unicode.utf16leToUtf8(&buf2, wide_slice2) catch unreachable;
785 std.debug.warn("path: {}\n", buf[0..len]);
786 //std.debug.warn("path: {}\nmask: {}\n", buf[0..len], buf2[0..len2]);
787 const rc = w.ntdll.NtCreateFile(
788 &result.fd,
789 w.GENERIC_READ | w.SYNCHRONIZE,
790 &attr,
791 &io,
792 null,
793 0,
794 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE,
795 w.FILE_OPEN,
796 w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT,
797 null,
798 0,
799 );
800 std.debug.warn("result.fd = {}\n", result.fd);
801 switch (rc) {
802 w.STATUS.SUCCESS => return result,
803 w.STATUS.OBJECT_NAME_INVALID => @panic("openDirW invalid object name"),
804 w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
805 w.STATUS.INVALID_PARAMETER => {
806 @panic("invalid parameter");
807 },
808 else => return w.unexpectedStatus(rc),
809 }
810 }
811
645 pub const DeleteFileError = os.UnlinkError;812 pub const DeleteFileError = os.UnlinkError;
646813
647 /// Delete a file name and possibly the file it refers to, based on an open directory handle.814 /// 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 {...@@ -677,6 +844,10 @@ pub const Dir = struct {
677 /// Returns `error.DirNotEmpty` if the directory is not empty.844 /// Returns `error.DirNotEmpty` if the directory is not empty.
678 /// To delete a directory recursively, see `deleteTree`.845 /// To delete a directory recursively, see `deleteTree`.
679 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {846 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
847 if (os.windows.is_the_target) {
848 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
849 return self.deleteDirW(&sub_path_w);
850 }
680 const sub_path_c = try os.toPosixPath(sub_path);851 const sub_path_c = try os.toPosixPath(sub_path);
681 return self.deleteDirC(&sub_path_c);852 return self.deleteDirC(&sub_path_c);
682 }853 }
...@@ -689,24 +860,25 @@ pub const Dir = struct {...@@ -689,24 +860,25 @@ pub const Dir = struct {
689 };860 };
690 }861 }
691862
692 pub fn iterate(self: Dir) Iterator {863 /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.
693 switch (builtin.os) {864 /// This function is Windows-only.
694 .macosx, .ios, .freebsd, .netbsd => return Iterator{865 pub fn deleteDirW(self: Dir, sub_path_w: [*]const u16) DeleteDirError!void {
695 .dir = self,866 os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {
696 .seek = 0,867 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
697 .index = 0,868 else => |e| return e,
698 .end_index = 0,869 };
699 .buf = undefined,870 }
700 },871
701 .linux => return Iterator{872 /// Read value of a symbolic link.
702 .dir = self,873 /// The return value is a slice of `buffer`, from index `0`.
703 .index = 0,874 pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
704 .end_index = 0,875 const sub_path_c = try os.toPosixPath(sub_path);
705 .buf = undefined,876 return self.readLinkC(&sub_path_c, buffer);
706 },877 }
707 .windows => @panic("TODO"),878
708 else => @compileError("unimplemented"),879 /// Same as `readLink`, except the `pathname` parameter is null-terminated.
709 }880 pub fn readLinkC(self: Dir, sub_path_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
881 return os.readlinkatC(self.fd, sub_path_c, buffer);
710 }882 }
711883
712 pub const DeleteTreeError = error{884 pub const DeleteTreeError = error{
...@@ -953,7 +1125,6 @@ pub const Walker = struct {...@@ -953,7 +1125,6 @@ pub const Walker = struct {
953/// Must call `Walker.deinit` when done.1125/// Must call `Walker.deinit` when done.
954/// `dir_path` must not end in a path separator.1126/// `dir_path` must not end in a path separator.
955/// The order of returned file system entries is undefined.1127/// The order of returned file system entries is undefined.
956/// TODO: https://github.com/ziglang/zig/issues/2888
957pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {1128pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
958 assert(!mem.endsWith(u8, dir_path, path.sep_str));1129 assert(!mem.endsWith(u8, dir_path, path.sep_str));
9591130
...@@ -978,15 +1149,13 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {...@@ -978,15 +1149,13 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
9781149
979/// Read value of a symbolic link.1150/// Read value of a symbolic link.
980/// The return value is a slice of buffer, from index `0`.1151/// The return value is a slice of buffer, from index `0`.
981/// TODO https://github.com/ziglang/zig/issues/28881152pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
982pub fn readLink(pathname: []const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
983 return os.readlink(pathname, buffer);1153 return os.readlink(pathname, buffer);
984}1154}
9851155
986/// Same as `readLink`, except the `pathname` parameter is null-terminated.1156/// Same as `readLink`, except the parameter is null-terminated.
987/// TODO https://github.com/ziglang/zig/issues/28881157pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
988pub fn readLinkC(pathname: [*]const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {1158 return os.readlinkC(pathname_c, buffer);
989 return os.readlinkC(pathname, buffer);
990}1159}
9911160
992pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError;1161pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError;
lib/std/fs/file.zig+1
...@@ -243,6 +243,7 @@ pub const File = struct {...@@ -243,6 +243,7 @@ pub const File = struct {
243 switch (rc) {243 switch (rc) {
244 windows.STATUS.SUCCESS => {},244 windows.STATUS.SUCCESS => {},
245 windows.STATUS.BUFFER_OVERFLOW => {},245 windows.STATUS.BUFFER_OVERFLOW => {},
246 windows.STATUS.INVALID_PARAMETER => unreachable,
246 else => return windows.unexpectedStatus(rc),247 else => return windows.unexpectedStatus(rc),
247 }248 }
248 return Stat{249 return Stat{
lib/std/fs/path.zig+19
...@@ -136,6 +136,25 @@ pub fn isAbsolute(path: []const u8) bool {...@@ -136,6 +136,25 @@ pub fn isAbsolute(path: []const u8) bool {
136 }136 }
137}137}
138138
139pub fn isAbsoluteW(path_w: [*]const u16) bool {
140 if (path_w[0] == '/')
141 return true;
142
143 if (path_w[0] == '\\') {
144 return true;
145 }
146 if (path_w[0] == 0 or path_w[1] == 0 or path_w[2] == 0) {
147 return false;
148 }
149 if (path_w[1] == ':') {
150 if (path_w[2] == '/')
151 return true;
152 if (path_w[2] == '\\')
153 return true;
154 }
155 return false;
156}
157
139pub fn isAbsoluteWindows(path: []const u8) bool {158pub fn isAbsoluteWindows(path: []const u8) bool {
140 if (path[0] == '/')159 if (path[0] == '/')
141 return true;160 return true;
lib/std/io.zig+4-1
...@@ -127,6 +127,7 @@ pub fn OutStream(comptime WriteError: type) type {...@@ -127,6 +127,7 @@ pub fn OutStream(comptime WriteError: type) type {
127 };127 };
128}128}
129129
130/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
130pub fn writeFile(path: []const u8, data: []const u8) !void {131pub fn writeFile(path: []const u8, data: []const u8) !void {
131 var file = try File.openWrite(path);132 var file = try File.openWrite(path);
132 defer file.close();133 defer file.close();
...@@ -134,11 +135,13 @@ pub fn writeFile(path: []const u8, data: []const u8) !void {...@@ -134,11 +135,13 @@ pub fn writeFile(path: []const u8, data: []const u8) !void {
134}135}
135136
136/// On success, caller owns returned buffer.137/// On success, caller owns returned buffer.
138/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
137pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {139pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
138 return readFileAllocAligned(allocator, path, @alignOf(u8));140 return readFileAllocAligned(allocator, path, @alignOf(u8));
139}141}
140142
141/// On success, caller owns returned buffer.143/// On success, caller owns returned buffer.
144/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
142pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {145pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {
143 var file = try File.openRead(path);146 var file = try File.openRead(path);
144 defer file.close();147 defer file.close();
...@@ -1084,7 +1087,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,...@@ -1084,7 +1087,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
1084 // safety. If it is bad, it will be caught anyway.1087 // safety. If it is bad, it will be caught anyway.
1085 const TagInt = @TagType(TagType);1088 const TagInt = @TagType(TagType);
1086 const tag = try self.deserializeInt(TagInt);1089 const tag = try self.deserializeInt(TagInt);
1087 1090
1088 inline for (info.fields) |field_info| {1091 inline for (info.fields) |field_info| {
1089 if (field_info.enum_field.?.value == tag) {1092 if (field_info.enum_field.?.value == tag) {
1090 const name = field_info.name;1093 const name = field_info.name;
lib/std/os.zig+79
...@@ -999,12 +999,20 @@ pub const UnlinkatError = UnlinkError || error{...@@ -999,12 +999,20 @@ pub const UnlinkatError = UnlinkError || error{
999999
1000/// Delete a file name and possibly the file it refers to, based on an open directory handle.1000/// Delete a file name and possibly the file it refers to, based on an open directory handle.
1001pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {1001pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
1002 if (windows.is_the_target) {
1003 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1004 return unlinkatW(dirfd, &file_path_w, flags);
1005 }
1002 const file_path_c = try toPosixPath(file_path);1006 const file_path_c = try toPosixPath(file_path);
1003 return unlinkatC(dirfd, &file_path_c, flags);1007 return unlinkatC(dirfd, &file_path_c, flags);
1004}1008}
10051009
1006/// Same as `unlinkat` but `file_path` is a null-terminated string.1010/// Same as `unlinkat` but `file_path` is a null-terminated string.
1007pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatError!void {1011pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatError!void {
1012 if (windows.is_the_target) {
1013 const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
1014 return unlinkatW(dirfd, &file_path_w, flags);
1015 }
1008 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {1016 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
1009 0 => return,1017 0 => return,
1010 EACCES => return error.AccessDenied,1018 EACCES => return error.AccessDenied,
...@@ -1028,6 +1036,56 @@ pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatErro...@@ -1028,6 +1036,56 @@ pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatErro
1028 }1036 }
1029}1037}
10301038
1039/// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.
1040pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*]const u16, flags: u32) UnlinkatError!void {
1041 const w = windows;
1042
1043 const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0;
1044 const create_options_flags = if (want_rmdir_behavior)
1045 w.ULONG(w.FILE_DELETE_ON_CLOSE)
1046 else
1047 w.ULONG(w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE);
1048 var nt_name: w.UNICODE_STRING = undefined;
1049 if (w.ntdll.RtlDosPathNameToNtPathName_U(sub_path_w, &nt_name, null, null) == 0) {
1050 return error.FileNotFound;
1051 }
1052 defer w.ntdll.RtlFreeUnicodeString(&nt_name);
1053
1054 var attr = w.OBJECT_ATTRIBUTES{
1055 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
1056 .RootDirectory = dirfd,
1057 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
1058 .ObjectName = &nt_name,
1059 .SecurityDescriptor = null,
1060 .SecurityQualityOfService = null,
1061 };
1062 var io: w.IO_STATUS_BLOCK = undefined;
1063 var tmp_handle: w.HANDLE = undefined;
1064 var rc = w.ntdll.NtCreateFile(
1065 &tmp_handle,
1066 w.SYNCHRONIZE | w.DELETE,
1067 &attr,
1068 &io,
1069 null,
1070 0,
1071 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE,
1072 w.FILE_OPEN,
1073 create_options_flags,
1074 null,
1075 0,
1076 );
1077 if (rc == w.STATUS.SUCCESS) {
1078 rc = w.ntdll.NtClose(tmp_handle);
1079 }
1080 switch (rc) {
1081 w.STATUS.SUCCESS => return,
1082 w.STATUS.OBJECT_NAME_INVALID => unreachable,
1083 w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
1084 w.STATUS.INVALID_PARAMETER => unreachable,
1085 else => return w.unexpectedStatus(rc),
1086 }
1087}
1088
1031const RenameError = error{1089const RenameError = error{
1032 AccessDenied,1090 AccessDenied,
1033 FileBusy,1091 FileBusy,
...@@ -1287,6 +1345,27 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {...@@ -1287,6 +1345,27 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
1287 }1345 }
1288}1346}
12891347
1348pub fn readlinkatC(dirfd: fd_t, file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
1349 if (windows.is_the_target) {
1350 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
1351 @compileError("TODO implement readlink for Windows");
1352 }
1353 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
1354 switch (errno(rc)) {
1355 0 => return out_buffer[0..@bitCast(usize, rc)],
1356 EACCES => return error.AccessDenied,
1357 EFAULT => unreachable,
1358 EINVAL => unreachable,
1359 EIO => return error.FileSystem,
1360 ELOOP => return error.SymLinkLoop,
1361 ENAMETOOLONG => return error.NameTooLong,
1362 ENOENT => return error.FileNotFound,
1363 ENOMEM => return error.SystemResources,
1364 ENOTDIR => return error.NotDir,
1365 else => |err| return unexpectedErrno(err),
1366 }
1367}
1368
1290pub const SetIdError = error{1369pub const SetIdError = error{
1291 ResourceLimitReached,1370 ResourceLimitReached,
1292 InvalidUserId,1371 InvalidUserId,
lib/std/os/bits/windows.zig+3
...@@ -158,3 +158,6 @@ pub const EWOULDBLOCK = 140;...@@ -158,3 +158,6 @@ pub const EWOULDBLOCK = 140;
158pub const EDQUOT = 10069;158pub const EDQUOT = 10069;
159159
160pub const F_OK = 0;160pub const F_OK = 0;
161
162/// Remove directory instead of unlinking file
163pub const AT_REMOVEDIR = 0x200;
lib/std/os/windows.zig+21-2
...@@ -792,6 +792,25 @@ pub fn SetFileTime(...@@ -792,6 +792,25 @@ pub fn SetFileTime(
792 }792 }
793}793}
794794
795pub fn peb() *PEB {
796 switch (builtin.arch) {
797 .i386 => {
798 return asm (
799 \\ mov %%fs:0x18, %[ptr]
800 \\ mov %%ds:0x30(%[ptr]), %[ptr]
801 : [ptr] "=r" (-> *PEB)
802 );
803 },
804 .x86_64 => {
805 return asm (
806 \\ mov %%gs:0x60, %[ptr]
807 : [ptr] "=r" (-> *PEB)
808 );
809 },
810 else => @compileError("unsupported architecture"),
811 }
812}
813
795/// A file time is a 64-bit value that represents the number of 100-nanosecond814/// A file time is a 64-bit value that represents the number of 100-nanosecond
796/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated815/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated
797/// Universal Time (UTC).816/// Universal Time (UTC).
...@@ -844,8 +863,8 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16)...@@ -844,8 +863,8 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16)
844 else => {},863 else => {},
845 }864 }
846 }865 }
847 const start_index = if (mem.startsWith(u8, s, "\\\\") or !std.fs.path.isAbsolute(s)) 0 else blk: {866 const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: {
848 const prefix = [_]u16{ '\\', '\\', '?', '\\' };867 const prefix = [_]u16{ '\\', '?', '?', '\\' };
849 mem.copy(u16, result[0..], prefix);868 mem.copy(u16, result[0..], prefix);
850 break :blk prefix.len;869 break :blk prefix.len;
851 };870 };
lib/std/os/windows/bits.zig+143-3
...@@ -300,6 +300,44 @@ pub const FILE_SHARE_DELETE = 0x00000004;...@@ -300,6 +300,44 @@ pub const FILE_SHARE_DELETE = 0x00000004;
300pub const FILE_SHARE_READ = 0x00000001;300pub const FILE_SHARE_READ = 0x00000001;
301pub const FILE_SHARE_WRITE = 0x00000002;301pub const FILE_SHARE_WRITE = 0x00000002;
302302
303pub const DELETE = 0x00010000;
304pub const READ_CONTROL = 0x00020000;
305pub const WRITE_DAC = 0x00040000;
306pub const WRITE_OWNER = 0x00080000;
307pub const SYNCHRONIZE = 0x00100000;
308pub const STANDARD_RIGHTS_REQUIRED = 0x000f0000;
309
310// disposition for NtCreateFile
311pub const FILE_SUPERSEDE = 0;
312pub const FILE_OPEN = 1;
313pub const FILE_CREATE = 2;
314pub const FILE_OPEN_IF = 3;
315pub const FILE_OVERWRITE = 4;
316pub const FILE_OVERWRITE_IF = 5;
317pub const FILE_MAXIMUM_DISPOSITION = 5;
318
319// flags for NtCreateFile and NtOpenFile
320pub const FILE_DIRECTORY_FILE = 0x00000001;
321pub const FILE_WRITE_THROUGH = 0x00000002;
322pub const FILE_SEQUENTIAL_ONLY = 0x00000004;
323pub const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008;
324pub const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010;
325pub const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;
326pub const FILE_NON_DIRECTORY_FILE = 0x00000040;
327pub const FILE_CREATE_TREE_CONNECTION = 0x00000080;
328pub const FILE_COMPLETE_IF_OPLOCKED = 0x00000100;
329pub const FILE_NO_EA_KNOWLEDGE = 0x00000200;
330pub const FILE_OPEN_FOR_RECOVERY = 0x00000400;
331pub const FILE_RANDOM_ACCESS = 0x00000800;
332pub const FILE_DELETE_ON_CLOSE = 0x00001000;
333pub const FILE_OPEN_BY_FILE_ID = 0x00002000;
334pub const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000;
335pub const FILE_NO_COMPRESSION = 0x00008000;
336pub const FILE_RESERVE_OPFILTER = 0x00100000;
337pub const FILE_TRANSACTED_MODE = 0x00200000;
338pub const FILE_OPEN_OFFLINE_FILE = 0x00400000;
339pub const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000;
340
303pub const CREATE_ALWAYS = 2;341pub const CREATE_ALWAYS = 2;
304pub const CREATE_NEW = 1;342pub const CREATE_NEW = 1;
305pub const OPEN_ALWAYS = 4;343pub const OPEN_ALWAYS = 4;
...@@ -720,15 +758,117 @@ pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_P...@@ -720,15 +758,117 @@ pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_P
720758
721pub const OBJECT_ATTRIBUTES = extern struct {759pub const OBJECT_ATTRIBUTES = extern struct {
722 Length: ULONG,760 Length: ULONG,
723 RootDirectory: HANDLE,761 RootDirectory: ?HANDLE,
724 ObjectName: *UNICODE_STRING,762 ObjectName: *UNICODE_STRING,
725 Attributes: ULONG,763 Attributes: ULONG,
726 SecurityDescriptor: ?*c_void,764 SecurityDescriptor: ?*c_void,
727 SecurityQualityOfService: ?*c_void,765 SecurityQualityOfService: ?*c_void,
728};766};
729767
768pub const OBJ_INHERIT = 0x00000002;
769pub const OBJ_PERMANENT = 0x00000010;
770pub const OBJ_EXCLUSIVE = 0x00000020;
771pub const OBJ_CASE_INSENSITIVE = 0x00000040;
772pub const OBJ_OPENIF = 0x00000080;
773pub const OBJ_OPENLINK = 0x00000100;
774pub const OBJ_KERNEL_HANDLE = 0x00000200;
775pub const OBJ_VALID_ATTRIBUTES = 0x000003F2;
776
730pub const UNICODE_STRING = extern struct {777pub const UNICODE_STRING = extern struct {
731 Length: USHORT,778 Length: c_ushort,
732 MaximumLength: USHORT,779 MaximumLength: c_ushort,
733 Buffer: [*]WCHAR,780 Buffer: [*]WCHAR,
734};781};
782
783pub const PEB = extern struct {
784 Reserved1: [2]BYTE,
785 BeingDebugged: BYTE,
786 Reserved2: [1]BYTE,
787 Reserved3: [2]PVOID,
788 Ldr: *PEB_LDR_DATA,
789 ProcessParameters: *RTL_USER_PROCESS_PARAMETERS,
790 Reserved4: [3]PVOID,
791 AtlThunkSListPtr: PVOID,
792 Reserved5: PVOID,
793 Reserved6: ULONG,
794 Reserved7: PVOID,
795 Reserved8: ULONG,
796 AtlThunkSListPtr32: ULONG,
797 Reserved9: [45]PVOID,
798 Reserved10: [96]BYTE,
799 PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE,
800 Reserved11: [128]BYTE,
801 Reserved12: [1]PVOID,
802 SessionId: ULONG,
803};
804
805pub const PEB_LDR_DATA = extern struct {
806 Reserved1: [8]BYTE,
807 Reserved2: [3]PVOID,
808 InMemoryOrderModuleList: LIST_ENTRY,
809};
810
811pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
812 AllocationSize: ULONG,
813 Size: ULONG,
814 Flags: ULONG,
815 DebugFlags: ULONG,
816 ConsoleHandle: HANDLE,
817 ConsoleFlags: ULONG,
818 hStdInput: HANDLE,
819 hStdOutput: HANDLE,
820 hStdError: HANDLE,
821 CurrentDirectory: CURDIR,
822 DllPath: UNICODE_STRING,
823 ImagePathName: UNICODE_STRING,
824 CommandLine: UNICODE_STRING,
825 Environment: [*]WCHAR,
826 dwX: ULONG,
827 dwY: ULONG,
828 dwXSize: ULONG,
829 dwYSize: ULONG,
830 dwXCountChars: ULONG,
831 dwYCountChars: ULONG,
832 dwFillAttribute: ULONG,
833 dwFlags: ULONG,
834 dwShowWindow: ULONG,
835 WindowTitle: UNICODE_STRING,
836 Desktop: UNICODE_STRING,
837 ShellInfo: UNICODE_STRING,
838 RuntimeInfo: UNICODE_STRING,
839 DLCurrentDirectory: [0x20]RTL_DRIVE_LETTER_CURDIR,
840};
841
842pub const RTL_DRIVE_LETTER_CURDIR = extern struct {
843 Flags: c_ushort,
844 Length: c_ushort,
845 TimeStamp: ULONG,
846 DosPath: UNICODE_STRING,
847};
848
849pub const PPS_POST_PROCESS_INIT_ROUTINE = ?extern fn () void;
850
851pub const FILE_BOTH_DIR_INFORMATION = extern struct {
852 NextEntryOffset: ULONG,
853 FileIndex: ULONG,
854 CreationTime: LARGE_INTEGER,
855 LastAccessTime: LARGE_INTEGER,
856 LastWriteTime: LARGE_INTEGER,
857 ChangeTime: LARGE_INTEGER,
858 EndOfFile: LARGE_INTEGER,
859 AllocationSize: LARGE_INTEGER,
860 FileAttributes: ULONG,
861 FileNameLength: ULONG,
862 EaSize: ULONG,
863 ShortNameLength: CHAR,
864 ShortName: [12]WCHAR,
865 FileName: [1]WCHAR,
866};
867pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION;
868
869pub const IO_APC_ROUTINE = extern fn (PVOID, *IO_STATUS_BLOCK, ULONG) void;
870
871pub const CURDIR = extern struct {
872 DosPath: UNICODE_STRING,
873 Handle: HANDLE,
874};
lib/std/os/windows/ntdll.zig+23-2
...@@ -13,12 +13,33 @@ pub extern "NtDll" stdcallcc fn NtCreateFile(...@@ -13,12 +13,33 @@ pub extern "NtDll" stdcallcc fn NtCreateFile(
13 DesiredAccess: ACCESS_MASK,13 DesiredAccess: ACCESS_MASK,
14 ObjectAttributes: *OBJECT_ATTRIBUTES,14 ObjectAttributes: *OBJECT_ATTRIBUTES,
15 IoStatusBlock: *IO_STATUS_BLOCK,15 IoStatusBlock: *IO_STATUS_BLOCK,
16 AllocationSize: *LARGE_INTEGER,16 AllocationSize: ?*LARGE_INTEGER,
17 FileAttributes: ULONG,17 FileAttributes: ULONG,
18 ShareAccess: ULONG,18 ShareAccess: ULONG,
19 CreateDisposition: ULONG,19 CreateDisposition: ULONG,
20 CreateOptions: ULONG,20 CreateOptions: ULONG,
21 EaBuffer: *c_void,21 EaBuffer: ?*c_void,
22 EaLength: ULONG,22 EaLength: ULONG,
23) NTSTATUS;23) NTSTATUS;
24pub extern "NtDll" stdcallcc fn NtClose(Handle: HANDLE) NTSTATUS;24pub extern "NtDll" stdcallcc fn NtClose(Handle: HANDLE) NTSTATUS;
25pub extern "NtDll" stdcallcc fn RtlDosPathNameToNtPathName_U(
26 DosPathName: [*]const u16,
27 NtPathName: *UNICODE_STRING,
28 NtFileNamePart: ?*?[*]const u16,
29 DirectoryInfo: ?*CURDIR,
30) BOOL;
31pub extern "NtDll" stdcallcc fn RtlFreeUnicodeString(UnicodeString: *UNICODE_STRING) void;
32
33pub extern "NtDll" stdcallcc fn NtQueryDirectoryFile(
34 FileHandle: HANDLE,
35 Event: ?HANDLE,
36 ApcRoutine: ?IO_APC_ROUTINE,
37 ApcContext: ?*c_void,
38 IoStatusBlock: *IO_STATUS_BLOCK,
39 FileInformation: *c_void,
40 Length: ULONG,
41 FileInformationClass: FILE_INFORMATION_CLASS,
42 ReturnSingleEntry: BOOLEAN,
43 FileName: ?*UNICODE_STRING,
44 RestartScan: BOOLEAN,
45) NTSTATUS;
lib/std/os/windows/status.zig+6
...@@ -3,3 +3,9 @@ pub const SUCCESS = 0x00000000;...@@ -3,3 +3,9 @@ pub const SUCCESS = 0x00000000;
33
4/// The data was too large to fit into the specified buffer.4/// The data was too large to fit into the specified buffer.
5pub const BUFFER_OVERFLOW = 0x80000005;5pub const BUFFER_OVERFLOW = 0x80000005;
6
7pub const INVALID_PARAMETER = 0xC000000D;
8pub const ACCESS_DENIED = 0xC0000022;
9pub const OBJECT_NAME_INVALID = 0xC0000033;
10pub const OBJECT_NAME_NOT_FOUND = 0xC0000034;
11pub const OBJECT_PATH_SYNTAX_BAD = 0xC000003B;