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