|
| 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