| author | |
| committer | |
| log | 99e3e29e2e611cf86941921770644401a3cf74b7 |
| tree | 33729c1d9afa019af7c14716c05ce3fa9ca9ae4d |
| parent | c47cb8d09f7cf1c02d3af59ebbca665ce78c85ca |
4 files changed, 33 insertions(+), 31 deletions(-)
lib/std/os.zig+20-9| ... | ... | @@ -1568,8 +1568,7 @@ pub const symlinkC = @compileError("deprecated: renamed to symlinkZ"); |
| 1568 | 1568 | /// like to create a symbolic link to a directory, use `std.os.windows.CreateSymbolicLinkW` directly |
| 1569 | 1569 | /// specifying as flags `std.os.windows.CreateSymbolicLinkFlags.Directory`. |
| 1570 | 1570 | pub fn symlinkW(target_path: [*:0]const u16, sym_link_path: [*:0]const u16) SymLinkError!void { |
| 1571 | const flags = windows.CreateSymbolicLinkFlags.File; | |
| 1572 | return windows.CreateSymbolicLinkW(sym_link_path, target_path, flags); | |
| 1571 | return windows.CreateSymbolicLinkW(sym_link_path, target_path, false); | |
| 1573 | 1572 | } |
| 1574 | 1573 | |
| 1575 | 1574 | /// This is the same as `symlink` except the parameters are null-terminated pointers. |
| ... | ... | @@ -2395,20 +2394,20 @@ pub const readlinkC = @compileError("deprecated: renamed to readlinkZ"); |
| 2395 | 2394 | pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 { |
| 2396 | 2395 | const w = windows; |
| 2397 | 2396 | |
| 2398 | const dir = if (std.fs.path.isAbsoluteWindowsW(file_path)) null else std.fs.cwd().fd; | |
| 2399 | const handle = w.OpenAsReparsePoint(dir, file_path) catch |err| { | |
| 2397 | const sharing = w.FILE_SHARE_DELETE | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE; | |
| 2398 | const disposition = w.OPEN_EXISTING; | |
| 2399 | const flags = w.FILE_FLAG_BACKUP_SEMANTICS | w.FILE_FLAG_OPEN_REPARSE_POINT; | |
| 2400 | const handle = w.CreateFileW(file_path, 0, sharing, null, disposition, flags, null) catch |err| { | |
| 2400 | 2401 | switch (err) { |
| 2401 | 2402 | error.SharingViolation => return error.AccessDenied, |
| 2402 | 2403 | error.PipeBusy => unreachable, |
| 2403 | 2404 | error.PathAlreadyExists => unreachable, |
| 2404 | error.NoDevice => return error.FileNotFound, | |
| 2405 | 2405 | else => |e| return e, |
| 2406 | 2406 | } |
| 2407 | 2407 | }; |
| 2408 | 2408 | |
| 2409 | 2409 | var reparse_buf: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; |
| 2410 | 2410 | _ = try w.DeviceIoControl(handle, w.FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..], null); |
| 2411 | // std.debug.warn("\n\n{x}\n\n", .{reparse_buf}); | |
| 2412 | 2411 | const reparse_struct = @ptrCast(*const w.REPARSE_DATA_BUFFER, @alignCast(@alignOf(w.REPARSE_DATA_BUFFER), &reparse_buf[0])); |
| 2413 | 2412 | switch (reparse_struct.ReparseTag) { |
| 2414 | 2413 | w.IO_REPARSE_TAG_SYMLINK => { |
| ... | ... | @@ -2434,9 +2433,9 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 |
| 2434 | 2433 | } |
| 2435 | 2434 | |
| 2436 | 2435 | fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u8 { |
| 2437 | const out_len = std.unicode.utf16leToUtf8(out_buffer, path) catch unreachable; | |
| 2438 | std.debug.warn("got symlink => utf8={}\n", .{out_buffer[0..out_len]}); | |
| 2439 | // TODO handle absolute paths and namespace prefix '/??/' | |
| 2436 | const prefix = [_]u16{ '\\', '?', '?', '\\' }; | |
| 2437 | const start_index = if (mem.startsWith(u16, path, &prefix)) prefix.len else 0; | |
| 2438 | const out_len = std.unicode.utf16leToUtf8(out_buffer, path[start_index..]) catch unreachable; | |
| 2440 | 2439 | return out_buffer[0..out_len]; |
| 2441 | 2440 | } |
| 2442 | 2441 | |
| ... | ... | @@ -2502,6 +2501,18 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read |
| 2502 | 2501 | /// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded. |
| 2503 | 2502 | /// See also `readlinkat`. |
| 2504 | 2503 | pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 { |
| 2504 | const w = windows; | |
| 2505 | ||
| 2506 | const handle = w.OpenReparsePoint(dir, file_path) catch |err| { | |
| 2507 | switch (err) { | |
| 2508 | error.SharingViolation => return error.AccessDenied, | |
| 2509 | error.PathAlreadyExists => unreachable, | |
| 2510 | error.PipeBusy => unreachable, | |
| 2511 | error.PathAlreadyExists => unreachable, | |
| 2512 | error.NoDevice => return error.FileNotFound, | |
| 2513 | else => |e| return e, | |
| 2514 | } | |
| 2515 | }; | |
| 2505 | 2516 | @compileError("TODO implement on Windows"); |
| 2506 | 2517 | } |
| 2507 | 2518 |
lib/std/os/test.zig+1| ... | ... | @@ -86,6 +86,7 @@ test "readlink" { |
| 86 | 86 | // now, read the link and verify |
| 87 | 87 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; |
| 88 | 88 | const given = try os.readlink(symlink_path, buffer[0..]); |
| 89 | std.debug.warn("given={}\n", .{given}); | |
| 89 | 90 | expect(mem.eql(u8, symlink_path, given)); |
| 90 | 91 | } |
| 91 | 92 | } |
lib/std/os/windows.zig+11-20| ... | ... | @@ -602,43 +602,34 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 { |
| 602 | 602 | return buffer[0..end_index]; |
| 603 | 603 | } |
| 604 | 604 | |
| 605 | pub const CreateSymbolicLinkError = error{ | |
| 606 | AccessDenied, | |
| 607 | PathAlreadyExists, | |
| 608 | FileNotFound, | |
| 609 | Unexpected | |
| 610 | }; | |
| 611 | ||
| 612 | pub const CreateSymbolicLinkFlags = enum(DWORD) { | |
| 613 | File = SYMBOLIC_LINK_FLAG_FILE, | |
| 614 | Directory = SYMBOLIC_LINK_FLAG_DIRECTORY, | |
| 615 | }; | |
| 605 | pub const CreateSymbolicLinkError = error{ AccessDenied, PathAlreadyExists, FileNotFound, Unexpected }; | |
| 616 | 606 | |
| 617 | 607 | pub fn CreateSymbolicLink( |
| 618 | 608 | sym_link_path: []const u8, |
| 619 | 609 | target_path: []const u8, |
| 620 | flags: CreateSymbolicLinkFlags, | |
| 610 | is_directory: bool, | |
| 621 | 611 | ) CreateSymbolicLinkError!void { |
| 622 | 612 | const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path); |
| 623 | 613 | const target_path_w = try sliceToPrefixedFileW(target_path); |
| 624 | return CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, flags); | |
| 614 | return CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, is_directory); | |
| 625 | 615 | } |
| 626 | 616 | |
| 627 | 617 | pub fn CreateSymbolicLinkW( |
| 628 | 618 | sym_link_path: [*:0]const u16, |
| 629 | 619 | target_path: [*:0]const u16, |
| 630 | flags: CreateSymbolicLinkFlags, | |
| 620 | is_directory: bool, | |
| 631 | 621 | ) CreateSymbolicLinkError!void { |
| 632 | 622 | // Previously, until Win 10 Creators Update, creating symbolic links required |
| 633 | 623 | // SeCreateSymbolicLink privilege. Currently, this is no longer required if the |
| 634 | 624 | // OS is in Developer Mode; however, SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE |
| 635 | 625 | // must be added to the input flags. |
| 636 | if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, @enumToInt(flags) | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) == 0) { | |
| 626 | const flags = if (is_directory) SYMBOLIC_LINK_FLAG_DIRECTORY else 0; | |
| 627 | if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) == 0) { | |
| 637 | 628 | switch (kernel32.GetLastError()) { |
| 638 | 629 | .INVALID_PARAMETER => { |
| 639 | 630 | // If we're on Windows pre Creators Update, SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE |
| 640 | 631 | // flag is an invalid parameter, in which case repeat without the flag. |
| 641 | if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, @enumToInt(flags)) == 0) { | |
| 632 | if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags) == 0) { | |
| 642 | 633 | switch (kernel32.GetLastError()) { |
| 643 | 634 | .PRIVILEGE_NOT_HELD => return error.AccessDenied, |
| 644 | 635 | .FILE_NOT_FOUND => return error.FileNotFound, |
| ... | ... | @@ -1372,7 +1363,7 @@ pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError { |
| 1372 | 1363 | return error.Unexpected; |
| 1373 | 1364 | } |
| 1374 | 1365 | |
| 1375 | pub const OpenAsReparsePointError = error { | |
| 1366 | pub const OpenReparsePointError = error{ | |
| 1376 | 1367 | FileNotFound, |
| 1377 | 1368 | NoDevice, |
| 1378 | 1369 | SharingViolation, |
| ... | ... | @@ -1384,10 +1375,10 @@ pub const OpenAsReparsePointError = error { |
| 1384 | 1375 | }; |
| 1385 | 1376 | |
| 1386 | 1377 | /// Open file as a reparse point |
| 1387 | pub fn OpenAsReparsePoint( | |
| 1378 | pub fn OpenReparsePoint( | |
| 1388 | 1379 | dir: ?HANDLE, |
| 1389 | 1380 | sub_path_w: [*:0]const u16, |
| 1390 | ) OpenAsReparsePointError!HANDLE { | |
| 1381 | ) OpenReparsePointError!HANDLE { | |
| 1391 | 1382 | const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) { |
| 1392 | 1383 | error.Overflow => return error.NameTooLong, |
| 1393 | 1384 | }; |
| ... | ... | @@ -1440,4 +1431,4 @@ pub fn OpenAsReparsePoint( |
| 1440 | 1431 | .FILE_IS_A_DIRECTORY => unreachable, |
| 1441 | 1432 | else => return unexpectedStatus(rc), |
| 1442 | 1433 | } |
| 1443 | } | |
| \ No newline at end of file | ||
| 1434 | } |
lib/std/os/windows/bits.zig+1-2| ... | ... | @@ -1568,8 +1568,7 @@ pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: ULONG = 16 * 1024; |
| 1568 | 1568 | pub const FSCTL_GET_REPARSE_POINT: DWORD = 0x900a8; |
| 1569 | 1569 | pub const IO_REPARSE_TAG_SYMLINK: ULONG = 0xa000000c; |
| 1570 | 1570 | pub const IO_REPARSE_TAG_MOUNT_POINT: ULONG = 0xa0000003; |
| 1571 | pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x00000001; | |
| 1571 | pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1; | |
| 1572 | 1572 | |
| 1573 | pub const SYMBOLIC_LINK_FLAG_FILE: DWORD = 0x0; | |
| 1574 | 1573 | pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1; |
| 1575 | 1574 | pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2; |