mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Merge pull request #25539 from squeek502/windows-readlinkw
windows: Make readLinkW APIs output WTF-16, reduce stack usage of callers
This commit is contained in:
commit
aa4332fb0e
@ -1361,8 +1361,14 @@ pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ReadLinkError![]u
|
|||||||
return self.readLinkWasi(sub_path, buffer);
|
return self.readLinkWasi(sub_path, buffer);
|
||||||
}
|
}
|
||||||
if (native_os == .windows) {
|
if (native_os == .windows) {
|
||||||
const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
|
var sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
|
||||||
return self.readLinkW(sub_path_w.span(), buffer);
|
const result_w = try self.readLinkW(sub_path_w.span(), &sub_path_w.data);
|
||||||
|
|
||||||
|
const len = std.unicode.calcWtf8Len(result_w);
|
||||||
|
if (len > buffer.len) return error.NameTooLong;
|
||||||
|
|
||||||
|
const end_index = std.unicode.wtf16LeToWtf8(buffer, result_w);
|
||||||
|
return buffer[0..end_index];
|
||||||
}
|
}
|
||||||
const sub_path_c = try posix.toPosixPath(sub_path);
|
const sub_path_c = try posix.toPosixPath(sub_path);
|
||||||
return self.readLinkZ(&sub_path_c, buffer);
|
return self.readLinkZ(&sub_path_c, buffer);
|
||||||
@ -1376,15 +1382,24 @@ pub fn readLinkWasi(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
|
|||||||
/// Same as `readLink`, except the `sub_path_c` parameter is null-terminated.
|
/// Same as `readLink`, except the `sub_path_c` parameter is null-terminated.
|
||||||
pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
|
pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
|
||||||
if (native_os == .windows) {
|
if (native_os == .windows) {
|
||||||
const sub_path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path_c);
|
var sub_path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path_c);
|
||||||
return self.readLinkW(sub_path_w.span(), buffer);
|
const result_w = try self.readLinkW(sub_path_w.span(), &sub_path_w.data);
|
||||||
|
|
||||||
|
const len = std.unicode.calcWtf8Len(result_w);
|
||||||
|
if (len > buffer.len) return error.NameTooLong;
|
||||||
|
|
||||||
|
const end_index = std.unicode.wtf16LeToWtf8(buffer, result_w);
|
||||||
|
return buffer[0..end_index];
|
||||||
}
|
}
|
||||||
return posix.readlinkatZ(self.fd, sub_path_c, buffer);
|
return posix.readlinkatZ(self.fd, sub_path_c, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Windows-only. Same as `readLink` except the pathname parameter
|
/// Windows-only. Same as `readLink` except the path parameter
|
||||||
/// is WTF16 LE encoded.
|
/// is WTF-16 LE encoded, NT-prefixed.
|
||||||
pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
|
///
|
||||||
|
/// `sub_path_w` will never be accessed after `buffer` has been written to, so it
|
||||||
|
/// is safe to reuse a single buffer for both.
|
||||||
|
pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u16) ![]u16 {
|
||||||
return windows.ReadLink(self.fd, sub_path_w, buffer);
|
return windows.ReadLink(self.fd, sub_path_w, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -193,10 +193,16 @@ test "Dir.readLink" {
|
|||||||
// test 1: symlink to a file
|
// test 1: symlink to a file
|
||||||
try setupSymlink(ctx.dir, file_target_path, "symlink1", .{});
|
try setupSymlink(ctx.dir, file_target_path, "symlink1", .{});
|
||||||
try testReadLink(ctx.dir, canonical_file_target_path, "symlink1");
|
try testReadLink(ctx.dir, canonical_file_target_path, "symlink1");
|
||||||
|
if (builtin.os.tag == .windows) {
|
||||||
|
try testReadLinkW(testing.allocator, ctx.dir, canonical_file_target_path, "symlink1");
|
||||||
|
}
|
||||||
|
|
||||||
// test 2: symlink to a directory (can be different on Windows)
|
// test 2: symlink to a directory (can be different on Windows)
|
||||||
try setupSymlink(ctx.dir, dir_target_path, "symlink2", .{ .is_directory = true });
|
try setupSymlink(ctx.dir, dir_target_path, "symlink2", .{ .is_directory = true });
|
||||||
try testReadLink(ctx.dir, canonical_dir_target_path, "symlink2");
|
try testReadLink(ctx.dir, canonical_dir_target_path, "symlink2");
|
||||||
|
if (builtin.os.tag == .windows) {
|
||||||
|
try testReadLinkW(testing.allocator, ctx.dir, canonical_dir_target_path, "symlink2");
|
||||||
|
}
|
||||||
|
|
||||||
// test 3: relative path symlink
|
// test 3: relative path symlink
|
||||||
const parent_file = ".." ++ fs.path.sep_str ++ "target.txt";
|
const parent_file = ".." ++ fs.path.sep_str ++ "target.txt";
|
||||||
@ -205,6 +211,9 @@ test "Dir.readLink" {
|
|||||||
defer subdir.close();
|
defer subdir.close();
|
||||||
try setupSymlink(subdir, canonical_parent_file, "relative-link.txt", .{});
|
try setupSymlink(subdir, canonical_parent_file, "relative-link.txt", .{});
|
||||||
try testReadLink(subdir, canonical_parent_file, "relative-link.txt");
|
try testReadLink(subdir, canonical_parent_file, "relative-link.txt");
|
||||||
|
if (builtin.os.tag == .windows) {
|
||||||
|
try testReadLinkW(testing.allocator, subdir, canonical_parent_file, "relative-link.txt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.impl);
|
}.impl);
|
||||||
}
|
}
|
||||||
@ -215,6 +224,17 @@ fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !vo
|
|||||||
try testing.expectEqualStrings(target_path, actual);
|
try testing.expectEqualStrings(target_path, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn testReadLinkW(allocator: mem.Allocator, dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {
|
||||||
|
const target_path_w = try std.unicode.wtf8ToWtf16LeAlloc(allocator, target_path);
|
||||||
|
defer allocator.free(target_path_w);
|
||||||
|
// Calling the W functions directly requires the path to be NT-prefixed
|
||||||
|
const symlink_path_w = try std.os.windows.sliceToPrefixedFileW(dir.fd, symlink_path);
|
||||||
|
const wtf16_buffer = try allocator.alloc(u16, target_path_w.len);
|
||||||
|
defer allocator.free(wtf16_buffer);
|
||||||
|
const actual = try dir.readLinkW(symlink_path_w.span(), wtf16_buffer);
|
||||||
|
try testing.expectEqualSlices(u16, target_path_w, actual);
|
||||||
|
}
|
||||||
|
|
||||||
fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void {
|
fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void {
|
||||||
var buffer: [fs.max_path_bytes]u8 = undefined;
|
var buffer: [fs.max_path_bytes]u8 = undefined;
|
||||||
const given = try fs.readLinkAbsolute(symlink_path, buffer[0..]);
|
const given = try fs.readLinkAbsolute(symlink_path, buffer[0..]);
|
||||||
|
|||||||
@ -889,61 +889,28 @@ pub const ReadLinkError = error{
|
|||||||
AccessDenied,
|
AccessDenied,
|
||||||
Unexpected,
|
Unexpected,
|
||||||
NameTooLong,
|
NameTooLong,
|
||||||
|
BadPathName,
|
||||||
|
AntivirusInterference,
|
||||||
UnsupportedReparsePointType,
|
UnsupportedReparsePointType,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
/// `sub_path_w` will never be accessed after `out_buffer` has been written to, so it
|
||||||
// Here, we use `NtCreateFile` to shave off one syscall if we were to use `OpenFile` wrapper.
|
/// is safe to reuse a single buffer for both.
|
||||||
// With the latter, we'd need to call `NtCreateFile` twice, once for file symlink, and if that
|
pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLinkError![]u16 {
|
||||||
// failed, again for dir symlink. Omitting any mention of file/dir flags makes it possible
|
const result_handle = OpenFile(sub_path_w, .{
|
||||||
// to open the symlink there and then.
|
.access_mask = FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||||||
const path_len_bytes = math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
|
.dir = dir,
|
||||||
var nt_name = UNICODE_STRING{
|
.creation = FILE_OPEN,
|
||||||
.Length = path_len_bytes,
|
.follow_symlinks = false,
|
||||||
.MaximumLength = path_len_bytes,
|
.filter = .any,
|
||||||
.Buffer = @constCast(sub_path_w.ptr),
|
}) catch |err| switch (err) {
|
||||||
|
error.IsDir, error.NotDir => return error.Unexpected, // filter = .any
|
||||||
|
error.PathAlreadyExists => return error.Unexpected, // FILE_OPEN
|
||||||
|
error.WouldBlock => return error.Unexpected,
|
||||||
|
error.NoDevice => return error.FileNotFound,
|
||||||
|
error.PipeBusy => return error.AccessDenied,
|
||||||
|
else => |e| return e,
|
||||||
};
|
};
|
||||||
var attr = OBJECT_ATTRIBUTES{
|
|
||||||
.Length = @sizeOf(OBJECT_ATTRIBUTES),
|
|
||||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir,
|
|
||||||
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
|
||||||
.ObjectName = &nt_name,
|
|
||||||
.SecurityDescriptor = null,
|
|
||||||
.SecurityQualityOfService = null,
|
|
||||||
};
|
|
||||||
var result_handle: HANDLE = undefined;
|
|
||||||
var io: IO_STATUS_BLOCK = undefined;
|
|
||||||
|
|
||||||
const rc = ntdll.NtCreateFile(
|
|
||||||
&result_handle,
|
|
||||||
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
||||||
&attr,
|
|
||||||
&io,
|
|
||||||
null,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
||||||
FILE_OPEN,
|
|
||||||
FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
||||||
null,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
switch (rc) {
|
|
||||||
.SUCCESS => {},
|
|
||||||
.OBJECT_NAME_INVALID => unreachable,
|
|
||||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
|
||||||
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
|
||||||
.NO_MEDIA_IN_DEVICE => return error.FileNotFound,
|
|
||||||
.BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
|
|
||||||
.BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
|
|
||||||
.INVALID_PARAMETER => unreachable,
|
|
||||||
.SHARING_VIOLATION => return error.AccessDenied,
|
|
||||||
.ACCESS_DENIED => return error.AccessDenied,
|
|
||||||
.PIPE_BUSY => return error.AccessDenied,
|
|
||||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
|
||||||
.OBJECT_NAME_COLLISION => unreachable,
|
|
||||||
.FILE_IS_A_DIRECTORY => unreachable,
|
|
||||||
else => return unexpectedStatus(rc),
|
|
||||||
}
|
|
||||||
defer CloseHandle(result_handle);
|
defer CloseHandle(result_handle);
|
||||||
|
|
||||||
var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;
|
var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;
|
||||||
@ -961,35 +928,33 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
|
|||||||
const len = buf.SubstituteNameLength >> 1;
|
const len = buf.SubstituteNameLength >> 1;
|
||||||
const path_buf = @as([*]const u16, &buf.PathBuffer);
|
const path_buf = @as([*]const u16, &buf.PathBuffer);
|
||||||
const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
|
const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
|
||||||
return parseReadlinkPath(path_buf[offset..][0..len], is_relative, out_buffer);
|
return parseReadLinkPath(path_buf[offset..][0..len], is_relative, out_buffer);
|
||||||
},
|
},
|
||||||
IO_REPARSE_TAG_MOUNT_POINT => {
|
IO_REPARSE_TAG_MOUNT_POINT => {
|
||||||
const buf: *const MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
|
const buf: *const MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
|
||||||
const offset = buf.SubstituteNameOffset >> 1;
|
const offset = buf.SubstituteNameOffset >> 1;
|
||||||
const len = buf.SubstituteNameLength >> 1;
|
const len = buf.SubstituteNameLength >> 1;
|
||||||
const path_buf = @as([*]const u16, &buf.PathBuffer);
|
const path_buf = @as([*]const u16, &buf.PathBuffer);
|
||||||
return parseReadlinkPath(path_buf[offset..][0..len], false, out_buffer);
|
return parseReadLinkPath(path_buf[offset..][0..len], false, out_buffer);
|
||||||
},
|
},
|
||||||
else => |value| {
|
else => {
|
||||||
std.debug.print("unsupported symlink type: {}", .{value});
|
|
||||||
return error.UnsupportedReparsePointType;
|
return error.UnsupportedReparsePointType;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asserts that there is enough space is `out_buffer`.
|
fn parseReadLinkPath(path: []const u16, is_relative: bool, out_buffer: []u16) error{NameTooLong}![]u16 {
|
||||||
/// The result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
path: {
|
||||||
fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u8 {
|
if (is_relative) break :path;
|
||||||
const win32_namespace_path = path: {
|
return ntToWin32Namespace(path, out_buffer) catch |err| switch (err) {
|
||||||
if (is_relative) break :path path;
|
error.NameTooLong => |e| return e,
|
||||||
const win32_path = ntToWin32Namespace(path) catch |err| switch (err) {
|
error.NotNtPath => break :path,
|
||||||
error.NameTooLong => unreachable,
|
|
||||||
error.NotNtPath => break :path path,
|
|
||||||
};
|
};
|
||||||
break :path win32_path.span();
|
}
|
||||||
};
|
if (out_buffer.len < path.len) return error.NameTooLong;
|
||||||
const out_len = std.unicode.wtf16LeToWtf8(out_buffer, win32_namespace_path);
|
const dest = out_buffer[0..path.len];
|
||||||
return out_buffer[0..out_len];
|
@memcpy(dest, path);
|
||||||
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DeleteFileError = error{
|
pub const DeleteFileError = error{
|
||||||
@ -2620,10 +2585,11 @@ test getUnprefixedPathType {
|
|||||||
/// https://github.com/reactos/reactos/blob/master/modules/rostests/apitests/ntdll/RtlNtPathNameToDosPathName.c
|
/// https://github.com/reactos/reactos/blob/master/modules/rostests/apitests/ntdll/RtlNtPathNameToDosPathName.c
|
||||||
///
|
///
|
||||||
/// `path` should be encoded as WTF-16LE.
|
/// `path` should be encoded as WTF-16LE.
|
||||||
pub fn ntToWin32Namespace(path: []const u16) !PathSpace {
|
///
|
||||||
|
/// Supports in-place modification (`path` and `out` may refer to the same slice).
|
||||||
|
pub fn ntToWin32Namespace(path: []const u16, out: []u16) error{ NameTooLong, NotNtPath }![]u16 {
|
||||||
if (path.len > PATH_MAX_WIDE) return error.NameTooLong;
|
if (path.len > PATH_MAX_WIDE) return error.NameTooLong;
|
||||||
|
|
||||||
var path_space: PathSpace = undefined;
|
|
||||||
const namespace_prefix = getNamespacePrefix(u16, path);
|
const namespace_prefix = getNamespacePrefix(u16, path);
|
||||||
switch (namespace_prefix) {
|
switch (namespace_prefix) {
|
||||||
.nt => {
|
.nt => {
|
||||||
@ -2631,23 +2597,19 @@ pub fn ntToWin32Namespace(path: []const u16) !PathSpace {
|
|||||||
var after_prefix = path[4..]; // after the `\??\`
|
var after_prefix = path[4..]; // after the `\??\`
|
||||||
// The prefix \??\UNC\ means this is a UNC path, in which case the
|
// The prefix \??\UNC\ means this is a UNC path, in which case the
|
||||||
// `\??\UNC\` should be replaced by `\\` (two backslashes)
|
// `\??\UNC\` should be replaced by `\\` (two backslashes)
|
||||||
// TODO: the "UNC" should technically be matched case-insensitively, but
|
|
||||||
// it's unlikely to matter since most/all paths passed into this
|
|
||||||
// function will have come from the OS meaning it should have
|
|
||||||
// the 'canonical' uppercase UNC.
|
|
||||||
const is_unc = after_prefix.len >= 4 and
|
const is_unc = after_prefix.len >= 4 and
|
||||||
std.mem.eql(u16, after_prefix[0..3], std.unicode.utf8ToUtf16LeStringLiteral("UNC")) and
|
eqlIgnoreCaseWTF16(after_prefix[0..3], std.unicode.utf8ToUtf16LeStringLiteral("UNC")) and
|
||||||
std.fs.path.PathType.windows.isSep(u16, std.mem.littleToNative(u16, after_prefix[3]));
|
std.fs.path.PathType.windows.isSep(u16, std.mem.littleToNative(u16, after_prefix[3]));
|
||||||
|
const win32_len = path.len - @as(usize, if (is_unc) 6 else 4);
|
||||||
|
if (out.len < win32_len) return error.NameTooLong;
|
||||||
if (is_unc) {
|
if (is_unc) {
|
||||||
path_space.data[0] = comptime std.mem.nativeToLittle(u16, '\\');
|
out[0] = comptime std.mem.nativeToLittle(u16, '\\');
|
||||||
dest_index += 1;
|
dest_index += 1;
|
||||||
// We want to include the last `\` of `\??\UNC\`
|
// We want to include the last `\` of `\??\UNC\`
|
||||||
after_prefix = path[7..];
|
after_prefix = path[7..];
|
||||||
}
|
}
|
||||||
@memcpy(path_space.data[dest_index..][0..after_prefix.len], after_prefix);
|
@memmove(out[dest_index..][0..after_prefix.len], after_prefix);
|
||||||
path_space.len = dest_index + after_prefix.len;
|
return out[0..win32_len];
|
||||||
path_space.data[path_space.len] = 0;
|
|
||||||
return path_space;
|
|
||||||
},
|
},
|
||||||
else => return error.NotNtPath,
|
else => return error.NotNtPath,
|
||||||
}
|
}
|
||||||
@ -2656,25 +2618,14 @@ pub fn ntToWin32Namespace(path: []const u16) !PathSpace {
|
|||||||
test ntToWin32Namespace {
|
test ntToWin32Namespace {
|
||||||
const L = std.unicode.utf8ToUtf16LeStringLiteral;
|
const L = std.unicode.utf8ToUtf16LeStringLiteral;
|
||||||
|
|
||||||
try testNtToWin32Namespace(L("UNC"), L("\\??\\UNC"));
|
var mutable_unc_path_buf = L("\\??\\UNC\\path1\\path2").*;
|
||||||
try testNtToWin32Namespace(L("\\\\"), L("\\??\\UNC\\"));
|
try std.testing.expectEqualSlices(u16, L("\\\\path1\\path2"), try ntToWin32Namespace(&mutable_unc_path_buf, &mutable_unc_path_buf));
|
||||||
try testNtToWin32Namespace(L("\\\\path1"), L("\\??\\UNC\\path1"));
|
|
||||||
try testNtToWin32Namespace(L("\\\\path1\\path2"), L("\\??\\UNC\\path1\\path2"));
|
|
||||||
|
|
||||||
try testNtToWin32Namespace(L(""), L("\\??\\"));
|
var mutable_path_buf = L("\\??\\C:\\test\\").*;
|
||||||
try testNtToWin32Namespace(L("C:"), L("\\??\\C:"));
|
try std.testing.expectEqualSlices(u16, L("C:\\test\\"), try ntToWin32Namespace(&mutable_path_buf, &mutable_path_buf));
|
||||||
try testNtToWin32Namespace(L("C:\\"), L("\\??\\C:\\"));
|
|
||||||
try testNtToWin32Namespace(L("C:\\test"), L("\\??\\C:\\test"));
|
|
||||||
try testNtToWin32Namespace(L("C:\\test\\"), L("\\??\\C:\\test\\"));
|
|
||||||
|
|
||||||
try std.testing.expectError(error.NotNtPath, ntToWin32Namespace(L("foo")));
|
var too_small_buf: [6]u16 = undefined;
|
||||||
try std.testing.expectError(error.NotNtPath, ntToWin32Namespace(L("C:\\test")));
|
try std.testing.expectError(error.NameTooLong, ntToWin32Namespace(L("\\??\\C:\\test"), &too_small_buf));
|
||||||
try std.testing.expectError(error.NotNtPath, ntToWin32Namespace(L("\\\\.\\test")));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn testNtToWin32Namespace(expected: []const u16, path: []const u16) !void {
|
|
||||||
const converted = try ntToWin32Namespace(path);
|
|
||||||
try std.testing.expectEqualSlices(u16, expected, converted.span());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getFullPathNameW(path: [*:0]const u16, out: []u16) !usize {
|
fn getFullPathNameW(path: [*:0]const u16, out: []u16) !usize {
|
||||||
|
|||||||
@ -3001,6 +3001,12 @@ pub const ReadLinkError = error{
|
|||||||
UnsupportedReparsePointType,
|
UnsupportedReparsePointType,
|
||||||
/// On Windows, `\\server` or `\\server\share` was not found.
|
/// On Windows, `\\server` or `\\server\share` was not found.
|
||||||
NetworkNotFound,
|
NetworkNotFound,
|
||||||
|
/// On Windows, antivirus software is enabled by default. It can be
|
||||||
|
/// disabled, but Windows Update sometimes ignores the user's preference
|
||||||
|
/// and re-enables it. When enabled, antivirus software on Windows
|
||||||
|
/// intercepts file system operations and makes them significantly slower
|
||||||
|
/// in addition to possibly failing with this error code.
|
||||||
|
AntivirusInterference,
|
||||||
} || UnexpectedError;
|
} || UnexpectedError;
|
||||||
|
|
||||||
/// Read value of a symbolic link.
|
/// Read value of a symbolic link.
|
||||||
@ -3015,26 +3021,42 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
|||||||
if (native_os == .wasi and !builtin.link_libc) {
|
if (native_os == .wasi and !builtin.link_libc) {
|
||||||
return readlinkat(AT.FDCWD, file_path, out_buffer);
|
return readlinkat(AT.FDCWD, file_path, out_buffer);
|
||||||
} else if (native_os == .windows) {
|
} else if (native_os == .windows) {
|
||||||
const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
|
var file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
|
||||||
return readlinkW(file_path_w.span(), out_buffer);
|
const result_w = try readlinkW(file_path_w.span(), &file_path_w.data);
|
||||||
|
|
||||||
|
const len = std.unicode.calcWtf8Len(result_w);
|
||||||
|
if (len > out_buffer.len) return error.NameTooLong;
|
||||||
|
|
||||||
|
const end_index = std.unicode.wtf16LeToWtf8(out_buffer, result_w);
|
||||||
|
return out_buffer[0..end_index];
|
||||||
} else {
|
} else {
|
||||||
const file_path_c = try toPosixPath(file_path);
|
const file_path_c = try toPosixPath(file_path);
|
||||||
return readlinkZ(&file_path_c, out_buffer);
|
return readlinkZ(&file_path_c, out_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Windows-only. Same as `readlink` except `file_path` is WTF16 LE encoded.
|
/// Windows-only. Same as `readlink` except `file_path` is WTF-16 LE encoded, NT-prefixed.
|
||||||
/// The result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
/// The result is encoded as WTF-16 LE.
|
||||||
|
///
|
||||||
|
/// `file_path` will never be accessed after `out_buffer` has been written to, so it
|
||||||
|
/// is safe to reuse a single buffer for both.
|
||||||
|
///
|
||||||
/// See also `readlinkZ`.
|
/// See also `readlinkZ`.
|
||||||
pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
pub fn readlinkW(file_path: []const u16, out_buffer: []u16) ReadLinkError![]u16 {
|
||||||
return windows.ReadLink(fs.cwd().fd, file_path, out_buffer);
|
return windows.ReadLink(fs.cwd().fd, file_path, out_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `readlink` except `file_path` is null-terminated.
|
/// Same as `readlink` except `file_path` is null-terminated.
|
||||||
pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||||
if (native_os == .windows) {
|
if (native_os == .windows) {
|
||||||
const file_path_w = try windows.cStrToPrefixedFileW(null, file_path);
|
var file_path_w = try windows.cStrToPrefixedFileW(null, file_path);
|
||||||
return readlinkW(file_path_w.span(), out_buffer);
|
const result_w = try readlinkW(file_path_w.span(), &file_path_w.data);
|
||||||
|
|
||||||
|
const len = std.unicode.calcWtf8Len(result_w);
|
||||||
|
if (len > out_buffer.len) return error.NameTooLong;
|
||||||
|
|
||||||
|
const end_index = std.unicode.wtf16LeToWtf8(out_buffer, result_w);
|
||||||
|
return out_buffer[0..end_index];
|
||||||
} else if (native_os == .wasi and !builtin.link_libc) {
|
} else if (native_os == .wasi and !builtin.link_libc) {
|
||||||
return readlink(mem.sliceTo(file_path, 0), out_buffer);
|
return readlink(mem.sliceTo(file_path, 0), out_buffer);
|
||||||
}
|
}
|
||||||
@ -3069,8 +3091,14 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink
|
|||||||
return readlinkatWasi(dirfd, file_path, out_buffer);
|
return readlinkatWasi(dirfd, file_path, out_buffer);
|
||||||
}
|
}
|
||||||
if (native_os == .windows) {
|
if (native_os == .windows) {
|
||||||
const file_path_w = try windows.sliceToPrefixedFileW(dirfd, file_path);
|
var file_path_w = try windows.sliceToPrefixedFileW(dirfd, file_path);
|
||||||
return readlinkatW(dirfd, file_path_w.span(), out_buffer);
|
const result_w = try readlinkatW(dirfd, file_path_w.span(), &file_path_w.data);
|
||||||
|
|
||||||
|
const len = std.unicode.calcWtf8Len(result_w);
|
||||||
|
if (len > out_buffer.len) return error.NameTooLong;
|
||||||
|
|
||||||
|
const end_index = std.unicode.wtf16LeToWtf8(out_buffer, result_w);
|
||||||
|
return out_buffer[0..end_index];
|
||||||
}
|
}
|
||||||
const file_path_c = try toPosixPath(file_path);
|
const file_path_c = try toPosixPath(file_path);
|
||||||
return readlinkatZ(dirfd, &file_path_c, out_buffer);
|
return readlinkatZ(dirfd, &file_path_c, out_buffer);
|
||||||
@ -3097,10 +3125,14 @@ 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 LE encoded.
|
/// Windows-only. Same as `readlinkat` except `file_path` WTF16 LE encoded, NT-prefixed.
|
||||||
/// The result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
/// The result is encoded as WTF-16 LE.
|
||||||
|
///
|
||||||
|
/// `file_path` will never be accessed after `out_buffer` has been written to, so it
|
||||||
|
/// is safe to reuse a single buffer for both.
|
||||||
|
///
|
||||||
/// See also `readlinkat`.
|
/// See also `readlinkat`.
|
||||||
pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u16) ReadLinkError![]u16 {
|
||||||
return windows.ReadLink(dirfd, file_path, out_buffer);
|
return windows.ReadLink(dirfd, file_path, out_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3108,8 +3140,14 @@ pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLi
|
|||||||
/// See also `readlinkat`.
|
/// See also `readlinkat`.
|
||||||
pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||||
if (native_os == .windows) {
|
if (native_os == .windows) {
|
||||||
const file_path_w = try windows.cStrToPrefixedFileW(dirfd, file_path);
|
var file_path_w = try windows.cStrToPrefixedFileW(dirfd, file_path);
|
||||||
return readlinkatW(dirfd, file_path_w.span(), out_buffer);
|
const result_w = try readlinkatW(dirfd, file_path_w.span(), &file_path_w.data);
|
||||||
|
|
||||||
|
const len = std.unicode.calcWtf8Len(result_w);
|
||||||
|
if (len > out_buffer.len) return error.NameTooLong;
|
||||||
|
|
||||||
|
const end_index = std.unicode.wtf16LeToWtf8(out_buffer, result_w);
|
||||||
|
return out_buffer[0..end_index];
|
||||||
} else if (native_os == .wasi and !builtin.link_libc) {
|
} else if (native_os == .wasi and !builtin.link_libc) {
|
||||||
return readlinkat(dirfd, mem.sliceTo(file_path, 0), out_buffer);
|
return readlinkat(dirfd, mem.sliceTo(file_path, 0), out_buffer);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -705,6 +705,7 @@ fn abiAndDynamicLinkerFromFile(
|
|||||||
error.BadPathName => unreachable, // Windows only
|
error.BadPathName => unreachable, // Windows only
|
||||||
error.UnsupportedReparsePointType => unreachable, // Windows only
|
error.UnsupportedReparsePointType => unreachable, // Windows only
|
||||||
error.NetworkNotFound => unreachable, // Windows only
|
error.NetworkNotFound => unreachable, // Windows only
|
||||||
|
error.AntivirusInterference => unreachable, // Windows only
|
||||||
|
|
||||||
error.AccessDenied,
|
error.AccessDenied,
|
||||||
error.PermissionDenied,
|
error.PermissionDenied,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user