Skip to content

Commit 42e38e8

Browse files
committed
Use the junction crate in bootstrap instead of manually creating the junction
1 parent 12dff54 commit 42e38e8

File tree

3 files changed

+15
-115
lines changed

3 files changed

+15
-115
lines changed

src/bootstrap/Cargo.lock

+11
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ dependencies = [
4545
"hex",
4646
"ignore",
4747
"is-terminal",
48+
"junction",
4849
"libc",
4950
"object",
5051
"once_cell",
@@ -349,6 +350,16 @@ version = "1.0.2"
349350
source = "registry+https://github.com/rust-lang/crates.io-index"
350351
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
351352

353+
[[package]]
354+
name = "junction"
355+
version = "1.0.0"
356+
source = "registry+https://github.com/rust-lang/crates.io-index"
357+
checksum = "ca39ef0d69b18e6a2fd14c2f0a1d593200f4a4ed949b240b5917ab51fac754cb"
358+
dependencies = [
359+
"scopeguard",
360+
"winapi",
361+
]
362+
352363
[[package]]
353364
name = "lazy_static"
354365
version = "1.4.0"

src/bootstrap/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ sysinfo = { version = "0.26.0", optional = true }
6161
[target.'cfg(not(target_os = "solaris"))'.dependencies]
6262
fd-lock = "3.0.8"
6363

64+
[target.'cfg(windows)'.dependencies.junction]
65+
version = "1.0.0"
66+
6467
[target.'cfg(windows)'.dependencies.windows]
6568
version = "0.46.0"
6669
features = [

src/bootstrap/util.rs

+1-115
Original file line numberDiff line numberDiff line change
@@ -146,123 +146,9 @@ pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> {
146146
fs::symlink(src, dest)
147147
}
148148

149-
// Creating a directory junction on windows involves dealing with reparse
150-
// points and the DeviceIoControl function, and this code is a skeleton of
151-
// what can be found here:
152-
//
153-
// http://www.flexhex.com/docs/articles/hard-links.phtml
154149
#[cfg(windows)]
155150
fn symlink_dir_inner(target: &Path, junction: &Path) -> io::Result<()> {
156-
use std::ffi::OsStr;
157-
use std::os::windows::ffi::OsStrExt;
158-
159-
use windows::{
160-
core::PCWSTR,
161-
Win32::Foundation::{CloseHandle, HANDLE},
162-
Win32::Storage::FileSystem::{
163-
CreateFileW, FILE_ACCESS_FLAGS, FILE_FLAG_BACKUP_SEMANTICS,
164-
FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE,
165-
MAXIMUM_REPARSE_DATA_BUFFER_SIZE, OPEN_EXISTING,
166-
},
167-
Win32::System::Ioctl::FSCTL_SET_REPARSE_POINT,
168-
Win32::System::SystemServices::{GENERIC_WRITE, IO_REPARSE_TAG_MOUNT_POINT},
169-
Win32::System::IO::DeviceIoControl,
170-
};
171-
172-
#[allow(non_snake_case)]
173-
#[repr(C)]
174-
struct REPARSE_MOUNTPOINT_DATA_BUFFER {
175-
ReparseTag: u32,
176-
ReparseDataLength: u32,
177-
Reserved: u16,
178-
ReparseTargetLength: u16,
179-
ReparseTargetMaximumLength: u16,
180-
Reserved1: u16,
181-
ReparseTarget: u16,
182-
}
183-
184-
fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> {
185-
Ok(s.as_ref().encode_wide().chain(Some(0)).collect())
186-
}
187-
188-
// We're using low-level APIs to create the junction, and these are more
189-
// picky about paths. For example, forward slashes cannot be used as a
190-
// path separator, so we should try to canonicalize the path first.
191-
let target = fs::canonicalize(target)?;
192-
193-
fs::create_dir(junction)?;
194-
195-
let path = to_u16s(junction)?;
196-
197-
let h = unsafe {
198-
CreateFileW(
199-
PCWSTR(path.as_ptr()),
200-
FILE_ACCESS_FLAGS(GENERIC_WRITE),
201-
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
202-
None,
203-
OPEN_EXISTING,
204-
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
205-
HANDLE::default(),
206-
)
207-
}
208-
.map_err(|_| io::Error::last_os_error())?;
209-
210-
unsafe {
211-
#[repr(C, align(8))]
212-
struct Align8<T>(T);
213-
let mut data = Align8([0u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
214-
let db = data.0.as_mut_ptr() as *mut REPARSE_MOUNTPOINT_DATA_BUFFER;
215-
let end = db.cast::<u8>().add(MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize);
216-
let reparse_target_slice = {
217-
let buf_start = core::ptr::addr_of_mut!((*db).ReparseTarget).cast::<u16>();
218-
// Compute offset in bytes and then divide so that we round down
219-
// rather than hit any UB (admittedly this arithmetic should work
220-
// out so that this isn't necessary)
221-
let buf_len_bytes =
222-
usize::try_from(end.offset_from(buf_start.cast::<u8>())).unwrap();
223-
let buf_len_wchars = buf_len_bytes / core::mem::size_of::<u16>();
224-
core::slice::from_raw_parts_mut(buf_start, buf_len_wchars)
225-
};
226-
227-
// FIXME: this conversion is very hacky
228-
let iter = br"\??\"
229-
.iter()
230-
.map(|x| *x as u16)
231-
.chain(path.iter().copied())
232-
.chain(core::iter::once(0));
233-
let mut i = 0;
234-
for c in iter {
235-
if i >= reparse_target_slice.len() {
236-
return Err(io::Error::new(
237-
io::ErrorKind::Other,
238-
format!("path too long for reparse target: {target:?}"),
239-
));
240-
}
241-
reparse_target_slice[i] = c;
242-
i += 1;
243-
}
244-
(*db).ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
245-
(*db).ReparseTargetMaximumLength = (i * 2) as u16;
246-
(*db).ReparseTargetLength = ((i - 1) * 2) as u16;
247-
(*db).ReparseDataLength = ((*db).ReparseTargetLength + 12) as u32;
248-
249-
let mut ret = 0u32;
250-
DeviceIoControl(
251-
h,
252-
FSCTL_SET_REPARSE_POINT,
253-
Some(db.cast()),
254-
(*db).ReparseDataLength + 8,
255-
None,
256-
0,
257-
Some(&mut ret),
258-
None,
259-
)
260-
.ok()
261-
.map_err(|_| io::Error::last_os_error())?;
262-
}
263-
264-
unsafe { CloseHandle(h) };
265-
Ok(())
151+
junction::create(&target, &junction)
266152
}
267153
}
268154

0 commit comments

Comments
 (0)