| 147 | |
| 148 | #[pyfunction] |
| 149 | pub(super) fn symlink(args: SymlinkArgs<'_>, vm: &VirtualMachine) -> PyResult<()> { |
| 150 | use crate::exceptions::ToOSErrorBuilder; |
| 151 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 152 | use windows_sys::Win32::Storage::FileSystem::WIN32_FILE_ATTRIBUTE_DATA; |
| 153 | use windows_sys::Win32::Storage::FileSystem::{ |
| 154 | CreateSymbolicLinkW, FILE_ATTRIBUTE_DIRECTORY, GetFileAttributesExW, |
| 155 | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, SYMBOLIC_LINK_FLAG_DIRECTORY, |
| 156 | }; |
| 157 | |
| 158 | static HAS_UNPRIVILEGED_FLAG: AtomicBool = AtomicBool::new(true); |
| 159 | |
| 160 | fn check_dir(src: &OsPath, dst: &OsPath) -> bool { |
| 161 | use windows_sys::Win32::Storage::FileSystem::GetFileExInfoStandard; |
| 162 | |
| 163 | let dst_parent = dst.as_path().parent(); |
| 164 | let Some(dst_parent) = dst_parent else { |
| 165 | return false; |
| 166 | }; |
| 167 | let resolved = if src.as_path().is_absolute() { |
| 168 | src.as_path().to_path_buf() |
| 169 | } else { |
| 170 | dst_parent.join(src.as_path()) |
| 171 | }; |
| 172 | let wide = match widestring::WideCString::from_os_str(&resolved) { |
| 173 | Ok(wide) => wide, |
| 174 | Err(_) => return false, |
| 175 | }; |
| 176 | let mut info: WIN32_FILE_ATTRIBUTE_DATA = unsafe { core::mem::zeroed() }; |
| 177 | let ok = unsafe { |
| 178 | GetFileAttributesExW( |
| 179 | wide.as_ptr(), |
| 180 | GetFileExInfoStandard, |
| 181 | &mut info as *mut _ as *mut _, |
| 182 | ) |
| 183 | }; |
| 184 | ok != 0 && (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 |
| 185 | } |
| 186 | |
| 187 | let mut flags = 0u32; |
| 188 | if HAS_UNPRIVILEGED_FLAG.load(Ordering::Relaxed) { |
| 189 | flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; |
| 190 | } |
| 191 | if args.target_is_directory.target_is_directory || check_dir(&args.src, &args.dst) { |
| 192 | flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; |
| 193 | } |
| 194 | |
| 195 | let src = args.src.to_wide_cstring(vm)?; |
| 196 | let dst = args.dst.to_wide_cstring(vm)?; |
| 197 | |
| 198 | let mut result = unsafe { CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) }; |
| 199 | if !result |
| 200 | && HAS_UNPRIVILEGED_FLAG.load(Ordering::Relaxed) |
| 201 | && unsafe { Foundation::GetLastError() } == Foundation::ERROR_INVALID_PARAMETER |
| 202 | { |
| 203 | let flags = flags & !SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; |
| 204 | result = unsafe { CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) }; |
| 205 | if result |
| 206 | || unsafe { Foundation::GetLastError() } != Foundation::ERROR_INVALID_PARAMETER |