diff --git a/lib/std/os.zig b/lib/std/os.zig index 7b85871446..cfcab92bd4 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1568,8 +1568,7 @@ pub const symlinkC = @compileError("deprecated: renamed to symlinkZ"); /// like to create a symbolic link to a directory, use `std.os.windows.CreateSymbolicLinkW` directly /// specifying as flags `std.os.windows.CreateSymbolicLinkFlags.Directory`. pub fn symlinkW(target_path: [*:0]const u16, sym_link_path: [*:0]const u16) SymLinkError!void { - const flags = windows.CreateSymbolicLinkFlags.File; - return windows.CreateSymbolicLinkW(sym_link_path, target_path, flags); + return windows.CreateSymbolicLinkW(sym_link_path, target_path, false); } /// 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"); pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 { const w = windows; - const dir = if (std.fs.path.isAbsoluteWindowsW(file_path)) null else std.fs.cwd().fd; - const handle = w.OpenAsReparsePoint(dir, file_path) catch |err| { + const sharing = w.FILE_SHARE_DELETE | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE; + const disposition = w.OPEN_EXISTING; + const flags = w.FILE_FLAG_BACKUP_SEMANTICS | w.FILE_FLAG_OPEN_REPARSE_POINT; + const handle = w.CreateFileW(file_path, 0, sharing, null, disposition, flags, null) catch |err| { switch (err) { error.SharingViolation => return error.AccessDenied, error.PipeBusy => unreachable, error.PathAlreadyExists => unreachable, - error.NoDevice => return error.FileNotFound, else => |e| return e, } }; var reparse_buf: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; _ = try w.DeviceIoControl(handle, w.FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..], null); - // std.debug.warn("\n\n{x}\n\n", .{reparse_buf}); const reparse_struct = @ptrCast(*const w.REPARSE_DATA_BUFFER, @alignCast(@alignOf(w.REPARSE_DATA_BUFFER), &reparse_buf[0])); switch (reparse_struct.ReparseTag) { w.IO_REPARSE_TAG_SYMLINK => { @@ -2434,9 +2433,9 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 } fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u8 { - const out_len = std.unicode.utf16leToUtf8(out_buffer, path) catch unreachable; - std.debug.warn("got symlink => utf8={}\n", .{out_buffer[0..out_len]}); - // TODO handle absolute paths and namespace prefix '/??/' + const prefix = [_]u16{ '\\', '?', '?', '\\' }; + const start_index = if (mem.startsWith(u16, path, &prefix)) prefix.len else 0; + const out_len = std.unicode.utf16leToUtf8(out_buffer, path[start_index..]) catch unreachable; return out_buffer[0..out_len]; } @@ -2502,6 +2501,18 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read /// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded. /// See also `readlinkat`. pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 { + const w = windows; + + const handle = w.OpenReparsePoint(dir, file_path) catch |err| { + switch (err) { + error.SharingViolation => return error.AccessDenied, + error.PathAlreadyExists => unreachable, + error.PipeBusy => unreachable, + error.PathAlreadyExists => unreachable, + error.NoDevice => return error.FileNotFound, + else => |e| return e, + } + }; @compileError("TODO implement on Windows"); } diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 6b9c19c484..3ef1006b0a 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -86,6 +86,7 @@ test "readlink" { // now, read the link and verify var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; const given = try os.readlink(symlink_path, buffer[0..]); + std.debug.warn("given={}\n", .{given}); expect(mem.eql(u8, symlink_path, given)); } } diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index d6d1b2db84..67f200d339 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -602,43 +602,34 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 { return buffer[0..end_index]; } -pub const CreateSymbolicLinkError = error{ - AccessDenied, - PathAlreadyExists, - FileNotFound, - Unexpected -}; - -pub const CreateSymbolicLinkFlags = enum(DWORD) { - File = SYMBOLIC_LINK_FLAG_FILE, - Directory = SYMBOLIC_LINK_FLAG_DIRECTORY, -}; +pub const CreateSymbolicLinkError = error{ AccessDenied, PathAlreadyExists, FileNotFound, Unexpected }; pub fn CreateSymbolicLink( sym_link_path: []const u8, target_path: []const u8, - flags: CreateSymbolicLinkFlags, + is_directory: bool, ) CreateSymbolicLinkError!void { const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path); const target_path_w = try sliceToPrefixedFileW(target_path); - return CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, flags); + return CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, is_directory); } pub fn CreateSymbolicLinkW( sym_link_path: [*:0]const u16, target_path: [*:0]const u16, - flags: CreateSymbolicLinkFlags, + is_directory: bool, ) CreateSymbolicLinkError!void { // Previously, until Win 10 Creators Update, creating symbolic links required // SeCreateSymbolicLink privilege. Currently, this is no longer required if the // OS is in Developer Mode; however, SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE // must be added to the input flags. - if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, @enumToInt(flags) | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) == 0) { + const flags = if (is_directory) SYMBOLIC_LINK_FLAG_DIRECTORY else 0; + if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) == 0) { switch (kernel32.GetLastError()) { .INVALID_PARAMETER => { // If we're on Windows pre Creators Update, SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE // flag is an invalid parameter, in which case repeat without the flag. - if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, @enumToInt(flags)) == 0) { + if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags) == 0) { switch (kernel32.GetLastError()) { .PRIVILEGE_NOT_HELD => return error.AccessDenied, .FILE_NOT_FOUND => return error.FileNotFound, @@ -1372,7 +1363,7 @@ pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError { return error.Unexpected; } -pub const OpenAsReparsePointError = error { +pub const OpenReparsePointError = error{ FileNotFound, NoDevice, SharingViolation, @@ -1384,10 +1375,10 @@ pub const OpenAsReparsePointError = error { }; /// Open file as a reparse point -pub fn OpenAsReparsePoint( +pub fn OpenReparsePoint( dir: ?HANDLE, sub_path_w: [*:0]const u16, -) OpenAsReparsePointError!HANDLE { +) OpenReparsePointError!HANDLE { const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) { error.Overflow => return error.NameTooLong, }; @@ -1440,4 +1431,4 @@ pub fn OpenAsReparsePoint( .FILE_IS_A_DIRECTORY => unreachable, else => return unexpectedStatus(rc), } -} \ No newline at end of file +} diff --git a/lib/std/os/windows/bits.zig b/lib/std/os/windows/bits.zig index 1c5e1a3f70..7295b6a404 100644 --- a/lib/std/os/windows/bits.zig +++ b/lib/std/os/windows/bits.zig @@ -1568,8 +1568,7 @@ pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: ULONG = 16 * 1024; pub const FSCTL_GET_REPARSE_POINT: DWORD = 0x900a8; pub const IO_REPARSE_TAG_SYMLINK: ULONG = 0xa000000c; pub const IO_REPARSE_TAG_MOUNT_POINT: ULONG = 0xa0000003; -pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x00000001; +pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1; -pub const SYMBOLIC_LINK_FLAG_FILE: DWORD = 0x0; pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1; pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2;