authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-28 15:32:40-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-28 15:33:04-08:00
log57742480414b2060411b0b07f258e9187b4e8ca0
tree269b4b37df6cef940840e76cdebdcb89a0184e0d
parent757ec185f0eb91a15c4bdbe0201f0d998f30258c

std.os.windows: delete unused CreateSymbolicLink


2 files changed, 1 insertions(+), 119 deletions(-)

lib/std/Io/Threaded.zig+1-2
...@@ -6353,8 +6353,7 @@ fn dirSymLinkWindows(...@@ -6353,8 +6353,7 @@ fn dirSymLinkWindows(
63536353
6354 // Target path does not use sliceToPrefixedFileW because certain paths6354 // Target path does not use sliceToPrefixedFileW because certain paths
6355 // are handled differently when creating a symlink than they would be6355 // are handled differently when creating a symlink than they would be
6356 // when converting to an NT namespaced path. CreateSymbolicLink in6356 // when converting to an NT namespaced path.
6357 // symLinkW will handle the necessary conversion.
6358 var target_path_w: w.PathSpace = undefined;6357 var target_path_w: w.PathSpace = undefined;
6359 target_path_w.len = try w.wtf8ToWtf16Le(&target_path_w.data, target_path);6358 target_path_w.len = try w.wtf8ToWtf16Le(&target_path_w.data, target_path);
6360 target_path_w.data[target_path_w.len] = 0;6359 target_path_w.data[target_path_w.len] = 0;
lib/std/os/windows.zig-117
...@@ -2908,123 +2908,6 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {...@@ -2908,123 +2908,6 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
2908 return buffer[0..end_index];2908 return buffer[0..end_index];
2909}2909}
29102910
2911pub const CreateSymbolicLinkError = error{
2912 AccessDenied,
2913 PathAlreadyExists,
2914 FileNotFound,
2915 NameTooLong,
2916 NoDevice,
2917 NetworkNotFound,
2918 BadPathName,
2919 Unexpected,
2920};
2921
2922/// Needs either:
2923/// - `SeCreateSymbolicLinkPrivilege` privilege
2924/// or
2925/// - Developer mode on Windows 10
2926/// otherwise fails with `error.AccessDenied`. In which case `sym_link_path` may still
2927/// be created on the file system but will lack reparse processing data applied to it.
2928pub fn CreateSymbolicLink(
2929 dir: ?HANDLE,
2930 sym_link_path: []const u16,
2931 target_path: [:0]const u16,
2932 is_directory: bool,
2933) CreateSymbolicLinkError!void {
2934 const SYMLINK_DATA = extern struct {
2935 ReparseTag: IO_REPARSE_TAG,
2936 ReparseDataLength: USHORT,
2937 Reserved: USHORT,
2938 SubstituteNameOffset: USHORT,
2939 SubstituteNameLength: USHORT,
2940 PrintNameOffset: USHORT,
2941 PrintNameLength: USHORT,
2942 Flags: ULONG,
2943 };
2944
2945 const symlink_handle = OpenFile(sym_link_path, .{
2946 .access_mask = .{
2947 .STANDARD = .{ .SYNCHRONIZE = true },
2948 .GENERIC = .{ .WRITE = true, .READ = true },
2949 },
2950 .dir = dir,
2951 .creation = .CREATE,
2952 .filter = if (is_directory) .dir_only else .non_directory_only,
2953 }) catch |err| switch (err) {
2954 error.IsDir => return error.PathAlreadyExists,
2955 error.NotDir => return error.Unexpected,
2956 error.WouldBlock => return error.Unexpected,
2957 error.PipeBusy => return error.Unexpected,
2958 error.NoDevice => return error.Unexpected,
2959 error.AntivirusInterference => return error.Unexpected,
2960 else => |e| return e,
2961 };
2962 defer CloseHandle(symlink_handle);
2963
2964 // Relevant portions of the documentation:
2965 // > Relative links are specified using the following conventions:
2966 // > - Root relative—for example, "\Windows\System32" resolves to "current drive:\Windows\System32".
2967 // > - Current working directory–relative—for example, if the current working directory is
2968 // > C:\Windows\System32, "C:File.txt" resolves to "C:\Windows\System32\File.txt".
2969 // > Note: If you specify a current working directory–relative link, it is created as an absolute
2970 // > link, due to the way the current working directory is processed based on the user and the thread.
2971 // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
2972 var is_target_absolute = false;
2973 const final_target_path = target_path: {
2974 if (hasCommonNtPrefix(u16, target_path)) {
2975 // Already an NT path, no need to do anything to it
2976 break :target_path target_path;
2977 } else {
2978 switch (std.fs.path.getWin32PathType(u16, target_path)) {
2979 // Rooted paths need to avoid getting put through wToPrefixedFileW
2980 // (and they are treated as relative in this context)
2981 // Note: It seems that rooted paths in symbolic links are relative to
2982 // the drive that the symbolic exists on, not to the CWD's drive.
2983 // So, if the symlink is on C:\ and the CWD is on D:\,
2984 // it will still resolve the path relative to the root of
2985 // the C:\ drive.
2986 .rooted => break :target_path target_path,
2987 // Keep relative paths relative, but anything else needs to get NT-prefixed.
2988 else => if (!std.fs.path.isAbsoluteWindowsWtf16(target_path))
2989 break :target_path target_path,
2990 }
2991 }
2992 var prefixed_target_path = try wToPrefixedFileW(dir, target_path);
2993 // We do this after prefixing to ensure that drive-relative paths are treated as absolute
2994 is_target_absolute = std.fs.path.isAbsoluteWindowsWtf16(prefixed_target_path.span());
2995 break :target_path prefixed_target_path.span();
2996 };
2997
2998 // prepare reparse data buffer
2999 var buffer: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
3000 const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4;
3001 const header_len = @sizeOf(ULONG) + @sizeOf(USHORT) * 2;
3002 const target_is_absolute = std.fs.path.isAbsoluteWindowsWtf16(final_target_path);
3003 const symlink_data: SYMLINK_DATA = .{
3004 .ReparseTag = .SYMLINK,
3005 .ReparseDataLength = @intCast(buf_len - header_len),
3006 .Reserved = 0,
3007 .SubstituteNameOffset = @intCast(final_target_path.len * 2),
3008 .SubstituteNameLength = @intCast(final_target_path.len * 2),
3009 .PrintNameOffset = 0,
3010 .PrintNameLength = @intCast(final_target_path.len * 2),
3011 .Flags = if (!target_is_absolute) SYMLINK_FLAG_RELATIVE else 0,
3012 };
3013
3014 @memcpy(buffer[0..@sizeOf(SYMLINK_DATA)], std.mem.asBytes(&symlink_data));
3015 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
3016 const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
3017 @memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
3018 const rc = DeviceIoControl(symlink_handle, FSCTL.SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] });
3019 switch (rc) {
3020 .SUCCESS => {},
3021 .PRIVILEGE_NOT_HELD => return error.AccessDenied,
3022 .ACCESS_DENIED => return error.AccessDenied,
3023 .INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem
3024 else => return unexpectedStatus(rc),
3025 }
3026}
3027
3028pub const ReadLinkError = error{2911pub const ReadLinkError = error{
3029 FileNotFound,2912 FileNotFound,
3030 NetworkNotFound,2913 NetworkNotFound,