use std::path::Path;
use std::{fs, io};
#[cfg(not(windows))]
#[allow(dead_code)]
pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>(
src: P,
dst_dir: &fs::File,
dst: Q,
) -> io::Result<()> {
crate::filesystem::primitives::symlink(src.as_ref(), dst_dir, dst.as_ref())
}
#[cfg(windows)]
#[allow(dead_code)]
pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>(
src: P,
dst_dir: &fs::File,
dst: Q,
) -> io::Result<()> {
symlink_junction_inner(src.as_ref(), dst_dir, dst.as_ref())
}
#[cfg(windows)]
#[repr(C, align(8))]
#[derive(Copy, Clone)]
struct Align8<T: ?Sized>(pub T);
#[cfg(windows)]
#[allow(dead_code)]
#[allow(non_snake_case)]
#[repr(C)]
pub struct REPARSE_MOUNTPOINT_DATA_BUFFER {
pub ReparseTag: u32,
pub ReparseDataLength: u32,
pub Reserved: u16,
pub ReparseTargetLength: u16,
pub ReparseTargetMaximumLength: u16,
pub Reserved1: u16,
pub ReparseTarget: u16,
}
#[cfg(windows)]
#[allow(dead_code)]
pub fn cvt(i: windows_sys::core::BOOL) -> io::Result<windows_sys::core::BOOL> {
if i == 0 {
Err(io::Error::last_os_error())
} else {
Ok(i)
}
}
#[cfg(windows)]
#[allow(dead_code)]
fn symlink_junction_inner(original: &Path, dir: &fs::File, junction: &Path) -> io::Result<()> {
use crate::filesystem::primitives::{
DirOptions, OpenOptions, OpenOptionsExt, create_dir, open,
};
use std::mem::MaybeUninit;
use std::os::windows::ffi::OsStrExt;
use std::os::windows::io::AsRawHandle;
use std::{mem, ptr};
use windows_sys::Win32::Storage::FileSystem::MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
create_dir(dir, junction, &DirOptions::new())?;
let mut opts = OpenOptions::new();
opts.write(true);
opts.custom_flags(
windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OPEN_REPARSE_POINT
| windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS,
);
let f = open(dir, junction, &opts)?;
let h = f.as_raw_handle();
unsafe {
let mut data =
Align8([MaybeUninit::<u8>::uninit(); MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
let data_ptr = data.0.as_mut_ptr();
let data_end = data_ptr.add(MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize);
let db = data_ptr.cast::<REPARSE_MOUNTPOINT_DATA_BUFFER>();
*db = mem::zeroed();
let reparse_target_slice = {
let buf_start = ptr::addr_of_mut!((*db).ReparseTarget).cast::<u16>();
let buf_len_bytes =
usize::try_from(data_end.cast::<u8>().offset_from(buf_start.cast::<u8>())).unwrap();
let buf_len_wchars = buf_len_bytes / core::mem::size_of::<u16>();
core::slice::from_raw_parts_mut(buf_start, buf_len_wchars)
};
let iter = br"\??\"
.iter()
.map(|x| *x as u16)
.chain(original.as_os_str().encode_wide())
.chain(core::iter::once(0));
let mut i = 0;
for c in iter {
if i >= reparse_target_slice.len() {
return Err(io::Error::new(
io::ErrorKind::Other,
"Input filename is too long",
));
}
reparse_target_slice[i] = c;
i += 1;
}
(*db).ReparseTag = windows_sys::Win32::System::SystemServices::IO_REPARSE_TAG_MOUNT_POINT;
(*db).ReparseTargetMaximumLength = (i * 2) as u16;
(*db).ReparseTargetLength = ((i - 1) * 2) as u16;
(*db).ReparseDataLength = (*db).ReparseTargetLength as u32 + 12;
let mut ret = 0;
cvt(windows_sys::Win32::System::IO::DeviceIoControl(
h as _,
windows_sys::Win32::System::Ioctl::FSCTL_SET_REPARSE_POINT,
data_ptr.cast(),
(*db).ReparseDataLength + 8,
ptr::null_mut(),
0,
&mut ret,
ptr::null_mut(),
))
.map(drop)
}
}