authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-30 17:50:49+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-31 16:31:44+02:00
log66bbe4ec4c11839217d6a9d65771d60d45cd6bc1
treecebd5ba00b67e673e8a68e7d7f87aff986788c31
parenta89d5cfc3eaaf97b914abc5d355099ec8357925d

Refactor internal Win routines to reuse OpenFile

This covers mainly `ReadLink` and `CreateSymolicLink` functions.

4 files changed, 83 insertions(+), 186 deletions(-)

lib/std/fs.zig+16-13
......@@ -1261,11 +1261,11 @@ pub const Dir = struct {
12611261 /// are null-terminated, WTF16 encoded.
12621262 pub fn symLinkW(
12631263 self: Dir,
1264 target_path_w: [:0]const u16,
1265 sym_link_path_w: [:0]const u16,
1264 target_path_w: []const u16,
1265 sym_link_path_w: []const u16,
12661266 flags: SymLinkFlags,
12671267 ) !void {
1268 return os.windows.CreateSymbolicLinkW(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
1268 return os.windows.CreateSymbolicLink(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
12691269 }
12701270
12711271 /// Read value of a symbolic link.
......@@ -1276,7 +1276,8 @@ pub const Dir = struct {
12761276 return self.readLinkWasi(sub_path, buffer);
12771277 }
12781278 if (builtin.os.tag == .windows) {
1279 return os.windows.ReadLink(self.fd, sub_path, buffer);
1279 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1280 return self.readLinkW(sub_path_w.span(), buffer);
12801281 }
12811282 const sub_path_c = try os.toPosixPath(sub_path);
12821283 return self.readLinkZ(&sub_path_c, buffer);
......@@ -1293,15 +1294,15 @@ pub const Dir = struct {
12931294 pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
12941295 if (builtin.os.tag == .windows) {
12951296 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
1296 return self.readLinkW(sub_path_w, buffer);
1297 return self.readLinkW(sub_path_w.span(), buffer);
12971298 }
12981299 return os.readlinkatZ(self.fd, sub_path_c, buffer);
12991300 }
13001301
13011302 /// Windows-only. Same as `readLink` except the pathname parameter
13021303 /// is null-terminated, WTF16 encoded.
1303 pub fn readLinkW(self: Dir, sub_path_w: [*:0]const u16, buffer: []u8) ![]u8 {
1304 return os.windows.ReadLinkW(self.fd, sub_path_w, buffer);
1304 pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
1305 return os.windows.ReadLink(self.fd, sub_path_w, buffer);
13051306 }
13061307
13071308 /// On success, caller owns returned buffer.
......@@ -1811,7 +1812,9 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
18111812 assert(path.isAbsolute(target_path));
18121813 assert(path.isAbsolute(sym_link_path));
18131814 if (builtin.os.tag == .windows) {
1814 return os.windows.CreateSymbolicLink(null, sym_link_path, target_path, flags.is_directory);
1815 const target_path_w = try os.windows.sliceToPrefixedFileW(target_path);
1816 const sym_link_path_w = try os.windows.sliceToPrefixedFileW(sym_link_path);
1817 return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
18151818 }
18161819 return os.symlink(target_path, sym_link_path);
18171820}
......@@ -1820,10 +1823,10 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
18201823/// Note that this function will by default try creating a symbolic link to a file. If you would
18211824/// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`.
18221825/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.
1823pub fn symLinkAbsoluteW(target_path_w: [:0]const u16, sym_link_path_w: [:0]const u16, flags: SymLinkFlags) !void {
1824 assert(path.isAbsoluteWindowsW(target_path_w));
1825 assert(path.isAbsoluteWindowsW(sym_link_path_w));
1826 return os.windows.CreateSymbolicLinkW(null, sym_link_path_w, target_path_w, flags.is_directory);
1826pub fn symLinkAbsoluteW(target_path_w: []const u16, sym_link_path_w: []const u16, flags: SymLinkFlags) !void {
1827 assert(path.isAbsoluteWindowsWTF16(target_path_w));
1828 assert(path.isAbsoluteWindowsWTF16(sym_link_path_w));
1829 return os.windows.CreateSymbolicLink(null, sym_link_path_w, target_path_w, flags.is_directory);
18271830}
18281831
18291832/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.
......@@ -1834,7 +1837,7 @@ pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]cons
18341837 if (builtin.os.tag == .windows) {
18351838 const target_path_w = try os.windows.cStrToWin32PrefixedFileW(target_path_c);
18361839 const sym_link_path_w = try os.windows.cStrToWin32PrefixedFileW(sym_link_path_c);
1837 return os.windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, flags.is_directory);
1840 return os.windows.CreateSymbolicLink(sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
18381841 }
18391842 return os.symlinkZ(target_path_c, sym_link_path_c);
18401843}
lib/std/os.zig+25-23
......@@ -2087,7 +2087,7 @@ pub fn renameatW(
20872087pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
20882088 if (builtin.os.tag == .windows) {
20892089 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
2090 return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);
2090 return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
20912091 } else if (builtin.os.tag == .wasi) {
20922092 return mkdiratWasi(dir_fd, sub_dir_path, mode);
20932093 } else {
......@@ -2145,13 +2145,13 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr
21452145 }
21462146}
21472147
2148pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
2149 const sub_dir_handle = windows.OpenFile(std.mem.spanZ(sub_path_w), .{
2148pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
2149 const sub_dir_handle = windows.OpenFile(sub_path_w, .{
21502150 .dir = dir_fd,
21512151 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
21522152 .creation = windows.FILE_CREATE,
21532153 .io_mode = .blocking,
2154 .expect_dir = true,
2154 .open_dir = true,
21552155 }) catch |err| switch (err) {
21562156 error.IsDir => unreachable,
21572157 error.PipeBusy => unreachable,
......@@ -2187,7 +2187,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
21872187 @compileError("mkdir is not supported in WASI; use mkdirat instead");
21882188 } else if (builtin.os.tag == .windows) {
21892189 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
2190 return mkdirW(dir_path_w.span().ptr, mode);
2190 return mkdirW(dir_path_w.span(), mode);
21912191 } else {
21922192 const dir_path_c = try toPosixPath(dir_path);
21932193 return mkdirZ(&dir_path_c, mode);
......@@ -2198,7 +2198,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
21982198pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
21992199 if (builtin.os.tag == .windows) {
22002200 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
2201 return mkdirW(dir_path_w.span().ptr, mode);
2201 return mkdirW(dir_path_w.span(), mode);
22022202 }
22032203 switch (errno(system.mkdir(dir_path, mode))) {
22042204 0 => return,
......@@ -2220,13 +2220,13 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
22202220}
22212221
22222222/// Windows-only. Same as `mkdir` but the parameters is null-terminated, WTF16 encoded.
2223pub fn mkdirW(dir_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
2224 const sub_dir_handle = windows.OpenFile(std.mem.spanZ(dir_path_w), .{
2223pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
2224 const sub_dir_handle = windows.OpenFile(dir_path_w, .{
22252225 .dir = std.fs.cwd().fd,
22262226 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
22272227 .creation = windows.FILE_CREATE,
22282228 .io_mode = .blocking,
2229 .expect_dir = true,
2229 .open_dir = true,
22302230 }) catch |err| switch (err) {
22312231 error.IsDir => unreachable,
22322232 error.PipeBusy => unreachable,
......@@ -2379,7 +2379,8 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
23792379 if (builtin.os.tag == .wasi) {
23802380 @compileError("readlink is not supported in WASI; use readlinkat instead");
23812381 } else if (builtin.os.tag == .windows) {
2382 return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
2382 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
2383 return readlinkW(file_path_w.span(), out_buffer);
23832384 } else {
23842385 const file_path_c = try toPosixPath(file_path);
23852386 return readlinkZ(&file_path_c, out_buffer);
......@@ -2390,15 +2391,15 @@ pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");
23902391
23912392/// Windows-only. Same as `readlink` except `file_path` is null-terminated, WTF16 encoded.
23922393/// See also `readlinkZ`.
2393pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2394 return windows.ReadLinkW(std.fs.cwd().fd, file_path, out_buffer);
2394pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
2395 return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
23952396}
23962397
23972398/// Same as `readlink` except `file_path` is null-terminated.
23982399pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
23992400 if (builtin.os.tag == .windows) {
24002401 const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);
2401 return readlinkW(file_path_w.span().ptr, out_buffer);
2402 return readlinkW(file_path_w.span(), out_buffer);
24022403 }
24032404 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
24042405 switch (errno(rc)) {
......@@ -2424,7 +2425,8 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink
24242425 return readlinkatWasi(dirfd, file_path, out_buffer);
24252426 }
24262427 if (builtin.os.tag == .windows) {
2427 return windows.ReadLink(dirfd, file_path, out_buffer);
2428 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
2429 return readlinkatW(dirfd, file_path_w.span(), out_buffer);
24282430 }
24292431 const file_path_c = try toPosixPath(file_path);
24302432 return readlinkatZ(dirfd, &file_path_c, out_buffer);
......@@ -2454,8 +2456,8 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read
24542456
24552457/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
24562458/// See also `readlinkat`.
2457pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2458 return windows.ReadLinkW(dirfd, file_path, out_buffer);
2459pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
2460 return windows.ReadLink(dirfd, file_path, out_buffer);
24592461}
24602462
24612463/// Same as `readlinkat` except `file_path` is null-terminated.
......@@ -2463,7 +2465,7 @@ pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) Rea
24632465pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
24642466 if (builtin.os.tag == .windows) {
24652467 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2466 return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer);
2468 return readlinkatW(dirfd, file_path_w.span(), out_buffer);
24672469 }
24682470 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
24692471 switch (errno(rc)) {
......@@ -3984,7 +3986,7 @@ pub const RealPathError = error{
39843986pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
39853987 if (builtin.os.tag == .windows) {
39863988 const pathname_w = try windows.sliceToPrefixedFileW(pathname);
3987 return realpathW(pathname_w.span().ptr, out_buffer);
3989 return realpathW(pathname_w.span(), out_buffer);
39883990 }
39893991 if (builtin.os.tag == .wasi) {
39903992 @compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths");
......@@ -3999,7 +4001,7 @@ pub const realpathC = @compileError("deprecated: renamed realpathZ");
39994001pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
40004002 if (builtin.os.tag == .windows) {
40014003 const pathname_w = try windows.cStrToPrefixedFileW(pathname);
4002 return realpathW(pathname_w.span().ptr, out_buffer);
4004 return realpathW(pathname_w.span(), out_buffer);
40034005 }
40044006 if (builtin.os.tag == .linux and !builtin.link_libc) {
40054007 const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) {
......@@ -4037,7 +4039,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
40374039
40384040/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.
40394041/// TODO use ntdll for better semantics
4040pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4042pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
40414043 const w = windows;
40424044
40434045 const dir = std.fs.cwd().fd;
......@@ -4045,20 +4047,20 @@ pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) Real
40454047 const share_access = w.FILE_SHARE_READ;
40464048 const creation = w.FILE_OPEN;
40474049 const h_file = blk: {
4048 const res = w.OpenFile(std.mem.spanZ(pathname), .{
4050 const res = w.OpenFile(pathname, .{
40494051 .dir = dir,
40504052 .access_mask = access_mask,
40514053 .share_access = share_access,
40524054 .creation = creation,
40534055 .io_mode = .blocking,
40544056 }) catch |err| switch (err) {
4055 error.IsDir => break :blk w.OpenFile(std.mem.spanZ(pathname), .{
4057 error.IsDir => break :blk w.OpenFile(pathname, .{
40564058 .dir = dir,
40574059 .access_mask = access_mask,
40584060 .share_access = share_access,
40594061 .creation = creation,
40604062 .io_mode = .blocking,
4061 .expect_dir = true,
4063 .open_dir = true,
40624064 }) catch |er| switch (er) {
40634065 error.WouldBlock => unreachable,
40644066 else => |e2| return e2,
lib/std/os/test.zig+2-2
......@@ -27,7 +27,7 @@ test "symlink with relative paths" {
2727 try cwd.writeFile("file.txt", "nonsense");
2828
2929 if (builtin.os.tag == .windows) {
30 try os.windows.CreateSymbolicLink(cwd.fd, "symlinked", "file.txt", false);
30 try os.windows.CreateSymbolicLink(cwd.fd, &[_]u16{ 's', 'y', 'm', 'l', 'i', 'n', 'k', 'e', 'd' }, &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, false);
3131 } else {
3232 try os.symlink("file.txt", "symlinked");
3333 }
......@@ -85,7 +85,7 @@ test "readlinkat" {
8585
8686 // create a symbolic link
8787 if (builtin.os.tag == .windows) {
88 try os.windows.CreateSymbolicLink(tmp.dir.fd, "link", "file.txt", false);
88 try os.windows.CreateSymbolicLink(tmp.dir.fd, &[_]u16{ 'l', 'i', 'n', 'k' }, &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, false);
8989 } else {
9090 try os.symlinkat("file.txt", tmp.dir.fd, "link");
9191 }
lib/std/os/windows.zig+40-148
......@@ -69,16 +69,21 @@ pub const OpenFileOptions = struct {
6969 share_access_nonblocking: bool = false,
7070 creation: ULONG,
7171 io_mode: std.io.ModeOverride,
72 expect_dir: bool = false,
72 /// If true, tries to open path as a directory.
73 /// Defaults to false.
74 open_dir: bool = false,
75 /// If false, tries to open path as a reparse point without dereferencing it.
76 /// Defaults to true.
77 follow_symlinks: bool = true,
7378};
7479
7580/// TODO when share_access_nonblocking is false, this implementation uses
7681/// untinterruptible sleep() to block. This is not the final iteration of the API.
7782pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
78 if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.expect_dir) {
83 if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir and options.follow_symlinks) {
7984 return error.IsDir;
8085 }
81 if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.expect_dir) {
86 if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir and options.follow_symlinks) {
8287 return error.IsDir;
8388 }
8489
......@@ -105,8 +110,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
105110 var delay: usize = 1;
106111 while (true) {
107112 const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
108 const file_or_dir_flag: ULONG = if (options.expect_dir) FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT else FILE_NON_DIRECTORY_FILE;
109 const flags: ULONG = file_or_dir_flag | blocking_flag;
113 const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT else FILE_NON_DIRECTORY_FILE;
114 const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else FILE_OPEN_REPARSE_POINT;
110115 const rc = ntdll.NtCreateFile(
111116 &result,
112117 options.access_mask,
......@@ -143,7 +148,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
143148 .PIPE_BUSY => return error.PipeBusy,
144149 .OBJECT_PATH_SYNTAX_BAD => unreachable,
145150 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
146 .FILE_IS_A_DIRECTORY => if (options.expect_dir) unreachable else return error.IsDir,
151 .FILE_IS_A_DIRECTORY => if (options.open_dir) unreachable else return error.IsDir,
147152 else => return unexpectedStatus(rc),
148153 }
149154 }
......@@ -598,27 +603,14 @@ pub const CreateSymbolicLinkError = error{
598603 PathAlreadyExists,
599604 FileNotFound,
600605 NameTooLong,
601 InvalidUtf8,
602 BadPathName,
603606 NoDevice,
604607 Unexpected,
605608};
606609
607610pub fn CreateSymbolicLink(
608611 dir: ?HANDLE,
609 sym_link_path: []const u8,
610 target_path: []const u8,
611 is_directory: bool,
612) CreateSymbolicLinkError!void {
613 const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path);
614 const target_path_w = try sliceToPrefixedFileW(target_path);
615 return CreateSymbolicLinkW(dir, sym_link_path_w.span(), target_path_w.span(), is_directory);
616}
617
618pub fn CreateSymbolicLinkW(
619 dir: ?HANDLE,
620 sym_link_path: [:0]const u16,
621 target_path: [:0]const u16,
612 sym_link_path: []const u16,
613 target_path: []const u16,
622614 is_directory: bool,
623615) CreateSymbolicLinkError!void {
624616 const SYMLINK_DATA = extern struct {
......@@ -632,70 +624,18 @@ pub fn CreateSymbolicLinkW(
632624 Flags: ULONG,
633625 };
634626
635 var symlink_handle: HANDLE = undefined;
636 if (is_directory) {
637 const sym_link_len_bytes = math.cast(u16, sym_link_path.len * 2) catch |err| switch (err) {
638 error.Overflow => return error.NameTooLong,
639 };
640 var nt_name = UNICODE_STRING{
641 .Length = sym_link_len_bytes,
642 .MaximumLength = sym_link_len_bytes,
643 .Buffer = @intToPtr([*]u16, @ptrToInt(sym_link_path.ptr)),
644 };
645
646 if (sym_link_path[0] == '.' and sym_link_path[1] == 0) {
647 // Windows does not recognize this, but it does work with empty string.
648 nt_name.Length = 0;
649 }
650
651 var attr = OBJECT_ATTRIBUTES{
652 .Length = @sizeOf(OBJECT_ATTRIBUTES),
653 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sym_link_path)) null else dir,
654 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
655 .ObjectName = &nt_name,
656 .SecurityDescriptor = null,
657 .SecurityQualityOfService = null,
658 };
659
660 var io: IO_STATUS_BLOCK = undefined;
661 const rc = ntdll.NtCreateFile(
662 &symlink_handle,
663 GENERIC_READ | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
664 &attr,
665 &io,
666 null,
667 FILE_ATTRIBUTE_NORMAL,
668 FILE_SHARE_READ,
669 FILE_CREATE,
670 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
671 null,
672 0,
673 );
674 switch (rc) {
675 .SUCCESS => {},
676 .OBJECT_NAME_INVALID => unreachable,
677 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
678 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
679 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
680 .INVALID_PARAMETER => unreachable,
681 .ACCESS_DENIED => return error.AccessDenied,
682 .OBJECT_PATH_SYNTAX_BAD => unreachable,
683 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
684 else => return unexpectedStatus(rc),
685 }
686 } else {
687 symlink_handle = OpenFile(sym_link_path, .{
688 .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
689 .dir = dir,
690 .creation = FILE_CREATE,
691 .io_mode = .blocking,
692 }) catch |err| switch (err) {
693 error.WouldBlock => unreachable,
694 error.IsDir => return error.PathAlreadyExists,
695 error.PipeBusy => unreachable,
696 else => |e| return e,
697 };
698 }
627 const symlink_handle = OpenFile(sym_link_path, .{
628 .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
629 .dir = dir,
630 .creation = FILE_CREATE,
631 .io_mode = .blocking,
632 .open_dir = is_directory,
633 }) catch |err| switch (err) {
634 error.IsDir => return error.PathAlreadyExists,
635 error.WouldBlock => unreachable,
636 error.PipeBusy => unreachable,
637 else => |e| return e,
638 };
699639 defer CloseHandle(symlink_handle);
700640
701641 // prepare reparse data buffer
......@@ -726,72 +666,24 @@ pub const ReadLinkError = error{
726666 Unexpected,
727667 NameTooLong,
728668 UnsupportedReparsePointType,
729 InvalidUtf8,
730 BadPathName,
731669};
732670
733pub fn ReadLink(
734 dir: ?HANDLE,
735 sub_path: []const u8,
736 out_buffer: []u8,
737) ReadLinkError![]u8 {
738 const sub_path_w = try sliceToPrefixedFileW(sub_path);
739 return ReadLinkW(dir, sub_path_w.span().ptr, out_buffer);
740}
741
742pub fn ReadLinkW(dir: ?HANDLE, sub_path_w: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
743 const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
744 error.Overflow => return error.NameTooLong,
745 };
746 var nt_name = UNICODE_STRING{
747 .Length = path_len_bytes,
748 .MaximumLength = path_len_bytes,
749 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
671pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
672 const result_handle = OpenFile(sub_path_w, .{
673 .dir = dir,
674 .access_mask = FILE_READ_ATTRIBUTES,
675 .share_access = FILE_SHARE_READ,
676 .creation = FILE_OPEN,
677 .io_mode = .blocking,
678 .follow_symlinks = false,
679 }) catch |err| switch (err) {
680 error.WouldBlock => unreachable,
681 error.PipeBusy => unreachable,
682 error.IsDir => unreachable,
683 error.NoDevice => unreachable,
684 error.PathAlreadyExists => unreachable,
685 else => |e| return e,
750686 };
751
752 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
753 // Windows does not recognize this, but it does work with empty string.
754 nt_name.Length = 0;
755 }
756
757 var attr = OBJECT_ATTRIBUTES{
758 .Length = @sizeOf(OBJECT_ATTRIBUTES),
759 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
760 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
761 .ObjectName = &nt_name,
762 .SecurityDescriptor = null,
763 .SecurityQualityOfService = null,
764 };
765 var io: IO_STATUS_BLOCK = undefined;
766 var result_handle: HANDLE = undefined;
767 const rc = ntdll.NtCreateFile(
768 &result_handle,
769 FILE_READ_ATTRIBUTES,
770 &attr,
771 &io,
772 null,
773 FILE_ATTRIBUTE_NORMAL,
774 FILE_SHARE_READ,
775 FILE_OPEN,
776 FILE_OPEN_REPARSE_POINT,
777 null,
778 0,
779 );
780 switch (rc) {
781 .SUCCESS => {},
782 .OBJECT_NAME_INVALID => unreachable,
783 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
784 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
785 .NO_MEDIA_IN_DEVICE => return error.FileNotFound,
786 .INVALID_PARAMETER => unreachable,
787 .SHARING_VIOLATION => return error.AccessDenied,
788 .ACCESS_DENIED => return error.AccessDenied,
789 .PIPE_BUSY => return error.AccessDenied,
790 .OBJECT_PATH_SYNTAX_BAD => unreachable,
791 .OBJECT_NAME_COLLISION => unreachable,
792 .FILE_IS_A_DIRECTORY => unreachable,
793 else => return unexpectedStatus(rc),
794 }
795687 defer CloseHandle(result_handle);
796688
797689 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;