Fix unlinkatW to allow file symlink deletion on Windows
This commit is contained in:
@@ -1834,7 +1834,7 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr
|
||||
const create_options_flags = if (want_rmdir_behavior)
|
||||
@as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_DIRECTORY_FILE)
|
||||
else
|
||||
@as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE);
|
||||
@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?
|
||||
|
||||
const path_len_bytes = @intCast(u16, mem.lenZ(sub_path_w) * 2);
|
||||
var nt_name = w.UNICODE_STRING{
|
||||
@@ -2371,7 +2371,7 @@ pub const ReadLinkError = error{
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
/// Windows-only.
|
||||
UnsupportedSymlinkType,
|
||||
UnsupportedReparsePointType,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// Read value of a symbolic link.
|
||||
@@ -2412,7 +2412,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8
|
||||
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 => {
|
||||
const buf = @ptrCast(*const w.SymbolicLinkReparseBuffer, @alignCast(@alignOf(w.SymbolicLinkReparseBuffer), &reparse_struct.DataBuffer[0]));
|
||||
const buf = @ptrCast(*const w.SYMBOLIC_LINK_REPARSE_BUFFER, @alignCast(@alignOf(w.SYMBOLIC_LINK_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
|
||||
const offset = buf.SubstituteNameOffset >> 1;
|
||||
const len = buf.SubstituteNameLength >> 1;
|
||||
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
|
||||
return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer);
|
||||
},
|
||||
w.IO_REPARSE_TAG_MOUNT_POINT => {
|
||||
const buf = @ptrCast(*const w.MountPointReparseBuffer, @alignCast(@alignOf(w.MountPointReparseBuffer), &reparse_struct.DataBuffer[0]));
|
||||
const buf = @ptrCast(*const w.MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(w.MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
|
||||
const offset = buf.SubstituteNameOffset >> 1;
|
||||
const len = buf.SubstituteNameLength >> 1;
|
||||
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
|
||||
},
|
||||
else => |value| {
|
||||
std.debug.warn("unsupported symlink type: {}", .{value});
|
||||
return error.UnsupportedSymlinkType;
|
||||
return error.UnsupportedReparsePointType;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,42 +43,51 @@ test "fstatat" {
|
||||
|
||||
test "readlink" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
var cwd = fs.cwd();
|
||||
try cwd.writeFile("file.txt", "nonsense");
|
||||
try os.symlink("file.txt", "symlinked");
|
||||
|
||||
var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
const given = try os.readlink("symlinked", buffer[0..]);
|
||||
expect(mem.eql(u8, "file.txt", given));
|
||||
// First, try relative paths
|
||||
{
|
||||
var cwd = fs.cwd();
|
||||
try cwd.writeFile("file.txt", "nonsense");
|
||||
try os.symlink("file.txt", "symlinked");
|
||||
|
||||
// var tmp = tmpDir(.{});
|
||||
// defer tmp.cleanup();
|
||||
var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
const given = try os.readlink("symlinked", buffer[0..]);
|
||||
expect(mem.eql(u8, "file.txt", given));
|
||||
|
||||
// // create file
|
||||
// try tmp.dir.writeFile("file.txt", "nonsense");
|
||||
try cwd.deleteFile("file.txt");
|
||||
try cwd.deleteFile("symlinked");
|
||||
}
|
||||
|
||||
// // get paths
|
||||
// // TODO: use Dir's realpath function once that exists
|
||||
// var arena = ArenaAllocator.init(testing.allocator);
|
||||
// defer arena.deinit();
|
||||
// Next, let's try fully-qualified paths
|
||||
{
|
||||
var tmp = tmpDir(.{});
|
||||
// defer tmp.cleanup();
|
||||
|
||||
// const base_path = blk: {
|
||||
// const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..]});
|
||||
// break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
|
||||
// };
|
||||
// const target_path = try fs.path.join(&arena.allocator, &[_][]const u8{base_path, "file.txt"});
|
||||
// const symlink_path = try fs.path.join(&arena.allocator, &[_][]const u8{base_path, "symlinked"});
|
||||
// std.debug.warn("\ntarget_path={}\n", .{target_path});
|
||||
// std.debug.warn("symlink_path={}\n", .{symlink_path});
|
||||
// create file
|
||||
try tmp.dir.writeFile("file.txt", "nonsense");
|
||||
|
||||
// // create symbolic link by path
|
||||
// try os.symlink(target_path, symlink_path);
|
||||
// get paths
|
||||
// TODO: use Dir's realpath function once that exists
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
// // now, read the link and verify
|
||||
// var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
// const given = try os.readlink(symlink_path, buffer[0..]);
|
||||
// expect(mem.eql(u8, symlink_path, given));
|
||||
const base_path = blk: {
|
||||
const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
|
||||
break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
|
||||
};
|
||||
const target_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "file.txt" });
|
||||
const symlink_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "symlinked" });
|
||||
std.debug.warn("\ntarget_path={}\n", .{target_path});
|
||||
std.debug.warn("symlink_path={}\n", .{symlink_path});
|
||||
|
||||
// create symbolic link by path
|
||||
try os.symlink(target_path, symlink_path);
|
||||
|
||||
// now, read the link and verify
|
||||
var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
const given = try os.readlink(symlink_path, buffer[0..]);
|
||||
expect(mem.eql(u8, symlink_path, given));
|
||||
}
|
||||
}
|
||||
|
||||
test "readlinkat" {
|
||||
|
||||
@@ -1549,7 +1549,7 @@ pub const REPARSE_DATA_BUFFER = extern struct {
|
||||
Reserved: USHORT,
|
||||
DataBuffer: [1]UCHAR,
|
||||
};
|
||||
pub const SymbolicLinkReparseBuffer = extern struct {
|
||||
pub const SYMBOLIC_LINK_REPARSE_BUFFER = extern struct {
|
||||
SubstituteNameOffset: USHORT,
|
||||
SubstituteNameLength: USHORT,
|
||||
PrintNameOffset: USHORT,
|
||||
@@ -1557,7 +1557,7 @@ pub const SymbolicLinkReparseBuffer = extern struct {
|
||||
Flags: ULONG,
|
||||
PathBuffer: [1]WCHAR,
|
||||
};
|
||||
pub const MountPointReparseBuffer = extern struct {
|
||||
pub const MOUNT_POINT_REPARSE_BUFFER = extern struct {
|
||||
SubstituteNameOffset: USHORT,
|
||||
SubstituteNameLength: USHORT,
|
||||
PrintNameOffset: USHORT,
|
||||
|
||||
Reference in New Issue
Block a user