Ensure we use Win32 prefix in Win32 calls

This commit is contained in:
Jakub Konka 2020-07-15 18:15:20 +02:00
parent cc9c5c5b0e
commit 3ab5e6b1a9
3 changed files with 59 additions and 29 deletions

View File

@ -1556,8 +1556,8 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8, flags: Symlin
@compileError("symlink is not supported in WASI; use symlinkat instead");
}
if (builtin.os.tag == .windows) {
const target_path_w = try windows.sliceToPrefixedFileW(target_path);
const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
const target_path_w = try windows.sliceToWin32PrefixedFileW(target_path);
const sym_link_path_w = try windows.sliceToWin32PrefixedFileW(sym_link_path);
return symlinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr, flags);
}
const target_path_c = try toPosixPath(target_path);
@ -1578,8 +1578,8 @@ pub fn symlinkW(target_path: [*:0]const u16, sym_link_path: [*:0]const u16, flag
/// See also `symlink`.
pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8, flags: SymlinkFlags) SymLinkError!void {
if (builtin.os.tag == .windows) {
const target_path_w = try windows.cStrToPrefixedFileW(target_path);
const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
const target_path_w = try windows.cStrToWin32PrefixedFileW(target_path);
const sym_link_path_w = try windows.cStrToWin32PrefixedFileW(sym_link_path);
return symlinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr);
}
switch (errno(system.symlink(target_path, sym_link_path))) {
@ -2382,7 +2382,7 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
if (builtin.os.tag == .wasi) {
@compileError("readlink is not supported in WASI; use readlinkat instead");
} else if (builtin.os.tag == .windows) {
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
const file_path_w = try windows.sliceToWin32PrefixedFileW(file_path);
return readlinkW(file_path_w.span().ptr, out_buffer);
} else {
const file_path_c = try toPosixPath(file_path);
@ -2448,7 +2448,7 @@ fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u
/// Same as `readlink` except `file_path` is null-terminated.
pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);
return readlinkW(file_path_w.span().ptr, out_buffer);
}
const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);

View File

@ -80,8 +80,6 @@ test "readlink" {
{
const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "file.txt" });
const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink1" });
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, .{ .is_directory = false });
@ -90,8 +88,6 @@ test "readlink" {
{
const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "subdir" });
const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink2" });
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, .{ .is_directory = true });
@ -108,7 +104,6 @@ test "readlink" {
fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
const given = try os.readlink(symlink_path, buffer[0..]);
std.debug.warn("given={}\n", .{given});
expect(mem.eql(u8, target_path, given));
}

View File

@ -1263,40 +1263,80 @@ pub const PathSpace = struct {
pub fn span(self: PathSpace) [:0]const u16 {
return self.data[0..self.len :0];
}
fn ensureNtStyle(self: *PathSpace) void {
// > File I/O functions in the Windows API convert "/" to "\" as part of
// > converting the name to an NT-style name, except when using the "\\?\"
// > prefix as detailed in the following sections.
// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
// Because we want the larger maximum path length for absolute paths, we
// convert forward slashes to backward slashes here.
for (self.data[0..self.len]) |*elem| {
if (elem.* == '/') {
elem.* = '\\';
}
}
self.data[self.len] = 0;
}
};
/// Same as `sliceToPrefixedFileW` but accepts a pointer
/// to a null-terminated path.
pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {
return sliceToPrefixedFileW(mem.spanZ(s));
}
/// Same as `sliceToWin32PrefixedFileW` but accepts a pointer
/// to a null-terminated path.
pub fn cStrToWin32PrefixedFileW(s: [*:0]const u8) !PathSpace {
return sliceToWin32PrefixedFileW(mem.spanZ(s));
}
/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,
/// it will get NT-style prefix `\??\` prepended automatically. For prepending
/// Win32-style prefix, see `sliceToWin32PrefixedFileW` instead.
pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {
// TODO https://github.com/ziglang/zig/issues/2765
var path_space: PathSpace = undefined;
for (s) |byte| {
const prefix_index: usize = if (mem.startsWith(u8, s, "\\??\\")) 4 else 0;
for (s[prefix_index..]) |byte| {
switch (byte) {
'*', '?', '"', '<', '>', '|' => return error.BadPathName,
else => {},
}
}
const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: {
const start_index = if (prefix_index > 0 or !std.fs.path.isAbsolute(s)) 0 else blk: {
const prefix = [_]u16{ '\\', '?', '?', '\\' };
mem.copy(u16, path_space.data[0..], &prefix);
break :blk prefix.len;
};
path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s);
if (path_space.len > path_space.data.len) return error.NameTooLong;
// > File I/O functions in the Windows API convert "/" to "\" as part of
// > converting the name to an NT-style name, except when using the "\\?\"
// > prefix as detailed in the following sections.
// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
// Because we want the larger maximum path length for absolute paths, we
// convert forward slashes to backward slashes here.
for (path_space.data[0..path_space.len]) |*elem| {
if (elem.* == '/') {
elem.* = '\\';
path_space.ensureNtStyle();
return path_space;
}
/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,
/// it will get Win32-style extended prefix `\\?\` prepended automatically. For prepending
/// NT-style prefix, see `sliceToPrefixedFileW` instead.
pub fn sliceToWin32PrefixedFileW(s: []const u8) !PathSpace {
// TODO https://github.com/ziglang/zig/issues/2765
var path_space: PathSpace = undefined;
const prefix_index: usize = if (mem.startsWith(u8, s, "\\\\?\\")) 4 else 0;
for (s[prefix_index..]) |byte| {
switch (byte) {
'*', '?', '"', '<', '>', '|' => return error.BadPathName,
else => {},
}
}
path_space.data[path_space.len] = 0;
const start_index = if (prefix_index > 0 or !std.fs.path.isAbsolute(s)) 0 else blk: {
const prefix = [_]u16{ '\\', '\\', '?', '\\' };
mem.copy(u16, path_space.data[0..], &prefix);
break :blk prefix.len;
};
path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s);
if (path_space.len > path_space.data.len) return error.NameTooLong;
path_space.ensureNtStyle();
return path_space;
}
@ -1313,12 +1353,7 @@ pub fn wToPrefixedFileW(s: []const u16) !PathSpace {
path_space.len = start_index + s.len;
if (path_space.len > path_space.data.len) return error.NameTooLong;
mem.copy(u16, path_space.data[start_index..], s);
for (path_space.data[0..path_space.len]) |*elem| {
if (elem.* == '/') {
elem.* = '\\';
}
}
path_space.data[path_space.len] = 0;
path_space.ensureNtStyle();
return path_space;
}