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 {...@@ -1261,11 +1261,11 @@ pub const Dir = struct {
1261 /// are null-terminated, WTF16 encoded.1261 /// are null-terminated, WTF16 encoded.
1262 pub fn symLinkW(1262 pub fn symLinkW(
1263 self: Dir,1263 self: Dir,
1264 target_path_w: [:0]const u16,1264 target_path_w: []const u16,
1265 sym_link_path_w: [:0]const u16,1265 sym_link_path_w: []const u16,
1266 flags: SymLinkFlags,1266 flags: SymLinkFlags,
1267 ) !void {1267 ) !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);
1269 }1269 }
12701270
1271 /// Read value of a symbolic link.1271 /// Read value of a symbolic link.
...@@ -1276,7 +1276,8 @@ pub const Dir = struct {...@@ -1276,7 +1276,8 @@ pub const Dir = struct {
1276 return self.readLinkWasi(sub_path, buffer);1276 return self.readLinkWasi(sub_path, buffer);
1277 }1277 }
1278 if (builtin.os.tag == .windows) {1278 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);
1280 }1281 }
1281 const sub_path_c = try os.toPosixPath(sub_path);1282 const sub_path_c = try os.toPosixPath(sub_path);
1282 return self.readLinkZ(&sub_path_c, buffer);1283 return self.readLinkZ(&sub_path_c, buffer);
...@@ -1293,15 +1294,15 @@ pub const Dir = struct {...@@ -1293,15 +1294,15 @@ pub const Dir = struct {
1293 pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {1294 pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
1294 if (builtin.os.tag == .windows) {1295 if (builtin.os.tag == .windows) {
1295 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);1296 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);
1297 }1298 }
1298 return os.readlinkatZ(self.fd, sub_path_c, buffer);1299 return os.readlinkatZ(self.fd, sub_path_c, buffer);
1299 }1300 }
13001301
1301 /// Windows-only. Same as `readLink` except the pathname parameter1302 /// Windows-only. Same as `readLink` except the pathname parameter
1302 /// is null-terminated, WTF16 encoded.1303 /// is null-terminated, WTF16 encoded.
1303 pub fn readLinkW(self: Dir, sub_path_w: [*:0]const u16, buffer: []u8) ![]u8 {1304 pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
1304 return os.windows.ReadLinkW(self.fd, sub_path_w, buffer);1305 return os.windows.ReadLink(self.fd, sub_path_w, buffer);
1305 }1306 }
13061307
1307 /// On success, caller owns returned buffer.1308 /// On success, caller owns returned buffer.
...@@ -1811,7 +1812,9 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags...@@ -1811,7 +1812,9 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
1811 assert(path.isAbsolute(target_path));1812 assert(path.isAbsolute(target_path));
1812 assert(path.isAbsolute(sym_link_path));1813 assert(path.isAbsolute(sym_link_path));
1813 if (builtin.os.tag == .windows) {1814 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);
1815 }1818 }
1816 return os.symlink(target_path, sym_link_path);1819 return os.symlink(target_path, sym_link_path);
1817}1820}
...@@ -1820,10 +1823,10 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags...@@ -1820,10 +1823,10 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
1820/// Note that this function will by default try creating a symbolic link to a file. If you would1823/// Note that this function will by default try creating a symbolic link to a file. If you would
1821/// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`.1824/// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`.
1822/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.1825/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.
1823pub fn symLinkAbsoluteW(target_path_w: [:0]const u16, sym_link_path_w: [:0]const u16, flags: SymLinkFlags) !void {1826pub fn symLinkAbsoluteW(target_path_w: []const u16, sym_link_path_w: []const u16, flags: SymLinkFlags) !void {
1824 assert(path.isAbsoluteWindowsW(target_path_w));1827 assert(path.isAbsoluteWindowsWTF16(target_path_w));
1825 assert(path.isAbsoluteWindowsW(sym_link_path_w));1828 assert(path.isAbsoluteWindowsWTF16(sym_link_path_w));
1826 return os.windows.CreateSymbolicLinkW(null, sym_link_path_w, target_path_w, flags.is_directory);1829 return os.windows.CreateSymbolicLink(null, sym_link_path_w, target_path_w, flags.is_directory);
1827}1830}
18281831
1829/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.1832/// 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...@@ -1834,7 +1837,7 @@ pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]cons
1834 if (builtin.os.tag == .windows) {1837 if (builtin.os.tag == .windows) {
1835 const target_path_w = try os.windows.cStrToWin32PrefixedFileW(target_path_c);1838 const target_path_w = try os.windows.cStrToWin32PrefixedFileW(target_path_c);
1836 const sym_link_path_w = try os.windows.cStrToWin32PrefixedFileW(sym_link_path_c);1839 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);
1838 }1841 }
1839 return os.symlinkZ(target_path_c, sym_link_path_c);1842 return os.symlinkZ(target_path_c, sym_link_path_c);
1840}1843}
lib/std/os.zig+25-23
...@@ -2087,7 +2087,7 @@ pub fn renameatW(...@@ -2087,7 +2087,7 @@ pub fn renameatW(
2087pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {2087pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
2088 if (builtin.os.tag == .windows) {2088 if (builtin.os.tag == .windows) {
2089 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);2089 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);
2091 } else if (builtin.os.tag == .wasi) {2091 } else if (builtin.os.tag == .wasi) {
2092 return mkdiratWasi(dir_fd, sub_dir_path, mode);2092 return mkdiratWasi(dir_fd, sub_dir_path, mode);
2093 } else {2093 } else {
...@@ -2145,13 +2145,13 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr...@@ -2145,13 +2145,13 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr
2145 }2145 }
2146}2146}
21472147
2148pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void {2148pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
2149 const sub_dir_handle = windows.OpenFile(std.mem.spanZ(sub_path_w), .{2149 const sub_dir_handle = windows.OpenFile(sub_path_w, .{
2150 .dir = dir_fd,2150 .dir = dir_fd,
2151 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,2151 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
2152 .creation = windows.FILE_CREATE,2152 .creation = windows.FILE_CREATE,
2153 .io_mode = .blocking,2153 .io_mode = .blocking,
2154 .expect_dir = true,2154 .open_dir = true,
2155 }) catch |err| switch (err) {2155 }) catch |err| switch (err) {
2156 error.IsDir => unreachable,2156 error.IsDir => unreachable,
2157 error.PipeBusy => unreachable,2157 error.PipeBusy => unreachable,
...@@ -2187,7 +2187,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {...@@ -2187,7 +2187,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
2187 @compileError("mkdir is not supported in WASI; use mkdirat instead");2187 @compileError("mkdir is not supported in WASI; use mkdirat instead");
2188 } else if (builtin.os.tag == .windows) {2188 } else if (builtin.os.tag == .windows) {
2189 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);2189 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);
2191 } else {2191 } else {
2192 const dir_path_c = try toPosixPath(dir_path);2192 const dir_path_c = try toPosixPath(dir_path);
2193 return mkdirZ(&dir_path_c, mode);2193 return mkdirZ(&dir_path_c, mode);
...@@ -2198,7 +2198,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {...@@ -2198,7 +2198,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
2198pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {2198pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
2199 if (builtin.os.tag == .windows) {2199 if (builtin.os.tag == .windows) {
2200 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);2200 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);
2202 }2202 }
2203 switch (errno(system.mkdir(dir_path, mode))) {2203 switch (errno(system.mkdir(dir_path, mode))) {
2204 0 => return,2204 0 => return,
...@@ -2220,13 +2220,13 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {...@@ -2220,13 +2220,13 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
2220}2220}
22212221
2222/// Windows-only. Same as `mkdir` but the parameters is null-terminated, WTF16 encoded.2222/// 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 {2223pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
2224 const sub_dir_handle = windows.OpenFile(std.mem.spanZ(dir_path_w), .{2224 const sub_dir_handle = windows.OpenFile(dir_path_w, .{
2225 .dir = std.fs.cwd().fd,2225 .dir = std.fs.cwd().fd,
2226 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,2226 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
2227 .creation = windows.FILE_CREATE,2227 .creation = windows.FILE_CREATE,
2228 .io_mode = .blocking,2228 .io_mode = .blocking,
2229 .expect_dir = true,2229 .open_dir = true,
2230 }) catch |err| switch (err) {2230 }) catch |err| switch (err) {
2231 error.IsDir => unreachable,2231 error.IsDir => unreachable,
2232 error.PipeBusy => unreachable,2232 error.PipeBusy => unreachable,
...@@ -2379,7 +2379,8 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {...@@ -2379,7 +2379,8 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2379 if (builtin.os.tag == .wasi) {2379 if (builtin.os.tag == .wasi) {
2380 @compileError("readlink is not supported in WASI; use readlinkat instead");2380 @compileError("readlink is not supported in WASI; use readlinkat instead");
2381 } else if (builtin.os.tag == .windows) {2381 } 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);
2383 } else {2384 } else {
2384 const file_path_c = try toPosixPath(file_path);2385 const file_path_c = try toPosixPath(file_path);
2385 return readlinkZ(&file_path_c, out_buffer);2386 return readlinkZ(&file_path_c, out_buffer);
...@@ -2390,15 +2391,15 @@ pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");...@@ -2390,15 +2391,15 @@ pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");
23902391
2391/// Windows-only. Same as `readlink` except `file_path` is null-terminated, WTF16 encoded.2392/// Windows-only. Same as `readlink` except `file_path` is null-terminated, WTF16 encoded.
2392/// See also `readlinkZ`.2393/// See also `readlinkZ`.
2393pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {2394pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
2394 return windows.ReadLinkW(std.fs.cwd().fd, file_path, out_buffer);2395 return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
2395}2396}
23962397
2397/// Same as `readlink` except `file_path` is null-terminated.2398/// Same as `readlink` except `file_path` is null-terminated.
2398pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {2399pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
2399 if (builtin.os.tag == .windows) {2400 if (builtin.os.tag == .windows) {
2400 const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);2401 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);
2402 }2403 }
2403 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);2404 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
2404 switch (errno(rc)) {2405 switch (errno(rc)) {
...@@ -2424,7 +2425,8 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink...@@ -2424,7 +2425,8 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink
2424 return readlinkatWasi(dirfd, file_path, out_buffer);2425 return readlinkatWasi(dirfd, file_path, out_buffer);
2425 }2426 }
2426 if (builtin.os.tag == .windows) {2427 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);
2428 }2430 }
2429 const file_path_c = try toPosixPath(file_path);2431 const file_path_c = try toPosixPath(file_path);
2430 return readlinkatZ(dirfd, &file_path_c, out_buffer);2432 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...@@ -2454,8 +2456,8 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read
24542456
2455/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.2457/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
2456/// See also `readlinkat`.2458/// See also `readlinkat`.
2457pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {2459pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
2458 return windows.ReadLinkW(dirfd, file_path, out_buffer);2460 return windows.ReadLink(dirfd, file_path, out_buffer);
2459}2461}
24602462
2461/// Same as `readlinkat` except `file_path` is null-terminated.2463/// 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...@@ -2463,7 +2465,7 @@ pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) Rea
2463pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {2465pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
2464 if (builtin.os.tag == .windows) {2466 if (builtin.os.tag == .windows) {
2465 const file_path_w = try windows.cStrToPrefixedFileW(file_path);2467 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);
2467 }2469 }
2468 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);2470 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
2469 switch (errno(rc)) {2471 switch (errno(rc)) {
...@@ -3984,7 +3986,7 @@ pub const RealPathError = error{...@@ -3984,7 +3986,7 @@ pub const RealPathError = error{
3984pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {3986pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
3985 if (builtin.os.tag == .windows) {3987 if (builtin.os.tag == .windows) {
3986 const pathname_w = try windows.sliceToPrefixedFileW(pathname);3988 const pathname_w = try windows.sliceToPrefixedFileW(pathname);
3987 return realpathW(pathname_w.span().ptr, out_buffer);3989 return realpathW(pathname_w.span(), out_buffer);
3988 }3990 }
3989 if (builtin.os.tag == .wasi) {3991 if (builtin.os.tag == .wasi) {
3990 @compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths");3992 @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");...@@ -3999,7 +4001,7 @@ pub const realpathC = @compileError("deprecated: renamed realpathZ");
3999pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {4001pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4000 if (builtin.os.tag == .windows) {4002 if (builtin.os.tag == .windows) {
4001 const pathname_w = try windows.cStrToPrefixedFileW(pathname);4003 const pathname_w = try windows.cStrToPrefixedFileW(pathname);
4002 return realpathW(pathname_w.span().ptr, out_buffer);4004 return realpathW(pathname_w.span(), out_buffer);
4003 }4005 }
4004 if (builtin.os.tag == .linux and !builtin.link_libc) {4006 if (builtin.os.tag == .linux and !builtin.link_libc) {
4005 const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) {4007 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...@@ -4037,7 +4039,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
40374039
4038/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.4040/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.
4039/// TODO use ntdll for better semantics4041/// 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 {
4041 const w = windows;4043 const w = windows;
40424044
4043 const dir = std.fs.cwd().fd;4045 const dir = std.fs.cwd().fd;
...@@ -4045,20 +4047,20 @@ pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) Real...@@ -4045,20 +4047,20 @@ pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) Real
4045 const share_access = w.FILE_SHARE_READ;4047 const share_access = w.FILE_SHARE_READ;
4046 const creation = w.FILE_OPEN;4048 const creation = w.FILE_OPEN;
4047 const h_file = blk: {4049 const h_file = blk: {
4048 const res = w.OpenFile(std.mem.spanZ(pathname), .{4050 const res = w.OpenFile(pathname, .{
4049 .dir = dir,4051 .dir = dir,
4050 .access_mask = access_mask,4052 .access_mask = access_mask,
4051 .share_access = share_access,4053 .share_access = share_access,
4052 .creation = creation,4054 .creation = creation,
4053 .io_mode = .blocking,4055 .io_mode = .blocking,
4054 }) catch |err| switch (err) {4056 }) catch |err| switch (err) {
4055 error.IsDir => break :blk w.OpenFile(std.mem.spanZ(pathname), .{4057 error.IsDir => break :blk w.OpenFile(pathname, .{
4056 .dir = dir,4058 .dir = dir,
4057 .access_mask = access_mask,4059 .access_mask = access_mask,
4058 .share_access = share_access,4060 .share_access = share_access,
4059 .creation = creation,4061 .creation = creation,
4060 .io_mode = .blocking,4062 .io_mode = .blocking,
4061 .expect_dir = true,4063 .open_dir = true,
4062 }) catch |er| switch (er) {4064 }) catch |er| switch (er) {
4063 error.WouldBlock => unreachable,4065 error.WouldBlock => unreachable,
4064 else => |e2| return e2,4066 else => |e2| return e2,
lib/std/os/test.zig+2-2
...@@ -27,7 +27,7 @@ test "symlink with relative paths" {...@@ -27,7 +27,7 @@ test "symlink with relative paths" {
27 try cwd.writeFile("file.txt", "nonsense");27 try cwd.writeFile("file.txt", "nonsense");
2828
29 if (builtin.os.tag == .windows) {29 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);
31 } else {31 } else {
32 try os.symlink("file.txt", "symlinked");32 try os.symlink("file.txt", "symlinked");
33 }33 }
...@@ -85,7 +85,7 @@ test "readlinkat" {...@@ -85,7 +85,7 @@ test "readlinkat" {
8585
86 // create a symbolic link86 // create a symbolic link
87 if (builtin.os.tag == .windows) {87 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);
89 } else {89 } else {
90 try os.symlinkat("file.txt", tmp.dir.fd, "link");90 try os.symlinkat("file.txt", tmp.dir.fd, "link");
91 }91 }
lib/std/os/windows.zig+40-148
...@@ -69,16 +69,21 @@ pub const OpenFileOptions = struct {...@@ -69,16 +69,21 @@ pub const OpenFileOptions = struct {
69 share_access_nonblocking: bool = false,69 share_access_nonblocking: bool = false,
70 creation: ULONG,70 creation: ULONG,
71 io_mode: std.io.ModeOverride,71 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,
73};78};
7479
75/// TODO when share_access_nonblocking is false, this implementation uses80/// TODO when share_access_nonblocking is false, this implementation uses
76/// untinterruptible sleep() to block. This is not the final iteration of the API.81/// untinterruptible sleep() to block. This is not the final iteration of the API.
77pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {82pub 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) {
79 return error.IsDir;84 return error.IsDir;
80 }85 }
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) {
82 return error.IsDir;87 return error.IsDir;
83 }88 }
8489
...@@ -105,8 +110,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -105,8 +110,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
105 var delay: usize = 1;110 var delay: usize = 1;
106 while (true) {111 while (true) {
107 const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;112 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;113 const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT else FILE_NON_DIRECTORY_FILE;
109 const flags: ULONG = file_or_dir_flag | blocking_flag;114 const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else FILE_OPEN_REPARSE_POINT;
110 const rc = ntdll.NtCreateFile(115 const rc = ntdll.NtCreateFile(
111 &result,116 &result,
112 options.access_mask,117 options.access_mask,
...@@ -143,7 +148,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -143,7 +148,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
143 .PIPE_BUSY => return error.PipeBusy,148 .PIPE_BUSY => return error.PipeBusy,
144 .OBJECT_PATH_SYNTAX_BAD => unreachable,149 .OBJECT_PATH_SYNTAX_BAD => unreachable,
145 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,150 .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,
147 else => return unexpectedStatus(rc),152 else => return unexpectedStatus(rc),
148 }153 }
149 }154 }
...@@ -598,27 +603,14 @@ pub const CreateSymbolicLinkError = error{...@@ -598,27 +603,14 @@ pub const CreateSymbolicLinkError = error{
598 PathAlreadyExists,603 PathAlreadyExists,
599 FileNotFound,604 FileNotFound,
600 NameTooLong,605 NameTooLong,
601 InvalidUtf8,
602 BadPathName,
603 NoDevice,606 NoDevice,
604 Unexpected,607 Unexpected,
605};608};
606609
607pub fn CreateSymbolicLink(610pub fn CreateSymbolicLink(
608 dir: ?HANDLE,611 dir: ?HANDLE,
609 sym_link_path: []const u8,612 sym_link_path: []const u16,
610 target_path: []const u8,613 target_path: []const u16,
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,
622 is_directory: bool,614 is_directory: bool,
623) CreateSymbolicLinkError!void {615) CreateSymbolicLinkError!void {
624 const SYMLINK_DATA = extern struct {616 const SYMLINK_DATA = extern struct {
...@@ -632,70 +624,18 @@ pub fn CreateSymbolicLinkW(...@@ -632,70 +624,18 @@ pub fn CreateSymbolicLinkW(
632 Flags: ULONG,624 Flags: ULONG,
633 };625 };
634626
635 var symlink_handle: HANDLE = undefined;627 const symlink_handle = OpenFile(sym_link_path, .{
636 if (is_directory) {628 .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
637 const sym_link_len_bytes = math.cast(u16, sym_link_path.len * 2) catch |err| switch (err) {629 .dir = dir,
638 error.Overflow => return error.NameTooLong,630 .creation = FILE_CREATE,
639 };631 .io_mode = .blocking,
640 var nt_name = UNICODE_STRING{632 .open_dir = is_directory,
641 .Length = sym_link_len_bytes,633 }) catch |err| switch (err) {
642 .MaximumLength = sym_link_len_bytes,634 error.IsDir => return error.PathAlreadyExists,
643 .Buffer = @intToPtr([*]u16, @ptrToInt(sym_link_path.ptr)),635 error.WouldBlock => unreachable,
644 };636 error.PipeBusy => unreachable,
645637 else => |e| return e,
646 if (sym_link_path[0] == '.' and sym_link_path[1] == 0) {638 };
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 }
699 defer CloseHandle(symlink_handle);639 defer CloseHandle(symlink_handle);
700640
701 // prepare reparse data buffer641 // prepare reparse data buffer
...@@ -726,72 +666,24 @@ pub const ReadLinkError = error{...@@ -726,72 +666,24 @@ pub const ReadLinkError = error{
726 Unexpected,666 Unexpected,
727 NameTooLong,667 NameTooLong,
728 UnsupportedReparsePointType,668 UnsupportedReparsePointType,
729 InvalidUtf8,
730 BadPathName,
731};669};
732670
733pub fn ReadLink(671pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
734 dir: ?HANDLE,672 const result_handle = OpenFile(sub_path_w, .{
735 sub_path: []const u8,673 .dir = dir,
736 out_buffer: []u8,674 .access_mask = FILE_READ_ATTRIBUTES,
737) ReadLinkError![]u8 {675 .share_access = FILE_SHARE_READ,
738 const sub_path_w = try sliceToPrefixedFileW(sub_path);676 .creation = FILE_OPEN,
739 return ReadLinkW(dir, sub_path_w.span().ptr, out_buffer);677 .io_mode = .blocking,
740}678 .follow_symlinks = false,
741679 }) catch |err| switch (err) {
742pub fn ReadLinkW(dir: ?HANDLE, sub_path_w: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {680 error.WouldBlock => unreachable,
743 const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {681 error.PipeBusy => unreachable,
744 error.Overflow => return error.NameTooLong,682 error.IsDir => unreachable,
745 };683 error.NoDevice => unreachable,
746 var nt_name = UNICODE_STRING{684 error.PathAlreadyExists => unreachable,
747 .Length = path_len_bytes,685 else => |e| return e,
748 .MaximumLength = path_len_bytes,
749 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
750 };686 };
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 }
795 defer CloseHandle(result_handle);687 defer CloseHandle(result_handle);
796688
797 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;689 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;