std: finish moving os.windows.ReadLink logic to Io.Threaded

- remove error.SharingViolation from all error sets since it has the
  same meaning as FileBusy
- add error.FileBusy to CreateFileAtomicError and ReadLinkError
- update dirReadLinkWindows to use NtCreateFile and NtFsControlFile and
  integrate with cancelation properly.
- move windows CTL_CODE constants to the proper namespace
- delete os.windows.ReadLink
This commit is contained in:
Andrew Kelley 2026-01-28 16:18:43 -08:00
parent 5774248041
commit 18c6abc0ba
7 changed files with 218 additions and 173 deletions

View file

@ -940,6 +940,7 @@ pub const RenameError = error{
/// Attempted to replace a nonempty directory.
DirNotEmpty,
PermissionDenied,
/// The file attempted to be moved or replaced is a running executable.
FileBusy,
DiskQuota,
IsDir,
@ -952,7 +953,6 @@ pub const RenameError = error{
ReadOnlyFileSystem,
CrossDevice,
NoDevice,
SharingViolation,
PipeBusy,
/// On Windows, `\\server` or `\\server\share` was not found.
NetworkNotFound,
@ -1167,6 +1167,8 @@ pub const ReadLinkError = error{
/// intercepts file system operations and makes them significantly slower
/// in addition to possibly failing with this error code.
AntivirusInterference,
/// File attempted to be opened is a running executable.
FileBusy,
} || PathNameError || Io.Cancelable || Io.UnexpectedError;
/// Obtain target of a symbolic link.
@ -1791,6 +1793,8 @@ pub const CreateFileAtomicError = error{
NotDir,
WouldBlock,
ReadOnlyFileSystem,
/// The file attempted to be created is a running executable.
FileBusy,
} || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
/// Create an unnamed ephemeral file that can eventually be atomically

View file

@ -249,7 +249,6 @@ pub const CreateFlags = struct {
};
pub const OpenError = error{
SharingViolation,
PipeBusy,
NoDevice,
/// On Windows, `\\server` or `\\server\share` was not found.
@ -757,7 +756,7 @@ pub const RealPathError = error{
NoSpaceLeft,
FileSystem,
DeviceBusy,
SharingViolation,
FileBusy,
PipeBusy,
/// On Windows, `\\server` or `\\server\share` was not found.
NetworkNotFound,

View file

@ -3635,7 +3635,7 @@ fn dirCreateFileWindows(
// after an executable file is closed. Here we work around the
// kernel bug with retry attempts.
syscall.finish();
if (max_attempts - attempt == 0) return error.SharingViolation;
if (max_attempts - attempt == 0) return error.FileBusy;
try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
attempt += 1;
syscall = try .start();
@ -3648,7 +3648,7 @@ fn dirCreateFileWindows(
// call has failed. Here, we simulate the kernel bug being
// fixed by sleeping and retrying until the error goes away.
syscall.finish();
if (max_attempts - attempt == 0) return error.SharingViolation;
if (max_attempts - attempt == 0) return error.FileBusy;
try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
attempt += 1;
syscall = try .start();
@ -3668,10 +3668,10 @@ fn dirCreateFileWindows(
.NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
.USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
.VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
.INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
.OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
.INVALID_HANDLE => |err| return syscall.ntstatusBug(err),
else => |err| return syscall.unexpectedNtstatus(err),
.INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
.OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
.INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
else => |status| return syscall.unexpectedNtstatus(status),
};
errdefer windows.CloseHandle(handle);
@ -3819,7 +3819,6 @@ fn dirCreateFileAtomic(
error.DiskQuota,
error.PathAlreadyExists,
error.LinkQuotaExceeded,
error.SharingViolation,
error.PipeBusy,
error.FileTooBig,
error.DeviceBusy,
@ -3889,11 +3888,9 @@ fn dirCreateFileAtomic(
error.DiskQuota,
error.PathAlreadyExists,
error.LinkQuotaExceeded,
error.SharingViolation,
error.PipeBusy,
error.FileTooBig,
error.FileLocksUnsupported,
error.FileBusy,
error.DeviceBusy,
=> return error.Unexpected,
@ -3926,7 +3923,6 @@ fn atomicFileInit(
error.PathAlreadyExists => continue,
error.DeviceBusy => continue,
error.FileBusy => continue,
error.SharingViolation => continue,
error.IsDir => return error.Unexpected, // No path components.
error.FileTooBig => return error.Unexpected, // Creating, not opening.
@ -4236,7 +4232,7 @@ pub fn dirOpenFileWtf16(
// after an executable file is closed. Here we work around the
// kernel bug with retry attempts.
syscall.finish();
if (max_attempts - attempt == 0) return error.SharingViolation;
if (max_attempts - attempt == 0) return error.FileBusy;
try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
attempt += 1;
syscall = try .start();
@ -4258,7 +4254,7 @@ pub fn dirOpenFileWtf16(
// call has failed. Here, we simulate the kernel bug being
// fixed by sleeping and retrying until the error goes away.
syscall.finish();
if (max_attempts - attempt == 0) return error.SharingViolation;
if (max_attempts - attempt == 0) return error.FileBusy;
try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
attempt += 1;
syscall = try .start();
@ -6464,7 +6460,7 @@ fn dirSymLinkWindows(
@memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
@memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
const rc = w.DeviceIoControl(symlink_handle, w.FSCTL.SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] });
const rc = w.DeviceIoControl(symlink_handle, .SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] });
switch (rc) {
.SUCCESS => {},
.PRIVILEGE_NOT_HELD => return error.PermissionDenied,
@ -6571,44 +6567,189 @@ fn dirSymLinkPosix(
}
}
const dirReadLink = switch (native_os) {
.windows => dirReadLinkWindows,
.wasi => dirReadLinkWasi,
else => dirReadLinkPosix,
};
fn dirReadLinkWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
fn dirReadLink(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
const w = windows;
switch (native_os) {
.windows => return dirReadLinkWindows(dir, sub_path, buffer),
.wasi => return dirReadLinkWasi(dir, sub_path, buffer),
else => return dirReadLinkPosix(dir, sub_path, buffer),
}
}
fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
// This gets used once for `sub_path` and then reused again temporarily
// before converting back to `buffer`.
var sub_path_w_buf = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
const sub_path_w = sub_path_w_buf.span();
const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
var nt_name: windows.UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
const attr: windows.OBJECT_ATTRIBUTES = .{
.Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.Attributes = .{
.INHERIT = false,
},
.ObjectName = &nt_name,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
var result_handle: windows.HANDLE = undefined;
const syscall: Syscall = try .start();
const result_w = while (true) {
if (w.ReadLink(dir.handle, sub_path_w_buf.span(), &sub_path_w_buf.data)) |res| {
// There are multiple kernel bugs being worked around with retries.
const max_attempts = 13;
var attempt: u5 = 0;
var syscall: Syscall = try .start();
while (true) switch (windows.ntdll.NtCreateFile(
&result_handle,
.{
.SPECIFIC = .{ .FILE = .{
.READ_ATTRIBUTES = true,
} },
.STANDARD = .{ .SYNCHRONIZE = true },
},
&attr,
&io_status_block,
null,
.{ .NORMAL = true },
.VALID_FLAGS,
.OPEN,
.{
.DIRECTORY_FILE = false,
.NON_DIRECTORY_FILE = false,
.IO = .ASYNCHRONOUS,
.OPEN_REPARSE_POINT = true,
},
null,
0,
)) {
.SUCCESS => {
syscall.finish();
break res;
} else |err| switch (err) {
error.OperationCanceled => {
try syscall.checkCancel();
continue;
},
else => |e| return syscall.fail(e),
}
break;
},
.CANCELLED => {
try syscall.checkCancel();
continue;
},
.SHARING_VIOLATION => {
// This occurs if the file attempting to be opened is a running
// executable. However, there's a kernel bug: the error may be
// incorrectly returned for an indeterminate amount of time
// after an executable file is closed. Here we work around the
// kernel bug with retry attempts.
syscall.finish();
if (max_attempts - attempt == 0) return error.FileBusy;
try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
attempt += 1;
syscall = try .start();
continue;
},
.DELETE_PENDING => {
// This error means that there *was* a file in this location on
// the file system, but it was deleted. However, the OS is not
// finished with the deletion operation, and so this CreateFile
// call has failed. Here, we simulate the kernel bug being
// fixed by sleeping and retrying until the error goes away.
syscall.finish();
if (max_attempts - attempt == 0) return error.FileBusy;
try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
attempt += 1;
syscall = try .start();
continue;
},
.OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
.OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
.OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
.BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
.BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
.NO_MEDIA_IN_DEVICE => return syscall.fail(error.FileNotFound),
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
.PIPE_BUSY => return syscall.fail(error.AccessDenied),
.PIPE_NOT_AVAILABLE => return syscall.fail(error.FileNotFound),
.USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
.VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
.INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
.OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
.INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
else => |status| return syscall.unexpectedNtstatus(status),
};
defer windows.CloseHandle(result_handle);
var reparse_buf: [windows.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(windows.REPARSE_DATA_BUFFER)) = undefined;
syscall = try .start();
while (true) switch (windows.ntdll.NtFsControlFile(
result_handle,
null, // event
null, // APC routine
null, // APC context
&io_status_block,
.GET_REPARSE_POINT,
null, // input buffer
0, // input buffer length
&reparse_buf,
reparse_buf.len,
)) {
.SUCCESS => {
syscall.finish();
break;
},
.CANCELLED => {
try syscall.checkCancel();
continue;
},
.NOT_A_REPARSE_POINT => return syscall.fail(error.NotLink),
else => |status| return syscall.unexpectedNtstatus(status),
};
const reparse_struct: *const windows.REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf));
const IoReparseTagInt = @typeInfo(windows.IO_REPARSE_TAG).@"struct".backing_integer.?;
const result_w = switch (@as(IoReparseTagInt, @bitCast(reparse_struct.ReparseTag))) {
@as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.SYMLINK)) => r: {
const buf: *const windows.SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
const offset = buf.SubstituteNameOffset >> 1;
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
const is_relative = buf.Flags & windows.SYMLINK_FLAG_RELATIVE != 0;
break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w_buf.data);
},
@as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.MOUNT_POINT)) => r: {
const buf: *const windows.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
const offset = buf.SubstituteNameOffset >> 1;
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w_buf.data);
},
else => return error.UnsupportedReparsePointType,
};
const len = std.unicode.calcWtf8Len(result_w);
if (len > buffer.len) return error.NameTooLong;
return std.unicode.wtf16LeToWtf8(buffer, result_w);
}
fn dirReadLinkWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
if (builtin.link_libc) return dirReadLinkPosix(userdata, dir, sub_path, buffer);
fn parseReadLinkPath(path: []const u16, is_relative: bool, out_buffer: []u16) error{NameTooLong}![]u16 {
path: {
if (is_relative) break :path;
return windows.ntToWin32Namespace(path, out_buffer) catch |err| switch (err) {
error.NameTooLong => |e| return e,
error.NotNtPath => break :path,
};
}
if (out_buffer.len < path.len) return error.NameTooLong;
const dest = out_buffer[0..path.len];
@memcpy(dest, path);
return dest;
}
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
fn dirReadLinkWasi(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
if (builtin.link_libc) return dirReadLinkPosix(dir, sub_path, buffer);
var n: usize = undefined;
const syscall: Syscall = try .start();
@ -6643,10 +6784,7 @@ fn dirReadLinkWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer
}
}
fn dirReadLinkPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
fn dirReadLinkPosix(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
var sub_path_buffer: [posix.PATH_MAX]u8 = undefined;
const sub_path_posix = try pathToPosix(sub_path, &sub_path_buffer);
@ -8708,45 +8846,41 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
const symlink_path = std.mem.sliceTo(&symlink_path_buf, 0);
return Io.Dir.realPathFileAbsolute(ioBasic(t), symlink_path, out_buffer) catch |err| switch (err) {
error.NetworkNotFound => unreachable, // Windows-only
error.FileBusy => unreachable, // Windows-only
else => |e| return e,
};
},
.linux, .serenity => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/exe", out_buffer) catch |err| switch (err) {
error.UnsupportedReparsePointType => unreachable, // Windows-only
error.NetworkNotFound => unreachable, // Windows-only
error.FileBusy => unreachable, // Windows-only
else => |e| return e,
},
.illumos => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
error.UnsupportedReparsePointType => unreachable, // Windows-only
error.NetworkNotFound => unreachable, // Windows-only
error.FileBusy => unreachable, // Windows-only
else => |e| return e,
},
.freebsd, .dragonfly => {
var mib: [4]c_int = .{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
var out_len: usize = out_buffer.len;
const syscall: Syscall = try .start();
while (true) {
switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {
.SUCCESS => {
syscall.finish();
return out_len - 1; // discard terminating NUL
},
.INTR => {
try syscall.checkCancel();
continue;
},
else => |e| {
syscall.finish();
switch (e) {
.FAULT => |err| return errnoBug(err),
.PERM => return error.PermissionDenied,
.NOMEM => return error.SystemResources,
.NOENT => |err| return errnoBug(err),
else => |err| return posix.unexpectedErrno(err),
}
},
}
}
while (true) switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {
.SUCCESS => {
syscall.finish();
return out_len - 1; // discard terminating NUL
},
.INTR => {
try syscall.checkCancel();
continue;
},
.PERM => return syscall.fail(error.PermissionDenied),
.NOMEM => return syscall.fail(error.SystemResources),
.FAULT => |err| return syscall.errnoBug(err),
.NOENT => |err| return syscall.errnoBug(err),
else => |err| return syscall.unexpectedErrno(err),
};
},
.netbsd => {
var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
@ -8762,16 +8896,11 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
try syscall.checkCancel();
continue;
},
else => |e| {
syscall.finish();
switch (e) {
.FAULT => |err| return errnoBug(err),
.PERM => return error.PermissionDenied,
.NOMEM => return error.SystemResources,
.NOENT => |err| return errnoBug(err),
else => |err| return posix.unexpectedErrno(err),
}
},
.PERM => return syscall.fail(error.PermissionDenied),
.NOMEM => return syscall.fail(error.SystemResources),
.FAULT => |err| return syscall.errnoBug(err),
.NOENT => |err| return syscall.errnoBug(err),
else => |err| return syscall.unexpectedErrno(err),
}
}
},

View file

@ -335,7 +335,6 @@ const Module = struct {
error.NoSpaceLeft,
error.DeviceBusy,
error.NoDevice,
error.SharingViolation,
error.PathAlreadyExists,
error.PipeBusy,
error.NetworkNotFound,

View file

@ -1135,19 +1135,7 @@ pub const CTL_CODE = packed struct(ULONG) {
_,
};
};
pub const IOCTL = struct {
pub const KSEC = struct {
pub const GEN_RANDOM: CTL_CODE = .{ .DeviceType = .KSEC, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
};
pub const MOUNTMGR = struct {
pub const QUERY_POINTS: CTL_CODE = .{ .DeviceType = .MOUNTMGRCONTROLTYPE, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
pub const QUERY_DOS_VOLUME_PATH: CTL_CODE = .{ .DeviceType = .MOUNTMGRCONTROLTYPE, .Function = 12, .Method = .BUFFERED, .Access = .ANY };
};
};
pub const FSCTL = struct {
pub const SET_REPARSE_POINT: CTL_CODE = .{ .DeviceType = .FILE_SYSTEM, .Function = 41, .Method = .BUFFERED, .Access = .SPECIAL };
pub const GET_REPARSE_POINT: CTL_CODE = .{ .DeviceType = .FILE_SYSTEM, .Function = 42, .Method = .BUFFERED, .Access = .ANY };
@ -1177,6 +1165,16 @@ pub const FSCTL = struct {
};
};
pub const IOCTL = struct {
pub const KSEC = struct {
pub const GEN_RANDOM: CTL_CODE = .{ .DeviceType = .KSEC, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
};
pub const MOUNTMGR = struct {
pub const QUERY_POINTS: CTL_CODE = .{ .DeviceType = .MOUNTMGRCONTROLTYPE, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
pub const QUERY_DOS_VOLUME_PATH: CTL_CODE = .{ .DeviceType = .MOUNTMGRCONTROLTYPE, .Function = 12, .Method = .BUFFERED, .Access = .ANY };
};
};
pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: ULONG = 16 * 1024;
pub const IO_REPARSE_TAG = packed struct(ULONG) {
@ -2908,88 +2906,6 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
return buffer[0..end_index];
}
pub const ReadLinkError = error{
FileNotFound,
NetworkNotFound,
AccessDenied,
Unexpected,
NameTooLong,
BadPathName,
AntivirusInterference,
UnsupportedReparsePointType,
NotLink,
OperationCanceled,
};
/// `sub_path_w` will never be accessed after `out_buffer` has been written to, so it
/// is safe to reuse a single buffer for both.
pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLinkError![]u16 {
const result_handle = OpenFile(sub_path_w, .{
.access_mask = .{
.SPECIFIC = .{ .FILE = .{
.READ_ATTRIBUTES = true,
} },
.STANDARD = .{ .SYNCHRONIZE = true },
},
.dir = dir,
.creation = .OPEN,
.follow_symlinks = false,
.filter = .any,
}) 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,
};
defer CloseHandle(result_handle);
var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;
const rc = DeviceIoControl(result_handle, FSCTL.GET_REPARSE_POINT, .{ .out = reparse_buf[0..] });
switch (rc) {
.SUCCESS => {},
.CANCELLED => return error.OperationCanceled,
.NOT_A_REPARSE_POINT => return error.NotLink,
else => return unexpectedStatus(rc),
}
const reparse_struct: *const REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0]));
const IoReparseTagInt = @typeInfo(IO_REPARSE_TAG).@"struct".backing_integer.?;
switch (@as(IoReparseTagInt, @bitCast(reparse_struct.ReparseTag))) {
@as(IoReparseTagInt, @bitCast(IO_REPARSE_TAG.SYMLINK)) => {
const buf: *const SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
const offset = buf.SubstituteNameOffset >> 1;
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
return parseReadLinkPath(path_buf[offset..][0..len], is_relative, out_buffer);
},
@as(IoReparseTagInt, @bitCast(IO_REPARSE_TAG.MOUNT_POINT)) => {
const buf: *const MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
const offset = buf.SubstituteNameOffset >> 1;
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
return parseReadLinkPath(path_buf[offset..][0..len], false, out_buffer);
},
else => return error.UnsupportedReparsePointType,
}
}
fn parseReadLinkPath(path: []const u16, is_relative: bool, out_buffer: []u16) error{NameTooLong}![]u16 {
path: {
if (is_relative) break :path;
return ntToWin32Namespace(path, out_buffer) catch |err| switch (err) {
error.NameTooLong => |e| return e,
error.NotNtPath => break :path,
};
}
if (out_buffer.len < path.len) return error.NameTooLong;
const dest = out_buffer[0..path.len];
@memcpy(dest, path);
return dest;
}
pub const DeleteFileError = error{
FileNotFound,
AccessDenied,

View file

@ -704,7 +704,6 @@ pub const ExecutablePathBaseError = error{
FileSystem,
BadPathName,
DeviceBusy,
SharingViolation,
PipeBusy,
NotLink,
PathAlreadyExists,

View file

@ -723,6 +723,7 @@ fn abiAndDynamicLinkerFromFile(
error.UnsupportedReparsePointType => unreachable, // Windows only
error.NetworkNotFound => unreachable, // Windows only
error.AntivirusInterference => unreachable, // Windows only
error.FileBusy => unreachable, // Windows only
error.AccessDenied,
error.PermissionDenied,
@ -844,7 +845,6 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
error.NameTooLong => return error.Unexpected,
error.BadPathName => return error.Unexpected,
error.PipeBusy => return error.Unexpected, // Windows-only
error.SharingViolation => return error.Unexpected, // Windows-only
error.NetworkNotFound => return error.Unexpected, // Windows-only
error.AntivirusInterference => return error.Unexpected, // Windows-only
error.FileLocksUnsupported => return error.Unexpected, // No lock requested.
@ -1052,7 +1052,6 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
error.NoSpaceLeft => return error.Unexpected,
error.NameTooLong => return error.Unexpected,
error.PathAlreadyExists => return error.Unexpected,
error.SharingViolation => return error.Unexpected,
error.BadPathName => return error.Unexpected,
error.PipeBusy => return error.Unexpected,
error.FileLocksUnsupported => return error.Unexpected,