| author | |
| committer | |
| log | ac85befbb4356daa774b6083a3aa853b9c78f548 |
| tree | d3f9b6ddedc307adde0241b1c929ec266b0a106a |
| parent | e355bcce36fc5d704fede510002d4b22bf9594b7 |
3 files changed, 60 insertions(+), 9 deletions(-)
lib/std/fs/test.zig+20-4| ... | @@ -25,12 +25,20 @@ test "Dir.readLink" { | ... | @@ -25,12 +25,20 @@ test "Dir.readLink" { |
| 25 | 25 | ||
| 26 | { | 26 | { |
| 27 | // Create symbolic link by path | 27 | // Create symbolic link by path |
| 28 | try tmp.dir.symLink("file.txt", "symlink1", .{}); | 28 | tmp.dir.symLink("file.txt", "symlink1", .{}) catch |err| switch (err) { |
| 29 | // Symlink requires admin privileges on windows, so this test can legitimately fail. | ||
| 30 | error.AccessDenied => return error.SkipZigTest, | ||
| 31 | else => return err, | ||
| 32 | }; | ||
| 29 | try testReadLink(tmp.dir, "file.txt", "symlink1"); | 33 | try testReadLink(tmp.dir, "file.txt", "symlink1"); |
| 30 | } | 34 | } |
| 31 | { | 35 | { |
| 32 | // Create symbolic link by path | 36 | // Create symbolic link by path |
| 33 | try tmp.dir.symLink("subdir", "symlink2", .{ .is_directory = true }); | 37 | tmp.dir.symLink("subdir", "symlink2", .{ .is_directory = true }) catch |err| switch (err) { |
| 38 | // Symlink requires admin privileges on windows, so this test can legitimately fail. | ||
| 39 | error.AccessDenied => return error.SkipZigTest, | ||
| 40 | else => return err, | ||
| 41 | }; | ||
| 34 | try testReadLink(tmp.dir, "subdir", "symlink2"); | 42 | try testReadLink(tmp.dir, "subdir", "symlink2"); |
| 35 | } | 43 | } |
| 36 | } | 44 | } |
| ... | @@ -66,7 +74,11 @@ test "readLinkAbsolute" { | ... | @@ -66,7 +74,11 @@ test "readLinkAbsolute" { |
| 66 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink1" }); | 74 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink1" }); |
| 67 | 75 | ||
| 68 | // Create symbolic link by path | 76 | // Create symbolic link by path |
| 69 | try fs.symLinkAbsolute(target_path, symlink_path, .{}); | 77 | fs.symLinkAbsolute(target_path, symlink_path, .{}) catch |err| switch (err) { |
| 78 | // Symlink requires admin privileges on windows, so this test can legitimately fail. | ||
| 79 | error.AccessDenied => return error.SkipZigTest, | ||
| 80 | else => return err, | ||
| 81 | }; | ||
| 70 | try testReadLinkAbsolute(target_path, symlink_path); | 82 | try testReadLinkAbsolute(target_path, symlink_path); |
| 71 | } | 83 | } |
| 72 | { | 84 | { |
| ... | @@ -74,7 +86,11 @@ test "readLinkAbsolute" { | ... | @@ -74,7 +86,11 @@ test "readLinkAbsolute" { |
| 74 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink2" }); | 86 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink2" }); |
| 75 | 87 | ||
| 76 | // Create symbolic link by path | 88 | // Create symbolic link by path |
| 77 | try fs.symLinkAbsolute(target_path, symlink_path, .{ .is_directory = true }); | 89 | fs.symLinkAbsolute(target_path, symlink_path, .{ .is_directory = true }) catch |err| switch (err) { |
| 90 | // Symlink requires admin privileges on windows, so this test can legitimately fail. | ||
| 91 | error.AccessDenied => return error.SkipZigTest, | ||
| 92 | else => return err, | ||
| 93 | }; | ||
| 78 | try testReadLinkAbsolute(target_path, symlink_path); | 94 | try testReadLinkAbsolute(target_path, symlink_path); |
| 79 | } | 95 | } |
| 80 | } | 96 | } |
lib/std/os/test.zig+24-2| ... | @@ -125,7 +125,20 @@ test "symlink with relative paths" { | ... | @@ -125,7 +125,20 @@ test "symlink with relative paths" { |
| 125 | try cwd.writeFile("file.txt", "nonsense"); | 125 | try cwd.writeFile("file.txt", "nonsense"); |
| 126 | 126 | ||
| 127 | if (builtin.os.tag == .windows) { | 127 | if (builtin.os.tag == .windows) { |
| 128 | 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); | 128 | os.windows.CreateSymbolicLink( |
| 129 | cwd.fd, | ||
| 130 | &[_]u16{ 's', 'y', 'm', 'l', 'i', 'n', 'k', 'e', 'd' }, | ||
| 131 | &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, | ||
| 132 | false, | ||
| 133 | ) catch |err| switch (err) { | ||
| 134 | // Symlink requires admin privileges on windows, so this test can legitimately fail. | ||
| 135 | error.AccessDenied => { | ||
| 136 | try cwd.deleteFile("file.txt"); | ||
| 137 | try cwd.deleteFile("symlinked"); | ||
| 138 | return error.SkipZigTest; | ||
| 139 | }, | ||
| 140 | else => return err, | ||
| 141 | }; | ||
| 129 | } else { | 142 | } else { |
| 130 | try os.symlink("file.txt", "symlinked"); | 143 | try os.symlink("file.txt", "symlinked"); |
| 131 | } | 144 | } |
| ... | @@ -183,7 +196,16 @@ test "readlinkat" { | ... | @@ -183,7 +196,16 @@ test "readlinkat" { |
| 183 | 196 | ||
| 184 | // create a symbolic link | 197 | // create a symbolic link |
| 185 | if (builtin.os.tag == .windows) { | 198 | if (builtin.os.tag == .windows) { |
| 186 | try os.windows.CreateSymbolicLink(tmp.dir.fd, &[_]u16{ 'l', 'i', 'n', 'k' }, &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, false); | 199 | os.windows.CreateSymbolicLink( |
| 200 | tmp.dir.fd, | ||
| 201 | &[_]u16{ 'l', 'i', 'n', 'k' }, | ||
| 202 | &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, | ||
| 203 | false, | ||
| 204 | ) catch |err| switch (err) { | ||
| 205 | // Symlink requires admin privileges on windows, so this test can legitimately fail. | ||
| 206 | error.AccessDenied => return error.SkipZigTest, | ||
| 207 | else => return err, | ||
| 208 | }; | ||
| 187 | } else { | 209 | } else { |
| 188 | try os.symlinkat("file.txt", tmp.dir.fd, "link"); | 210 | try os.symlinkat("file.txt", tmp.dir.fd, "link"); |
| 189 | } | 211 | } |
lib/std/os/windows.zig+16-3| ... | @@ -164,7 +164,7 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16, | ... | @@ -164,7 +164,7 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16, |
| 164 | } | 164 | } |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | pub const DeviceIoControlError = error{Unexpected}; | 167 | pub const DeviceIoControlError = error{ AccessDenied, Unexpected }; |
| 168 | 168 | ||
| 169 | /// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls. | 169 | /// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls. |
| 170 | /// It implements similar behavior to `DeviceIoControl` and is meant to serve | 170 | /// It implements similar behavior to `DeviceIoControl` and is meant to serve |
| ... | @@ -216,6 +216,7 @@ pub fn DeviceIoControl( | ... | @@ -216,6 +216,7 @@ pub fn DeviceIoControl( |
| 216 | }; | 216 | }; |
| 217 | switch (rc) { | 217 | switch (rc) { |
| 218 | .SUCCESS => {}, | 218 | .SUCCESS => {}, |
| 219 | .PRIVILEGE_NOT_HELD => return error.AccessDenied, | ||
| 219 | .INVALID_PARAMETER => unreachable, | 220 | .INVALID_PARAMETER => unreachable, |
| 220 | else => return unexpectedStatus(rc), | 221 | else => return unexpectedStatus(rc), |
| 221 | } | 222 | } |
| ... | @@ -593,6 +594,12 @@ pub const CreateSymbolicLinkError = error{ | ... | @@ -593,6 +594,12 @@ pub const CreateSymbolicLinkError = error{ |
| 593 | Unexpected, | 594 | Unexpected, |
| 594 | }; | 595 | }; |
| 595 | 596 | ||
| 597 | /// Needs either: | ||
| 598 | /// - `SeCreateSymbolicLinkPrivilege` privilege | ||
| 599 | /// or | ||
| 600 | /// - Developper mode on Windows 10 | ||
| 601 | /// otherwise fails with `error.AccessDenied`. In which case `sym_link_path` may still | ||
| 602 | /// be created on the file system but will lack reparse processing data applied to it. | ||
| 596 | pub fn CreateSymbolicLink( | 603 | pub fn CreateSymbolicLink( |
| 597 | dir: ?HANDLE, | 604 | dir: ?HANDLE, |
| 598 | sym_link_path: []const u16, | 605 | sym_link_path: []const u16, |
| ... | @@ -710,7 +717,10 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin | ... | @@ -710,7 +717,10 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin |
| 710 | defer CloseHandle(result_handle); | 717 | defer CloseHandle(result_handle); |
| 711 | 718 | ||
| 712 | var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; | 719 | var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; |
| 713 | _ = try DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]); | 720 | _ = DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]) catch |err| switch (err) { |
| 721 | error.AccessDenied => unreachable, | ||
| 722 | else => |e| return e, | ||
| 723 | }; | ||
| 714 | 724 | ||
| 715 | const reparse_struct = @ptrCast(*const REPARSE_DATA_BUFFER, @alignCast(@alignOf(REPARSE_DATA_BUFFER), &reparse_buf[0])); | 725 | const reparse_struct = @ptrCast(*const REPARSE_DATA_BUFFER, @alignCast(@alignOf(REPARSE_DATA_BUFFER), &reparse_buf[0])); |
| 716 | switch (reparse_struct.ReparseTag) { | 726 | switch (reparse_struct.ReparseTag) { |
| ... | @@ -992,7 +1002,10 @@ pub fn GetFinalPathNameByHandle( | ... | @@ -992,7 +1002,10 @@ pub fn GetFinalPathNameByHandle( |
| 992 | input_struct.DeviceNameLength = @intCast(USHORT, volume_name.FileNameLength); | 1002 | input_struct.DeviceNameLength = @intCast(USHORT, volume_name.FileNameLength); |
| 993 | @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, &volume_name.FileName[0]), volume_name.FileNameLength); | 1003 | @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, &volume_name.FileName[0]), volume_name.FileNameLength); |
| 994 | 1004 | ||
| 995 | try DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, input_buf[0..], output_buf[0..]); | 1005 | DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, input_buf[0..], output_buf[0..]) catch |err| switch (err) { |
| 1006 | error.AccessDenied => unreachable, | ||
| 1007 | else => |e| return e, | ||
| 1008 | }; | ||
| 996 | const mount_points_struct = @ptrCast(*const MOUNTMGR_MOUNT_POINTS, &output_buf[0]); | 1009 | const mount_points_struct = @ptrCast(*const MOUNTMGR_MOUNT_POINTS, &output_buf[0]); |
| 997 | 1010 | ||
| 998 | const mount_points = @ptrCast( | 1011 | const mount_points = @ptrCast( |