authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-14 23:30:05+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-22 08:51:22+02:00
logc47cb8d09f7cf1c02d3af59ebbca665ce78c85ca
treefebc011d21f115dfbd95fa0e8250e2c0327fc3ce
parentae8abedbeda33ff5cd93ca2fb21ef2f5453dfb37

Fix unlinkatW to allow file symlink deletion on Windows


3 files changed, 51 insertions(+), 42 deletions(-)

lib/std/os.zig+5-5
...@@ -1834,7 +1834,7 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr...@@ -1834,7 +1834,7 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr
1834 const create_options_flags = if (want_rmdir_behavior)1834 const create_options_flags = if (want_rmdir_behavior)
1835 @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_DIRECTORY_FILE)1835 @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_DIRECTORY_FILE)
1836 else1836 else
1837 @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE);1837 @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT); // would we ever want to delete the target instead?
18381838
1839 const path_len_bytes = @intCast(u16, mem.lenZ(sub_path_w) * 2);1839 const path_len_bytes = @intCast(u16, mem.lenZ(sub_path_w) * 2);
1840 var nt_name = w.UNICODE_STRING{1840 var nt_name = w.UNICODE_STRING{
...@@ -2371,7 +2371,7 @@ pub const ReadLinkError = error{...@@ -2371,7 +2371,7 @@ pub const ReadLinkError = error{
2371 InvalidUtf8,2371 InvalidUtf8,
2372 BadPathName,2372 BadPathName,
2373 /// Windows-only.2373 /// Windows-only.
2374 UnsupportedSymlinkType,2374 UnsupportedReparsePointType,
2375} || UnexpectedError;2375} || UnexpectedError;
23762376
2377/// Read value of a symbolic link.2377/// Read value of a symbolic link.
...@@ -2412,7 +2412,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8...@@ -2412,7 +2412,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8
2412 const reparse_struct = @ptrCast(*const w.REPARSE_DATA_BUFFER, @alignCast(@alignOf(w.REPARSE_DATA_BUFFER), &reparse_buf[0]));2412 const reparse_struct = @ptrCast(*const w.REPARSE_DATA_BUFFER, @alignCast(@alignOf(w.REPARSE_DATA_BUFFER), &reparse_buf[0]));
2413 switch (reparse_struct.ReparseTag) {2413 switch (reparse_struct.ReparseTag) {
2414 w.IO_REPARSE_TAG_SYMLINK => {2414 w.IO_REPARSE_TAG_SYMLINK => {
2415 const buf = @ptrCast(*const w.SymbolicLinkReparseBuffer, @alignCast(@alignOf(w.SymbolicLinkReparseBuffer), &reparse_struct.DataBuffer[0]));2415 const buf = @ptrCast(*const w.SYMBOLIC_LINK_REPARSE_BUFFER, @alignCast(@alignOf(w.SYMBOLIC_LINK_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
2416 const offset = buf.SubstituteNameOffset >> 1;2416 const offset = buf.SubstituteNameOffset >> 1;
2417 const len = buf.SubstituteNameLength >> 1;2417 const len = buf.SubstituteNameLength >> 1;
2418 const path_buf = @as([*]const u16, &buf.PathBuffer);2418 const path_buf = @as([*]const u16, &buf.PathBuffer);
...@@ -2420,7 +2420,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8...@@ -2420,7 +2420,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8
2420 return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer);2420 return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer);
2421 },2421 },
2422 w.IO_REPARSE_TAG_MOUNT_POINT => {2422 w.IO_REPARSE_TAG_MOUNT_POINT => {
2423 const buf = @ptrCast(*const w.MountPointReparseBuffer, @alignCast(@alignOf(w.MountPointReparseBuffer), &reparse_struct.DataBuffer[0]));2423 const buf = @ptrCast(*const w.MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(w.MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
2424 const offset = buf.SubstituteNameOffset >> 1;2424 const offset = buf.SubstituteNameOffset >> 1;
2425 const len = buf.SubstituteNameLength >> 1;2425 const len = buf.SubstituteNameLength >> 1;
2426 const path_buf = @as([*]const u16, &buf.PathBuffer);2426 const path_buf = @as([*]const u16, &buf.PathBuffer);
...@@ -2428,7 +2428,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8...@@ -2428,7 +2428,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8
2428 },2428 },
2429 else => |value| {2429 else => |value| {
2430 std.debug.warn("unsupported symlink type: {}", .{value});2430 std.debug.warn("unsupported symlink type: {}", .{value});
2431 return error.UnsupportedSymlinkType;2431 return error.UnsupportedReparsePointType;
2432 },2432 },
2433 }2433 }
2434}2434}
lib/std/os/test.zig+44-35
...@@ -43,42 +43,51 @@ test "fstatat" {...@@ -43,42 +43,51 @@ test "fstatat" {
4343
44test "readlink" {44test "readlink" {
45 if (builtin.os.tag == .wasi) return error.SkipZigTest;45 if (builtin.os.tag == .wasi) return error.SkipZigTest;
46
47 var cwd = fs.cwd();
48 try cwd.writeFile("file.txt", "nonsense");
49 try os.symlink("file.txt", "symlinked");
5046
51 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;47 // First, try relative paths
52 const given = try os.readlink("symlinked", buffer[0..]);48 {
53 expect(mem.eql(u8, "file.txt", given));49 var cwd = fs.cwd();
5450 try cwd.writeFile("file.txt", "nonsense");
55 // var tmp = tmpDir(.{});51 try os.symlink("file.txt", "symlinked");
56 // defer tmp.cleanup();52
5753 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
58 // // create file54 const given = try os.readlink("symlinked", buffer[0..]);
59 // try tmp.dir.writeFile("file.txt", "nonsense");55 expect(mem.eql(u8, "file.txt", given));
6056
61 // // get paths57 try cwd.deleteFile("file.txt");
62 // // TODO: use Dir's realpath function once that exists58 try cwd.deleteFile("symlinked");
63 // var arena = ArenaAllocator.init(testing.allocator);59 }
64 // defer arena.deinit();60
6561 // Next, let's try fully-qualified paths
66 // const base_path = blk: {62 {
67 // const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..]});63 var tmp = tmpDir(.{});
68 // break :blk try fs.realpathAlloc(&arena.allocator, relative_path);64 // defer tmp.cleanup();
69 // };65
70 // const target_path = try fs.path.join(&arena.allocator, &[_][]const u8{base_path, "file.txt"});66 // create file
71 // const symlink_path = try fs.path.join(&arena.allocator, &[_][]const u8{base_path, "symlinked"});67 try tmp.dir.writeFile("file.txt", "nonsense");
72 // std.debug.warn("\ntarget_path={}\n", .{target_path});68
73 // std.debug.warn("symlink_path={}\n", .{symlink_path});69 // get paths
7470 // TODO: use Dir's realpath function once that exists
75 // // create symbolic link by path71 var arena = ArenaAllocator.init(testing.allocator);
76 // try os.symlink(target_path, symlink_path);72 defer arena.deinit();
7773
78 // // now, read the link and verify74 const base_path = blk: {
79 // var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;75 const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
80 // const given = try os.readlink(symlink_path, buffer[0..]);76 break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
81 // expect(mem.eql(u8, symlink_path, given));77 };
78 const target_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "file.txt" });
79 const symlink_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "symlinked" });
80 std.debug.warn("\ntarget_path={}\n", .{target_path});
81 std.debug.warn("symlink_path={}\n", .{symlink_path});
82
83 // create symbolic link by path
84 try os.symlink(target_path, symlink_path);
85
86 // now, read the link and verify
87 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
88 const given = try os.readlink(symlink_path, buffer[0..]);
89 expect(mem.eql(u8, symlink_path, given));
90 }
82}91}
8392
84test "readlinkat" {93test "readlinkat" {
lib/std/os/windows/bits.zig+2-2
...@@ -1549,7 +1549,7 @@ pub const REPARSE_DATA_BUFFER = extern struct {...@@ -1549,7 +1549,7 @@ pub const REPARSE_DATA_BUFFER = extern struct {
1549 Reserved: USHORT,1549 Reserved: USHORT,
1550 DataBuffer: [1]UCHAR,1550 DataBuffer: [1]UCHAR,
1551};1551};
1552pub const SymbolicLinkReparseBuffer = extern struct {1552pub const SYMBOLIC_LINK_REPARSE_BUFFER = extern struct {
1553 SubstituteNameOffset: USHORT,1553 SubstituteNameOffset: USHORT,
1554 SubstituteNameLength: USHORT,1554 SubstituteNameLength: USHORT,
1555 PrintNameOffset: USHORT,1555 PrintNameOffset: USHORT,
...@@ -1557,7 +1557,7 @@ pub const SymbolicLinkReparseBuffer = extern struct {...@@ -1557,7 +1557,7 @@ pub const SymbolicLinkReparseBuffer = extern struct {
1557 Flags: ULONG,1557 Flags: ULONG,
1558 PathBuffer: [1]WCHAR,1558 PathBuffer: [1]WCHAR,
1559};1559};
1560pub const MountPointReparseBuffer = extern struct {1560pub const MOUNT_POINT_REPARSE_BUFFER = extern struct {
1561 SubstituteNameOffset: USHORT,1561 SubstituteNameOffset: USHORT,
1562 SubstituteNameLength: USHORT,1562 SubstituteNameLength: USHORT,
1563 PrintNameOffset: USHORT,1563 PrintNameOffset: USHORT,