Commit 99f0e64fa0
Changed files (1)
lib
std
lib/std/os/windows.zig
@@ -604,7 +604,7 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
pub const CreateSymbolicLinkError = error{ AccessDenied, PathAlreadyExists, FileNotFound, NameTooLong, InvalidUtf8, BadPathName, Unexpected };
-pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: []const u16, target_path: []const u16) CreateSymbolicLinkError!void {
+pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: [:0]const u16, target_path: [:0]const u16, is_directory: bool) CreateSymbolicLinkError!void {
const SYMLINK_DATA = extern struct {
ReparseTag: ULONG,
ReparseDataLength: USHORT,
@@ -616,15 +616,67 @@ pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: []const u16, target_pa
Flags: ULONG,
};
- const symlink_handle = OpenFile(sym_link_path, .{
- .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
- .dir = dir,
- .creation = FILE_CREATE,
- .io_mode = .blocking,
- }) catch |err| switch (err) {
- error.IsDir => unreachable, // TODO
- else => |e| unreachable,
- };
+ var symlink_handle: HANDLE = undefined;
+ if (is_directory) {
+ const sym_link_len_bytes = math.cast(u16, sym_link_path.len * 2) catch |err| switch (err) {
+ error.Overflow => return error.NameTooLong,
+ };
+ var nt_name = UNICODE_STRING{
+ .Length = sym_link_len_bytes,
+ .MaximumLength = sym_link_len_bytes,
+ .Buffer = @intToPtr([*]u16, @ptrToInt(sym_link_path.ptr)),
+ };
+
+ if (sym_link_path[0] == '.' and sym_link_path[1] == 0) {
+ // Windows does not recognize this, but it does work with empty string.
+ nt_name.Length = 0;
+ }
+
+ var attr = OBJECT_ATTRIBUTES{
+ .Length = @sizeOf(OBJECT_ATTRIBUTES),
+ .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sym_link_path)) null else dir,
+ .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
+ .ObjectName = &nt_name,
+ .SecurityDescriptor = null,
+ .SecurityQualityOfService = null,
+ };
+
+ var io: IO_STATUS_BLOCK = undefined;
+ const rc = ntdll.NtCreateFile(
+ &symlink_handle,
+ GENERIC_READ | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
+ &attr,
+ &io,
+ null,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_CREATE,
+ FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
+ 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.NoDevice,
+ .INVALID_PARAMETER => unreachable,
+ .ACCESS_DENIED => return error.AccessDenied,
+ .OBJECT_PATH_SYNTAX_BAD => unreachable,
+ .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
+ else => return unexpectedStatus(rc),
+ }
+ } else {
+ symlink_handle = OpenFile(sym_link_path, .{
+ .access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
+ .dir = dir,
+ .creation = FILE_CREATE,
+ .io_mode = .blocking,
+ }) catch |err| switch (err) {
+ else => |e| unreachable,
+ };
+ }
defer CloseHandle(symlink_handle);
// prepare reparse data buffer