|
| 1 | +use core::ptr::addr_of; |
| 2 | + |
1 | 3 | use crate::os::windows::prelude::*; |
2 | 4 |
|
3 | 5 | use crate::borrow::Cow; |
4 | | -use crate::ffi::{c_void, OsString}; |
| 6 | +use crate::ffi::{c_void, OsStr, OsString}; |
5 | 7 | use crate::fmt; |
6 | 8 | use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; |
7 | 9 | use crate::mem::{self, MaybeUninit}; |
@@ -1446,75 +1448,79 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { |
1446 | 1448 | Ok(size as u64) |
1447 | 1449 | } |
1448 | 1450 |
|
1449 | | -#[allow(dead_code)] |
1450 | | -pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>( |
1451 | | - original: P, |
1452 | | - junction: Q, |
1453 | | -) -> io::Result<()> { |
1454 | | - symlink_junction_inner(original.as_ref(), junction.as_ref()) |
1455 | | -} |
1456 | | - |
1457 | | -// Creating a directory junction on windows involves dealing with reparse |
1458 | | -// points and the DeviceIoControl function, and this code is a skeleton of |
1459 | | -// what can be found here: |
1460 | | -// |
1461 | | -// http://www.flexhex.com/docs/articles/hard-links.phtml |
1462 | | -#[allow(dead_code)] |
1463 | | -fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { |
1464 | | - let d = DirBuilder::new(); |
1465 | | - d.mkdir(junction)?; |
1466 | | - |
| 1451 | +pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { |
| 1452 | + // Create and open a new directory in one go. |
1467 | 1453 | let mut opts = OpenOptions::new(); |
| 1454 | + opts.create_new(true); |
1468 | 1455 | opts.write(true); |
1469 | | - opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); |
1470 | | - let f = File::open(junction, &opts)?; |
1471 | | - let h = f.as_inner().as_raw_handle(); |
1472 | | - unsafe { |
1473 | | - let mut data = |
1474 | | - Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]); |
1475 | | - let data_ptr = data.0.as_mut_ptr(); |
1476 | | - let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize); |
1477 | | - let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>(); |
1478 | | - // Zero the header to ensure it's fully initialized, including reserved parameters. |
1479 | | - *db = mem::zeroed(); |
1480 | | - let reparse_target_slice = { |
1481 | | - let buf_start = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>(); |
1482 | | - // Compute offset in bytes and then divide so that we round down |
1483 | | - // rather than hit any UB (admittedly this arithmetic should work |
1484 | | - // out so that this isn't necessary) |
1485 | | - let buf_len_bytes = usize::try_from(data_end.byte_offset_from(buf_start)).unwrap(); |
1486 | | - let buf_len_wchars = buf_len_bytes / core::mem::size_of::<c::WCHAR>(); |
1487 | | - core::slice::from_raw_parts_mut(buf_start, buf_len_wchars) |
1488 | | - }; |
1489 | | - |
1490 | | - // FIXME: this conversion is very hacky |
1491 | | - let iter = br"\??\" |
1492 | | - .iter() |
1493 | | - .map(|x| *x as u16) |
1494 | | - .chain(original.as_os_str().encode_wide()) |
1495 | | - .chain(core::iter::once(0)); |
1496 | | - let mut i = 0; |
1497 | | - for c in iter { |
1498 | | - if i >= reparse_target_slice.len() { |
1499 | | - return Err(crate::io::const_io_error!( |
1500 | | - crate::io::ErrorKind::InvalidFilename, |
1501 | | - "Input filename is too long" |
1502 | | - )); |
1503 | | - } |
1504 | | - reparse_target_slice[i] = c; |
1505 | | - i += 1; |
| 1456 | + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_POSIX_SEMANTICS); |
| 1457 | + opts.attributes(c::FILE_ATTRIBUTE_DIRECTORY); |
| 1458 | + |
| 1459 | + let d = File::open(link, &opts)?; |
| 1460 | + |
| 1461 | + // We need to get an absolute, NT-style path. |
| 1462 | + let path_bytes = original.as_os_str().as_encoded_bytes(); |
| 1463 | + let abs_path: Vec<u16> = if path_bytes.starts_with(br"\\?\") || path_bytes.starts_with(br"\??\") |
| 1464 | + { |
| 1465 | + // It's already an absolute path, we just need to convert the prefix to `\??\` |
| 1466 | + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&path_bytes[4..]) }; |
| 1467 | + r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() |
| 1468 | + } else { |
| 1469 | + // Get an absolute path and then convert the prefix to `\??\` |
| 1470 | + let abs_path = crate::path::absolute(original)?.into_os_string().into_encoded_bytes(); |
| 1471 | + if abs_path.len() > 0 && abs_path[1..].starts_with(br":\") { |
| 1472 | + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path) }; |
| 1473 | + r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() |
| 1474 | + } else if abs_path.starts_with(br"\\.\") { |
| 1475 | + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[4..]) }; |
| 1476 | + r"\??\".encode_utf16().chain(bytes.encode_wide()).collect() |
| 1477 | + } else if abs_path.starts_with(br"\\") { |
| 1478 | + let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) }; |
| 1479 | + r"\??\UNC\".encode_utf16().chain(bytes.encode_wide()).collect() |
| 1480 | + } else { |
| 1481 | + return Err(io::const_io_error!(io::ErrorKind::InvalidInput, "path is not valid")); |
1506 | 1482 | } |
1507 | | - (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT; |
1508 | | - (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD; |
1509 | | - (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD; |
1510 | | - (*db).ReparseDataLength = (*db).ReparseTargetLength as c::DWORD + 12; |
| 1483 | + }; |
| 1484 | + // Defined inline so we don't have to mess about with variable length buffer. |
| 1485 | + #[repr(C)] |
| 1486 | + pub struct MountPointBuffer { |
| 1487 | + ReparseTag: u32, |
| 1488 | + ReparseDataLength: u16, |
| 1489 | + Reserved: u16, |
| 1490 | + SubstituteNameOffset: u16, |
| 1491 | + SubstituteNameLength: u16, |
| 1492 | + PrintNameOffset: u16, |
| 1493 | + PrintNameLength: u16, |
| 1494 | + PathBuffer: [MaybeUninit<u16>; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize], |
| 1495 | + } |
| 1496 | + let data_len = 12 + (abs_path.len() * 2); |
| 1497 | + if data_len > u16::MAX as usize { |
| 1498 | + return Err(io::const_io_error!( |
| 1499 | + io::ErrorKind::InvalidInput, |
| 1500 | + "`original` path is too long" |
| 1501 | + )); |
| 1502 | + } |
| 1503 | + let data_len = data_len as u16; |
| 1504 | + let mut header = MountPointBuffer { |
| 1505 | + ReparseTag: c::IO_REPARSE_TAG_MOUNT_POINT, |
| 1506 | + ReparseDataLength: data_len, |
| 1507 | + Reserved: 0, |
| 1508 | + SubstituteNameOffset: 0, |
| 1509 | + SubstituteNameLength: (abs_path.len() * 2) as u16, |
| 1510 | + PrintNameOffset: ((abs_path.len() + 1) * 2) as u16, |
| 1511 | + PrintNameLength: 0, |
| 1512 | + PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize], |
| 1513 | + }; |
| 1514 | + unsafe { |
| 1515 | + let ptr = header.PathBuffer.as_mut_ptr(); |
| 1516 | + ptr.copy_from(abs_path.as_ptr().cast::<MaybeUninit<u16>>(), abs_path.len()); |
1511 | 1517 |
|
1512 | 1518 | let mut ret = 0; |
1513 | 1519 | cvt(c::DeviceIoControl( |
1514 | | - h as *mut _, |
| 1520 | + d.as_raw_handle(), |
1515 | 1521 | c::FSCTL_SET_REPARSE_POINT, |
1516 | | - data_ptr.cast(), |
1517 | | - (*db).ReparseDataLength + 8, |
| 1522 | + addr_of!(header).cast::<c_void>(), |
| 1523 | + data_len as u32 + 8, |
1518 | 1524 | ptr::null_mut(), |
1519 | 1525 | 0, |
1520 | 1526 | &mut ret, |
|
0 commit comments