authorgravatar for sahnvour@pm.meSahnvour <sahnvour@pm.me> 2020-08-27 20:55:47+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-08-30 06:52:53+02:00
logac85befbb4356daa774b6083a3aa853b9c78f548
treed3f9b6ddedc307adde0241b1c929ec266b0a106a
parente355bcce36fc5d704fede510002d4b22bf9594b7

handle lack of privilege to create symbolic links on windows


3 files changed, 60 insertions(+), 9 deletions(-)

lib/std/fs/test.zig+20-4
......@@ -25,12 +25,20 @@ test "Dir.readLink" {
2525
2626 {
2727 // 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 };
2933 try testReadLink(tmp.dir, "file.txt", "symlink1");
3034 }
3135 {
3236 // 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 };
3442 try testReadLink(tmp.dir, "subdir", "symlink2");
3543 }
3644}
......@@ -66,7 +74,11 @@ test "readLinkAbsolute" {
6674 const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink1" });
6775
6876 // 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 };
7082 try testReadLinkAbsolute(target_path, symlink_path);
7183 }
7284 {
......@@ -74,7 +86,11 @@ test "readLinkAbsolute" {
7486 const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink2" });
7587
7688 // 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 };
7894 try testReadLinkAbsolute(target_path, symlink_path);
7995 }
8096}
lib/std/os/test.zig+24-2
......@@ -125,7 +125,20 @@ test "symlink with relative paths" {
125125 try cwd.writeFile("file.txt", "nonsense");
126126
127127 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 };
129142 } else {
130143 try os.symlink("file.txt", "symlinked");
131144 }
......@@ -183,7 +196,16 @@ test "readlinkat" {
183196
184197 // create a symbolic link
185198 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 };
187209 } else {
188210 try os.symlinkat("file.txt", tmp.dir.fd, "link");
189211 }
lib/std/os/windows.zig+16-3
......@@ -164,7 +164,7 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16,
164164 }
165165}
166166
167pub const DeviceIoControlError = error{Unexpected};
167pub const DeviceIoControlError = error{ AccessDenied, Unexpected };
168168
169169/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
170170/// It implements similar behavior to `DeviceIoControl` and is meant to serve
......@@ -216,6 +216,7 @@ pub fn DeviceIoControl(
216216 };
217217 switch (rc) {
218218 .SUCCESS => {},
219 .PRIVILEGE_NOT_HELD => return error.AccessDenied,
219220 .INVALID_PARAMETER => unreachable,
220221 else => return unexpectedStatus(rc),
221222 }
......@@ -593,6 +594,12 @@ pub const CreateSymbolicLinkError = error{
593594 Unexpected,
594595};
595596
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.
596603pub fn CreateSymbolicLink(
597604 dir: ?HANDLE,
598605 sym_link_path: []const u16,
......@@ -710,7 +717,10 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
710717 defer CloseHandle(result_handle);
711718
712719 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 };
714724
715725 const reparse_struct = @ptrCast(*const REPARSE_DATA_BUFFER, @alignCast(@alignOf(REPARSE_DATA_BUFFER), &reparse_buf[0]));
716726 switch (reparse_struct.ReparseTag) {
......@@ -992,7 +1002,10 @@ pub fn GetFinalPathNameByHandle(
9921002 input_struct.DeviceNameLength = @intCast(USHORT, volume_name.FileNameLength);
9931003 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, &volume_name.FileName[0]), volume_name.FileNameLength);
9941004
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 };
9961009 const mount_points_struct = @ptrCast(*const MOUNTMGR_MOUNT_POINTS, &output_buf[0]);
9971010
9981011 const mount_points = @ptrCast(