From f4c2ec024a0a06798e2912805b12e466147607de Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sun, 26 Feb 2023 23:26:32 +0700 Subject: [PATCH 01/46] CI: enable normal builds again --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4baa3f18..4789bbb4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,6 @@ env: jobs: build: - if: false runs-on: windows-latest env: RUST_BACKTRACE: 1 From 0b7768a19be4281d1ccc72cbe8262236b1b62bee Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sun, 26 Feb 2023 23:28:22 +0700 Subject: [PATCH 02/46] readme: correct MSRV --- HOW-TO-RELEASE.md | 11 ++++++----- README.md | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/HOW-TO-RELEASE.md b/HOW-TO-RELEASE.md index 9d7242a8..b3947ac5 100644 --- a/HOW-TO-RELEASE.md +++ b/HOW-TO-RELEASE.md @@ -1,19 +1,20 @@ # Making a release 1. Update all `Cargo.toml` to have the new version. -2. Update README, CHANGELOG (run `date --iso-8601` to get ISO-8601 date format). -3. Run `cargo build` and review `Cargo.lock` changes if all looks well, make a commit. -4. Package up your crate into a format that can be uploaded to https://crates.io +2. Update MSRV in README and CI config. +3. Update README, CHANGELOG (run `date --iso-8601` to get ISO-8601 date format). +4. Run `cargo build` and review `Cargo.lock` changes if all looks well, make a commit. +5. Package up your crate into a format that can be uploaded to https://crates.io ```bash cargo package ``` Check if files in package are correct by `cargo package --list`. -5. Now upload the package +6. Now upload the package ```bash cargo publish ``` -6. Create tag and publish to remote +7. Create tag and publish to remote ```bash VER_NUM=v0.x.x diff --git a/README.md b/README.md index 2917708e..8e8db085 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Library for working with NTFS junctions. ### Minimal Supported Rust versions -1.48.0 +1.51.0 ## All relevant references From d14f7ff1f77c876d4dfd3714bcf847bbef27eff6 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 27 Feb 2023 15:08:21 +0700 Subject: [PATCH 03/46] CI: trying with Rudra --- .github/workflows/ci.yml | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4789bbb4..66e66b83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,3 +141,54 @@ jobs: cargo check --all-targets --all-features git ls-files '*.rs' | xargs touch - run: cargo clippy --all-targets --all-features -- -Dwarnings + + + # Use static analyzer Rudra . + # FIXME: Disable for now since it's very costly to run. + rudra: + if: false + runs-on: ubuntu-latest + env: + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse + MSRV: nightly-2021-08-20 + WIN_TARGET: x86_64-pc-windows-gnu + steps: + - uses: actions/checkout@v3 + with: + path: junction + - uses: actions/checkout@v3 + with: + repository: sslab-gatech/Rudra + path: Rudra + - name: setup + shell: bash + run: | + # Toolchain setup + rustup toolchain install ${{ env.MSRV }} -c rustc-dev -c miri + rustup default ${{ env.MSRV }} + rustup target add ${{ env.WIN_TARGET }} + + # Environment variable setup, put these in your `.bashrc` + export RUDRA_RUST_CHANNEL=${{ env.MSRV }} + export RUDRA_RUNNER_HOME="$HOME/rudra-home" + ./setup_rudra_runner_home.py ${RUDRA_RUNNER_HOME} + + RUDRA_LIB=$HOME/.rustup/toolchains/${RUDRA_RUST_CHANNEL}-x86_64-unknown-linux-gnu/lib + export RUSTFLAGS="-L ${RUDRA_LIB}" + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${RUDRA_LIB}" + + echo "RUDRA_RUNNER_HOME=${RUDRA_RUNNER_HOME}" >> $GITHUB_ENV + echo "RUDRA_RUST_CHANNEL=${RUDRA_RUST_CHANNEL}" >> $GITHUB_ENV + echo "RUSTFLAGS=${RUSTFLAGS}" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}" >> $GITHUB_ENV + working-directory: Rudra + - name: run + run: | + ./install-release.sh + shell: bash + working-directory: Rudra + - run: | + # for single file testing (you need to set library include path, or use `cargo run` instead) + # rudra --crate-type lib tests/unsafe_destructor/normal1.rs + cargo rudra --target ${{ env.WIN_TARGET }} --all-targets # for crate compilation + working-directory: junction From 7c25ab9ad85688fe0688b7664adab72626c689ea Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 4 Mar 2023 21:30:06 +0700 Subject: [PATCH 04/46] chore: shorten export items and clean docs --- src/internals.rs | 12 +++++++----- src/internals/helpers.rs | 2 +- src/internals/helpers/utf16.rs | 4 +++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 71b03863..88fd9647 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -6,6 +6,7 @@ use types::{MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE, REPARSE_DATA_BUFFER_HEADER_S use std::cmp; use std::fs; +use std::mem::{align_of, size_of, MaybeUninit}; use std::path::{Path, PathBuf}; use std::ptr; use std::slice; @@ -14,7 +15,7 @@ use std::{io, os::windows::io::AsRawHandle}; use winapi::um::winnt::{IO_REPARSE_TAG_MOUNT_POINT, MAXIMUM_REPARSE_DATA_BUFFER_SIZE}; -// makes sure layout of RawHandle and winapi's HANDLE are the same +// Makes sure layout of RawHandle and winapi's HANDLE are the same // for pointer casts between them. const _: () = { use std::alloc::Layout; @@ -27,8 +28,9 @@ const _: () = { /// This prefix indicates to NTFS that the path is to be treated as a non-interpreted /// path in the virtual file system. -const NON_INTERPRETED_PATH_PREFIX: [u16; 4] = [b'\\' as u16, b'?' as _, b'?' as _, b'\\' as _]; -const WCHAR_SIZE: u16 = std::mem::size_of::() as _; +const NON_INTERPRETED_PATH_PREFIX: [u16; 4] = helpers::utf16s!(br"\??\"); + +const WCHAR_SIZE: u16 = size_of::() as _; pub fn create(target: &Path, junction: &Path) -> io::Result<()> { const UNICODE_NULL_SIZE: u16 = WCHAR_SIZE; @@ -100,13 +102,13 @@ pub fn delete(junction: &Path) -> io::Result<()> { // Makes sure `align(ReparseDataBuffer) == 4` for struct `AlignAs` to be sound. const _: () = { - const A: usize = std::mem::align_of::(); + const A: usize = align_of::(); if A != 4 { let _ = [0; 0][A]; } }; -type MaybeU8 = std::mem::MaybeUninit; +type MaybeU8 = MaybeUninit; #[repr(align(4))] struct AlignAs { value: Vec, diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 4731dfca..2dd2ebde 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -1,8 +1,8 @@ -#[macro_use] mod utf16; use super::types::REPARSE_GUID_DATA_BUFFER_HEADER_SIZE; use super::types::{ReparseDataBuffer, ReparseGuidDataBuffer}; +pub(crate) use utf16::utf16s; use std::ffi::OsStr; use std::fs::{File, OpenOptions}; diff --git a/src/internals/helpers/utf16.rs b/src/internals/helpers/utf16.rs index 57b75fb6..6d987c80 100644 --- a/src/internals/helpers/utf16.rs +++ b/src/internals/helpers/utf16.rs @@ -1,7 +1,7 @@ // FIXME(const_generic) /// Convert ASCII bytes to UTF-16 sequences. macro_rules! utf16s { - ($src:expr) => {{ + ($src:literal) => {{ const SRC: &[u8] = $src; const N: usize = SRC.len(); let mut i = 0; @@ -13,3 +13,5 @@ macro_rules! utf16s { dst }}; } + +pub(crate) use utf16s; From d5aba7e31407495e2638277738edbf86f941a3e8 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 4 Mar 2023 21:30:54 +0700 Subject: [PATCH 05/46] clippy nonsense disallowed --- src/internals.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/internals.rs b/src/internals.rs index 88fd9647..61b8dafe 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -17,6 +17,8 @@ use winapi::um::winnt::{IO_REPARSE_TAG_MOUNT_POINT, MAXIMUM_REPARSE_DATA_BUFFER_ // Makes sure layout of RawHandle and winapi's HANDLE are the same // for pointer casts between them. +// CLIPPY: nonsense suggestions for assert! +#[allow(clippy::unnecessary_operation)] const _: () = { use std::alloc::Layout; let std_layout = Layout::new::(); From 7fc7abc44380fb8d42f1f181e4389e37c2aef59f Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 4 Mar 2023 21:47:59 +0700 Subject: [PATCH 06/46] ci: should be bash --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66e66b83..2b8e5d7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -140,6 +140,7 @@ jobs: - run: | cargo check --all-targets --all-features git ls-files '*.rs' | xargs touch + shell: bash - run: cargo clippy --all-targets --all-features -- -Dwarnings From eca2947e6a10bf6801229793eb27dfc27fe8a832 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 7 Mar 2023 18:57:50 +0700 Subject: [PATCH 07/46] Remove outdated azure paths --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index de93f597..bc6f4e4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ edition = "2018" exclude = [ "/.github", "/HOW-TO-RELEASE.md", - "/azure-pipelines.yml", ] keywords = ["junction", "symlink"] license = "MIT" From 21d56fd28fcf9c627db48b5f51bf1a0ce97aa4d4 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 12 Jun 2023 21:21:54 +0700 Subject: [PATCH 08/46] clippy now uses a separate build artifacts from cargo check --- .github/workflows/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b8e5d7f..535bca7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,10 +137,6 @@ jobs: - run: | rustup toolchain install nightly -c clippy rustup default nightly - - run: | - cargo check --all-targets --all-features - git ls-files '*.rs' | xargs touch - shell: bash - run: cargo clippy --all-targets --all-features -- -Dwarnings From 0a86e5c00d2c4129d2b1e7c83f9b80f34cb9d002 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 12 Jun 2023 21:32:27 +0700 Subject: [PATCH 09/46] tidy: now 1.70 supports sparse registry --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 535bca7a..b101220a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ on: pull_request: env: - SPARSE_TOOLCHAIN: nightly-2023-02-25 + SPARSE_TOOLCHAIN: stable jobs: build: From 9c066febc03ae34fe7b2aa9bb428d7b4e4344dba Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 12 Jun 2023 21:48:40 +0700 Subject: [PATCH 10/46] Fix the returned IO error code Name too long should be an invalid input error --- src/internals.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internals.rs b/src/internals.rs index 61b8dafe..9a2820d1 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -55,7 +55,7 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { let target_len_in_bytes = min_len.saturating_mul(WCHAR_SIZE); // Check if `target_wchar.len()` may lead to a buffer overflow. if target_len_in_bytes > MAX_AVAILABLE_PATH_BUFFER { - return Err(io::Error::new(io::ErrorKind::Other, "`target` is too long")); + return Err(io::Error::new(io::ErrorKind::InvalidInput, "`target` is too long")); } target_len_in_bytes }; From e0d3537ce79435afea2f62289048225dae297b4e Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 12 Jun 2023 21:49:50 +0700 Subject: [PATCH 11/46] style: unpack long borring field name of struct --- src/internals/types.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/internals/types.rs b/src/internals/types.rs index b10bf293..6c5ca4b7 100644 --- a/src/internals/types.rs +++ b/src/internals/types.rs @@ -88,17 +88,17 @@ pub struct ReparseGuidDataBuffer { impl std::fmt::Debug for ReparseGuidDataBuffer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let (a, b, c, d) = ( + self.reparse_guid.Data1, + self.reparse_guid.Data2, + self.reparse_guid.Data3, + self.reparse_guid.Data4, + ); f.debug_struct("ReparseGuidDataBuffer") .field("reparse_tag", &self.reparse_tag) .field("reparse_data_length", &self.reparse_data_length) .field("reserved", &self.reserved) - .field( - "reparse_guid", - &format_args!( - "{}:{}:{}:{:?}", - self.reparse_guid.Data1, self.reparse_guid.Data2, self.reparse_guid.Data3, self.reparse_guid.Data4, - ), - ) + .field("reparse_guid", &format_args!("{}:{}:{}:{:?}", a, b, c, d)) .field("generic", &self.generic.data_buffer) .finish() } From 36593ba4a167477ab1dd5d24a48cceb9c165051b Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 12 Jun 2023 21:57:23 +0700 Subject: [PATCH 12/46] Replace macros with const fn --- src/internals.rs | 2 +- src/internals/helpers.rs | 4 ++-- src/internals/helpers/utf16.rs | 23 ++++++++--------------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 9a2820d1..0a644f34 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -30,7 +30,7 @@ const _: () = { /// This prefix indicates to NTFS that the path is to be treated as a non-interpreted /// path in the virtual file system. -const NON_INTERPRETED_PATH_PREFIX: [u16; 4] = helpers::utf16s!(br"\??\"); +const NON_INTERPRETED_PATH_PREFIX: [u16; 4] = helpers::utf16s(br"\??\"); const WCHAR_SIZE: u16 = size_of::() as _; diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 2dd2ebde..2bb8a8be 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -25,8 +25,8 @@ use winapi::um::winbase::{FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POI use winapi::um::winioctl::{FSCTL_DELETE_REPARSE_POINT, FSCTL_GET_REPARSE_POINT, FSCTL_SET_REPARSE_POINT}; use winapi::um::winnt::*; -pub static SE_RESTORE_NAME: [u16; 19] = utf16s!(b"SeRestorePrivilege\0"); -pub static SE_BACKUP_NAME: [u16; 18] = utf16s!(b"SeBackupPrivilege\0"); +pub static SE_RESTORE_NAME: [u16; 19] = utf16s(b"SeRestorePrivilege\0"); +pub static SE_BACKUP_NAME: [u16; 18] = utf16s(b"SeBackupPrivilege\0"); pub fn open_reparse_point(reparse_point: &Path, rdwr: bool) -> io::Result { let access = if rdwr { diff --git a/src/internals/helpers/utf16.rs b/src/internals/helpers/utf16.rs index 6d987c80..47e23fc9 100644 --- a/src/internals/helpers/utf16.rs +++ b/src/internals/helpers/utf16.rs @@ -1,17 +1,10 @@ -// FIXME(const_generic) /// Convert ASCII bytes to UTF-16 sequences. -macro_rules! utf16s { - ($src:literal) => {{ - const SRC: &[u8] = $src; - const N: usize = SRC.len(); - let mut i = 0; - let mut dst = [0u16; N]; - while i < N { - dst[i] = SRC[i] as u16; - i += 1; - } - dst - }}; +pub const fn utf16s(src: &'static [u8; N]) -> [u16; N] { + let mut dst = [0u16; N]; + let mut i = 0; + while i < N { + dst[i] = src[i] as u16; + i += 1; + } + dst } - -pub(crate) use utf16s; From 9f6a81a2d1324516e732d009e8a98461db9a9600 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 13 Jun 2023 21:33:06 +0700 Subject: [PATCH 13/46] fmt: beatify code --- src/internals.rs | 2 ++ src/internals/helpers.rs | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 0a644f34..7b5177d2 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -111,6 +111,7 @@ const _: () = { }; type MaybeU8 = MaybeUninit; + #[repr(align(4))] struct AlignAs { value: Vec, @@ -132,6 +133,7 @@ pub fn exists(junction: &Path) -> io::Result { } pub fn get_target(junction: &Path) -> io::Result { + // MSRV(1.63): use Path::try_exists instead if !junction.exists() { return Err(io::Error::new(io::ErrorKind::NotFound, "`junction` does not exist")); } diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 2bb8a8be..c44408e6 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -29,11 +29,7 @@ pub static SE_RESTORE_NAME: [u16; 19] = utf16s(b"SeRestorePrivilege\0"); pub static SE_BACKUP_NAME: [u16; 18] = utf16s(b"SeBackupPrivilege\0"); pub fn open_reparse_point(reparse_point: &Path, rdwr: bool) -> io::Result { - let access = if rdwr { - GENERIC_READ | GENERIC_WRITE - } else { - GENERIC_READ - }; + let access = GENERIC_READ | if rdwr { GENERIC_WRITE } else { 0 }; let mut opts = OpenOptions::new(); opts.access_mode(access) .share_mode(0) From 129c166ec1756ab2636798d89a9ab7a65d56072f Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 14 Jun 2023 17:22:41 +0700 Subject: [PATCH 14/46] use Box instead of Vec for transmuting --- src/internals.rs | 39 ++++++++++----------------------------- src/internals/cast.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/internals/helpers.rs | 2 +- 3 files changed, 49 insertions(+), 30 deletions(-) create mode 100644 src/internals/cast.rs diff --git a/src/internals.rs b/src/internals.rs index 7b5177d2..2a149366 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -1,12 +1,15 @@ +mod cast; mod helpers; mod types; +use cast::BytesAsReparseDataBuffer; use types::ReparseDataBuffer; use types::{MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE, REPARSE_DATA_BUFFER_HEADER_SIZE}; +use std::alloc::Layout; use std::cmp; use std::fs; -use std::mem::{align_of, size_of, MaybeUninit}; +use std::mem::size_of; use std::path::{Path, PathBuf}; use std::ptr; use std::slice; @@ -20,7 +23,6 @@ use winapi::um::winnt::{IO_REPARSE_TAG_MOUNT_POINT, MAXIMUM_REPARSE_DATA_BUFFER_ // CLIPPY: nonsense suggestions for assert! #[allow(clippy::unnecessary_operation)] const _: () = { - use std::alloc::Layout; let std_layout = Layout::new::(); let winapi_layout = Layout::new::(); // MSVR(Rust v1.57): use assert! instead @@ -64,10 +66,8 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { target_wchar.append(&mut target); // Redefine the above char array into a ReparseDataBuffer we can work with - let mut data = AlignAs { - value: Vec::with_capacity(MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize), - }; - let rdb = data.value.as_mut_ptr().cast::(); + let mut data = BytesAsReparseDataBuffer::new(); + let rdb = data.as_mut_ptr(); let in_buffer_size: u16 = unsafe { // Set the type of reparse point we are creating ptr::addr_of_mut!((*rdb).reparse_tag).write(IO_REPARSE_TAG_MOUNT_POINT); @@ -102,31 +102,14 @@ pub fn delete(junction: &Path) -> io::Result<()> { helpers::delete_reparse_point(file.as_raw_handle().cast()) } -// Makes sure `align(ReparseDataBuffer) == 4` for struct `AlignAs` to be sound. -const _: () = { - const A: usize = align_of::(); - if A != 4 { - let _ = [0; 0][A]; - } -}; - -type MaybeU8 = MaybeUninit; - -#[repr(align(4))] -struct AlignAs { - value: Vec, -} - pub fn exists(junction: &Path) -> io::Result { if !junction.exists() { return Ok(false); } let file = helpers::open_reparse_point(junction, false)?; // Allocate enough space to fit the maximum sized reparse data buffer - let mut data = AlignAs { - value: Vec::with_capacity(MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize), - }; - let rdb = data.value.as_mut_ptr().cast::(); + let mut data = BytesAsReparseDataBuffer::new(); + let rdb = data.as_mut_ptr(); helpers::get_reparse_data_point(file.as_raw_handle().cast(), rdb)?; // The reparse tag indicates if this is a junction or not Ok(unsafe { (*rdb).reparse_tag } == IO_REPARSE_TAG_MOUNT_POINT) @@ -138,10 +121,8 @@ pub fn get_target(junction: &Path) -> io::Result { return Err(io::Error::new(io::ErrorKind::NotFound, "`junction` does not exist")); } let file = helpers::open_reparse_point(junction, false)?; - let mut data = AlignAs { - value: Vec::with_capacity(MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize), - }; - let rdb = data.value.as_mut_ptr().cast::(); + let mut data = BytesAsReparseDataBuffer::new(); + let rdb = data.as_mut_ptr(); helpers::get_reparse_data_point(file.as_raw_handle().cast(), rdb)?; // SAFETY: rdb should be initialized now let rdb = unsafe { &*rdb }; diff --git a/src/internals/cast.rs b/src/internals/cast.rs new file mode 100644 index 00000000..a3288053 --- /dev/null +++ b/src/internals/cast.rs @@ -0,0 +1,38 @@ +use crate::internals::ReparseDataBuffer; + +use std::alloc::{alloc, handle_alloc_error, Layout}; +use std::mem::align_of; + +use winapi::um::winnt::MAXIMUM_REPARSE_DATA_BUFFER_SIZE; + +type MaybeU8 = std::mem::MaybeUninit; + +#[repr(align(4))] +pub struct BytesAsReparseDataBuffer { + value: Box<[MaybeU8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]>, +} + +const _: () = { + let a = align_of::(); + let b = align_of::(); + [0; 1][!(a % b == 0) as usize]; +}; + +impl BytesAsReparseDataBuffer { + pub fn new() -> Self { + type Raw = [MaybeU8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]; + const LAYOUT: Layout = Layout::new::(); + let boxed = unsafe { + let ptr = alloc(LAYOUT).cast::(); + if ptr.is_null() { + handle_alloc_error(LAYOUT); + } + Box::from_raw(ptr) + }; + Self { value: boxed } + } + + pub fn as_mut_ptr(&mut self) -> *mut ReparseDataBuffer { + self.value.as_mut_ptr().cast::() + } +} diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index c44408e6..e53d802d 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -141,7 +141,7 @@ pub fn delete_reparse_point(handle: HANDLE) -> io::Result<()> { DeviceIoControl( handle, FSCTL_DELETE_REPARSE_POINT, - (&mut rgdb as *mut ReparseGuidDataBuffer).cast(), + ptr::addr_of_mut!(rgdb).cast(), u32::from(REPARSE_GUID_DATA_BUFFER_HEADER_SIZE), ptr::null_mut(), 0, From 98d182da6b5dd8dcde36db2e2a2b6f6d4cfa2720 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Thu, 15 Jun 2023 15:54:22 +0700 Subject: [PATCH 15/46] fix clippy --- src/internals/cast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internals/cast.rs b/src/internals/cast.rs index a3288053..4b35a243 100644 --- a/src/internals/cast.rs +++ b/src/internals/cast.rs @@ -15,7 +15,7 @@ pub struct BytesAsReparseDataBuffer { const _: () = { let a = align_of::(); let b = align_of::(); - [0; 1][!(a % b == 0) as usize]; + [(); 1][!((a % b) == 0) as usize] }; impl BytesAsReparseDataBuffer { From 9ca2b526c6be8f81ed779d2ac3caebd26bd327ee Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 16:59:30 +0700 Subject: [PATCH 16/46] Migrate to windows-sys from winapi --- .rustfmt.toml | 2 ++ Cargo.toml | 21 +++++------- src/internals.rs | 47 ++++++++----------------- src/internals/c.rs | 36 +++++++++++++++++++ src/internals/cast.rs | 5 ++- src/internals/helpers.rs | 74 +++++++++++++++++----------------------- src/internals/types.rs | 19 ++++++----- 7 files changed, 107 insertions(+), 97 deletions(-) create mode 100644 src/internals/c.rs diff --git a/.rustfmt.toml b/.rustfmt.toml index 261644ce..8354fb8a 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,2 +1,4 @@ edition = "2018" max_width = 120 +imports_granularity = "Module" +group_imports = "StdExternalCrate" diff --git a/Cargo.toml b/Cargo.toml index bc6f4e4c..3eb39d49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,19 +22,16 @@ targets = ["x86_64-pc-windows-msvc"] version = "1" default-features = false -[target.'cfg(windows)'.dependencies.winapi] -version = "0.3" +[target.'cfg(windows)'.dependencies.windows-sys] +version = "0.48" features = [ - "errhandlingapi", - "fileapi", - "guiddef", - "handleapi", - "ioapiset", - "processthreadsapi", - "securitybaseapi", - "winbase", - "winioctl", - "winnt", + "Win32_Foundation", + "Win32_Security", + "Win32_Storage_FileSystem", + "Win32_System_IO", + "Win32_System_Ioctl", + "Win32_System_SystemServices", + "Win32_System_Threading", ] [dev-dependencies] diff --git a/src/internals.rs b/src/internals.rs index 2a149366..72450ef7 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -1,34 +1,17 @@ +mod c; mod cast; mod helpers; mod types; -use cast::BytesAsReparseDataBuffer; -use types::ReparseDataBuffer; -use types::{MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE, REPARSE_DATA_BUFFER_HEADER_SIZE}; - -use std::alloc::Layout; -use std::cmp; -use std::fs; +use std::ffi::OsString; use std::mem::size_of; +use std::os::windows::ffi::OsStringExt; +use std::os::windows::io::AsRawHandle; use std::path::{Path, PathBuf}; -use std::ptr; -use std::slice; -use std::{ffi::OsString, os::windows::ffi::OsStringExt}; -use std::{io, os::windows::io::AsRawHandle}; +use std::{cmp, fs, io, ptr, slice}; -use winapi::um::winnt::{IO_REPARSE_TAG_MOUNT_POINT, MAXIMUM_REPARSE_DATA_BUFFER_SIZE}; - -// Makes sure layout of RawHandle and winapi's HANDLE are the same -// for pointer casts between them. -// CLIPPY: nonsense suggestions for assert! -#[allow(clippy::unnecessary_operation)] -const _: () = { - let std_layout = Layout::new::(); - let winapi_layout = Layout::new::(); - // MSVR(Rust v1.57): use assert! instead - [(); 1][!(std_layout.size() == winapi_layout.size()) as usize]; - [(); 1][!(std_layout.align() == winapi_layout.align()) as usize]; -}; +use cast::BytesAsReparseDataBuffer; +use types::{ReparseDataBuffer, MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE, REPARSE_DATA_BUFFER_HEADER_SIZE}; /// This prefix indicates to NTFS that the path is to be treated as a non-interpreted /// path in the virtual file system. @@ -38,7 +21,7 @@ const WCHAR_SIZE: u16 = size_of::() as _; pub fn create(target: &Path, junction: &Path) -> io::Result<()> { const UNICODE_NULL_SIZE: u16 = WCHAR_SIZE; - const MAX_AVAILABLE_PATH_BUFFER: u16 = MAXIMUM_REPARSE_DATA_BUFFER_SIZE as u16 + const MAX_AVAILABLE_PATH_BUFFER: u16 = c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as u16 - REPARSE_DATA_BUFFER_HEADER_SIZE - MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE - 2 * UNICODE_NULL_SIZE; @@ -70,7 +53,7 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { let rdb = data.as_mut_ptr(); let in_buffer_size: u16 = unsafe { // Set the type of reparse point we are creating - ptr::addr_of_mut!((*rdb).reparse_tag).write(IO_REPARSE_TAG_MOUNT_POINT); + ptr::addr_of_mut!((*rdb).reparse_tag).write(c::IO_REPARSE_TAG_MOUNT_POINT); ptr::addr_of_mut!((*rdb).reserved).write(0); // Copy the junction's target @@ -94,12 +77,12 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { size.wrapping_add(REPARSE_DATA_BUFFER_HEADER_SIZE) }; - helpers::set_reparse_point(file.as_raw_handle().cast(), rdb, u32::from(in_buffer_size)) + helpers::set_reparse_point(c::same_handle(file.as_raw_handle()), rdb, u32::from(in_buffer_size)) } pub fn delete(junction: &Path) -> io::Result<()> { let file = helpers::open_reparse_point(junction, true)?; - helpers::delete_reparse_point(file.as_raw_handle().cast()) + helpers::delete_reparse_point(c::same_handle(file.as_raw_handle())) } pub fn exists(junction: &Path) -> io::Result { @@ -110,9 +93,9 @@ pub fn exists(junction: &Path) -> io::Result { // Allocate enough space to fit the maximum sized reparse data buffer let mut data = BytesAsReparseDataBuffer::new(); let rdb = data.as_mut_ptr(); - helpers::get_reparse_data_point(file.as_raw_handle().cast(), rdb)?; + helpers::get_reparse_data_point(c::same_handle(file.as_raw_handle()), rdb)?; // The reparse tag indicates if this is a junction or not - Ok(unsafe { (*rdb).reparse_tag } == IO_REPARSE_TAG_MOUNT_POINT) + Ok(unsafe { (*rdb).reparse_tag } == c::IO_REPARSE_TAG_MOUNT_POINT) } pub fn get_target(junction: &Path) -> io::Result { @@ -123,10 +106,10 @@ pub fn get_target(junction: &Path) -> io::Result { let file = helpers::open_reparse_point(junction, false)?; let mut data = BytesAsReparseDataBuffer::new(); let rdb = data.as_mut_ptr(); - helpers::get_reparse_data_point(file.as_raw_handle().cast(), rdb)?; + helpers::get_reparse_data_point(c::same_handle(file.as_raw_handle()), rdb)?; // SAFETY: rdb should be initialized now let rdb = unsafe { &*rdb }; - if rdb.reparse_tag == IO_REPARSE_TAG_MOUNT_POINT { + if rdb.reparse_tag == c::IO_REPARSE_TAG_MOUNT_POINT { let offset = rdb.reparse_buffer.substitute_name_offset / WCHAR_SIZE; let len = rdb.reparse_buffer.substitute_name_length / WCHAR_SIZE; let wide = unsafe { diff --git a/src/internals/c.rs b/src/internals/c.rs new file mode 100644 index 00000000..831dbd20 --- /dev/null +++ b/src/internals/c.rs @@ -0,0 +1,36 @@ +use std::alloc::Layout; +use std::mem::transmute; +use std::os::windows::io::RawHandle; + +pub use windows_sys::Win32::Foundation::{ + CloseHandle, GetLastError, SetLastError, GENERIC_READ, GENERIC_WRITE, HANDLE, +}; +pub use windows_sys::Win32::Security::{ + AdjustTokenPrivileges, LookupPrivilegeValueW, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, +}; +pub use windows_sys::Win32::Storage::FileSystem::{ + GetFullPathNameW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, +}; +pub use windows_sys::Win32::System::Ioctl::{ + FSCTL_DELETE_REPARSE_POINT, FSCTL_GET_REPARSE_POINT, FSCTL_SET_REPARSE_POINT, +}; +pub use windows_sys::Win32::System::SystemServices::IO_REPARSE_TAG_MOUNT_POINT; +pub use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken}; +pub use windows_sys::Win32::System::IO::DeviceIoControl; + +pub fn same_handle(h: RawHandle) -> HANDLE { + // Makes sure layout of RawHandle and windows-sys's HANDLE are the same + // for pointer casts between them. + // CLIPPY: nonsense suggestions for assert! + #[allow(clippy::unnecessary_operation)] + const _: () = { + let std_layout = Layout::new::(); + let winapi_layout = Layout::new::(); + // MSVR(Rust v1.57): use assert! instead + [(); 1][!(std_layout.size() == winapi_layout.size()) as usize]; + [(); 1][!(std_layout.align() == winapi_layout.align()) as usize]; + }; + + // SAFETY: assured by above comparisons + unsafe { transmute::(h) } +} diff --git a/src/internals/cast.rs b/src/internals/cast.rs index 4b35a243..b5f72aa4 100644 --- a/src/internals/cast.rs +++ b/src/internals/cast.rs @@ -1,9 +1,8 @@ -use crate::internals::ReparseDataBuffer; - use std::alloc::{alloc, handle_alloc_error, Layout}; use std::mem::align_of; -use winapi::um::winnt::MAXIMUM_REPARSE_DATA_BUFFER_SIZE; +use super::c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE; +use crate::internals::ReparseDataBuffer; type MaybeU8 = std::mem::MaybeUninit; diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index e53d802d..3ea19e6b 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -1,39 +1,28 @@ mod utf16; -use super::types::REPARSE_GUID_DATA_BUFFER_HEADER_SIZE; -use super::types::{ReparseDataBuffer, ReparseGuidDataBuffer}; -pub(crate) use utf16::utf16s; - use std::ffi::OsStr; use std::fs::{File, OpenOptions}; -use std::io; use std::mem::{self, MaybeUninit}; use std::os::windows::ffi::OsStrExt; use std::os::windows::fs::OpenOptionsExt; use std::path::Path; -use std::ptr; +use std::{io, ptr}; use scopeguard::ScopeGuard; -use winapi::um::errhandlingapi::{GetLastError, SetLastError}; -use winapi::um::fileapi::GetFullPathNameW; -use winapi::um::handleapi::CloseHandle; -use winapi::um::ioapiset::DeviceIoControl; -use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken}; -use winapi::um::securitybaseapi::AdjustTokenPrivileges; -use winapi::um::winbase::LookupPrivilegeValueW; -use winapi::um::winbase::{FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT}; -use winapi::um::winioctl::{FSCTL_DELETE_REPARSE_POINT, FSCTL_GET_REPARSE_POINT, FSCTL_SET_REPARSE_POINT}; -use winapi::um::winnt::*; +pub(crate) use utf16::utf16s; + +use super::c; +use super::types::{ReparseDataBuffer, ReparseGuidDataBuffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE}; pub static SE_RESTORE_NAME: [u16; 19] = utf16s(b"SeRestorePrivilege\0"); pub static SE_BACKUP_NAME: [u16; 18] = utf16s(b"SeBackupPrivilege\0"); pub fn open_reparse_point(reparse_point: &Path, rdwr: bool) -> io::Result { - let access = GENERIC_READ | if rdwr { GENERIC_WRITE } else { 0 }; + let access = c::GENERIC_READ | if rdwr { c::GENERIC_WRITE } else { 0 }; let mut opts = OpenOptions::new(); opts.access_mode(access) .share_mode(0) - .custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS); + .custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); match opts.open(reparse_point) { Err(e) if e.kind() == io::ErrorKind::PermissionDenied => { // Obtain privilege in case we don't have it yet @@ -46,27 +35,28 @@ pub fn open_reparse_point(reparse_point: &Path, rdwr: bool) -> io::Result fn set_privilege(rdwr: bool) -> io::Result<()> { const ERROR_NOT_ALL_ASSIGNED: u32 = 1300; - const TOKEN_PRIVILEGES_SIZE: u32 = mem::size_of::() as _; + const TOKEN_PRIVILEGES_SIZE: u32 = mem::size_of::() as _; unsafe { - let mut handle = ptr::null_mut(); - if OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &mut handle) == 0 { + let mut handle = >::uninit(); + if c::OpenProcessToken(c::GetCurrentProcess(), c::TOKEN_ADJUST_PRIVILEGES, handle.as_mut_ptr()) == 0 { return Err(io::Error::last_os_error()); } + let handle = handle.assume_init(); let handle = scopeguard::guard(handle, |h| { - CloseHandle(h); + c::CloseHandle(h); }); - let mut tp: TOKEN_PRIVILEGES = mem::zeroed(); + let mut tp: c::TOKEN_PRIVILEGES = mem::zeroed(); let name = if rdwr { SE_RESTORE_NAME.as_ptr() } else { SE_BACKUP_NAME.as_ptr() }; - if LookupPrivilegeValueW(ptr::null(), name, &mut tp.Privileges[0].Luid) == 0 { + if c::LookupPrivilegeValueW(ptr::null(), name, &mut tp.Privileges[0].Luid) == 0 { return Err(io::Error::last_os_error()); } tp.PrivilegeCount = 1; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if AdjustTokenPrivileges( + tp.Privileges[0].Attributes = c::SE_PRIVILEGE_ENABLED; + if c::AdjustTokenPrivileges( *handle, 0, &mut tp, @@ -77,12 +67,12 @@ fn set_privilege(rdwr: bool) -> io::Result<()> { { return Err(io::Error::last_os_error()); } - if GetLastError() == ERROR_NOT_ALL_ASSIGNED { + if c::GetLastError() == ERROR_NOT_ALL_ASSIGNED { return Err(io::Error::from_raw_os_error(ERROR_NOT_ALL_ASSIGNED as i32)); } let handle = ScopeGuard::into_inner(handle); - if CloseHandle(handle) == 0 { + if c::CloseHandle(handle) == 0 { Err(io::Error::last_os_error()) } else { Ok(()) @@ -90,17 +80,17 @@ fn set_privilege(rdwr: bool) -> io::Result<()> { } } -pub fn get_reparse_data_point(handle: HANDLE, rdb: *mut ReparseDataBuffer) -> io::Result<()> { +pub fn get_reparse_data_point(handle: c::HANDLE, rdb: *mut ReparseDataBuffer) -> io::Result<()> { // Call DeviceIoControl to get the reparse point data let mut bytes_returned: u32 = 0; if unsafe { - DeviceIoControl( + c::DeviceIoControl( handle, - FSCTL_GET_REPARSE_POINT, + c::FSCTL_GET_REPARSE_POINT, ptr::null_mut(), 0, rdb.cast(), - MAXIMUM_REPARSE_DATA_BUFFER_SIZE, + c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &mut bytes_returned, ptr::null_mut(), ) @@ -111,12 +101,12 @@ pub fn get_reparse_data_point(handle: HANDLE, rdb: *mut ReparseDataBuffer) -> io Ok(()) } -pub fn set_reparse_point(handle: HANDLE, rdb: *mut ReparseDataBuffer, len: u32) -> io::Result<()> { +pub fn set_reparse_point(handle: c::HANDLE, rdb: *mut ReparseDataBuffer, len: u32) -> io::Result<()> { let mut bytes_returned: u32 = 0; if unsafe { - DeviceIoControl( + c::DeviceIoControl( handle, - FSCTL_SET_REPARSE_POINT, + c::FSCTL_SET_REPARSE_POINT, rdb.cast(), len, ptr::null_mut(), @@ -132,15 +122,15 @@ pub fn set_reparse_point(handle: HANDLE, rdb: *mut ReparseDataBuffer, len: u32) } // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa364560(v=vs.85).aspx -pub fn delete_reparse_point(handle: HANDLE) -> io::Result<()> { +pub fn delete_reparse_point(handle: c::HANDLE) -> io::Result<()> { let mut rgdb: ReparseGuidDataBuffer = unsafe { mem::zeroed() }; - rgdb.reparse_tag = IO_REPARSE_TAG_MOUNT_POINT; + rgdb.reparse_tag = c::IO_REPARSE_TAG_MOUNT_POINT; let mut bytes_returned: u32 = 0; if unsafe { - DeviceIoControl( + c::DeviceIoControl( handle, - FSCTL_DELETE_REPARSE_POINT, + c::FSCTL_DELETE_REPARSE_POINT, ptr::addr_of_mut!(rgdb).cast(), u32::from(REPARSE_GUID_DATA_BUFFER_HEADER_SIZE), ptr::null_mut(), @@ -192,8 +182,8 @@ pub fn get_full_path(target: &Path) -> io::Result> { &mut heap_buf[..] }; - SetLastError(0); - let k = GetFullPathNameW( + c::SetLastError(0); + let k = c::GetFullPathNameW( path.as_ptr().cast::(), n as u32, maybe_slice_to_ptr(buf), @@ -202,7 +192,7 @@ pub fn get_full_path(target: &Path) -> io::Result> { if k == 0 { return Err(crate::io::Error::last_os_error()); } - if GetLastError() == ERROR_INSUFFICIENT_BUFFER { + if c::GetLastError() == ERROR_INSUFFICIENT_BUFFER { n = n.saturating_mul(2).min(u32::MAX as usize); } else if k > n { n = k; diff --git a/src/internals/types.rs b/src/internals/types.rs index 6c5ca4b7..d344f7ef 100644 --- a/src/internals/types.rs +++ b/src/internals/types.rs @@ -1,4 +1,4 @@ -use winapi::shared::guiddef; +use std::os::raw::{c_uchar, c_ulong, c_ushort}; // NOTE: to use `size_of` operator, below structs should be packed. /// Reparse Data Buffer header size = `sizeof(u32) + 2 * sizeof(u16)` @@ -58,6 +58,14 @@ pub struct GenericReparseBuffer { pub data_buffer: VarLenArr, } +#[repr(C)] +pub struct GUID { + pub a: c_ulong, + pub b: c_ushort, + pub c: c_ushort, + pub d: [c_uchar; 8], +} + /// Used by all third-party layered drivers to store data for a reparse point. /// /// Each reparse point contains one instance of a `ReparseGuidDataBuffer` structure. @@ -79,7 +87,7 @@ pub struct ReparseGuidDataBuffer { /// point, the application must provide a non-`NULL` `GUID` in the `reparse_guid` /// member. When retrieving a reparse point from the file system, `reparse_guid` /// is the `GUID` assigned when the reparse point was set. - pub reparse_guid: guiddef::GUID, + pub reparse_guid: GUID, /// The user-defined data for the reparse point. The contents are determined by /// the reparse point implementer. The tag in the `reparse_tag` member and the /// `GUID` in the `reparse_guid` member indicate how the data is to be interpreted. @@ -88,12 +96,7 @@ pub struct ReparseGuidDataBuffer { impl std::fmt::Debug for ReparseGuidDataBuffer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let (a, b, c, d) = ( - self.reparse_guid.Data1, - self.reparse_guid.Data2, - self.reparse_guid.Data3, - self.reparse_guid.Data4, - ); + let GUID { a, b, c, d } = self.reparse_guid; f.debug_struct("ReparseGuidDataBuffer") .field("reparse_tag", &self.reparse_tag) .field("reparse_data_length", &self.reparse_data_length) From dbb40e9e17808dad8f68aa54f316b540163d8542 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 17:09:15 +0700 Subject: [PATCH 17/46] Fix clippy warns --- src/internals.rs | 8 ++++---- src/internals/c.rs | 28 +++++++++++----------------- src/internals/helpers.rs | 10 +--------- src/internals/types.rs | 6 +++--- 4 files changed, 19 insertions(+), 33 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 72450ef7..1985076c 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -77,12 +77,12 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { size.wrapping_add(REPARSE_DATA_BUFFER_HEADER_SIZE) }; - helpers::set_reparse_point(c::same_handle(file.as_raw_handle()), rdb, u32::from(in_buffer_size)) + helpers::set_reparse_point(file.as_raw_handle() as isize, rdb, u32::from(in_buffer_size)) } pub fn delete(junction: &Path) -> io::Result<()> { let file = helpers::open_reparse_point(junction, true)?; - helpers::delete_reparse_point(c::same_handle(file.as_raw_handle())) + helpers::delete_reparse_point(file.as_raw_handle() as isize) } pub fn exists(junction: &Path) -> io::Result { @@ -93,7 +93,7 @@ pub fn exists(junction: &Path) -> io::Result { // Allocate enough space to fit the maximum sized reparse data buffer let mut data = BytesAsReparseDataBuffer::new(); let rdb = data.as_mut_ptr(); - helpers::get_reparse_data_point(c::same_handle(file.as_raw_handle()), rdb)?; + helpers::get_reparse_data_point(file.as_raw_handle() as isize, rdb)?; // The reparse tag indicates if this is a junction or not Ok(unsafe { (*rdb).reparse_tag } == c::IO_REPARSE_TAG_MOUNT_POINT) } @@ -106,7 +106,7 @@ pub fn get_target(junction: &Path) -> io::Result { let file = helpers::open_reparse_point(junction, false)?; let mut data = BytesAsReparseDataBuffer::new(); let rdb = data.as_mut_ptr(); - helpers::get_reparse_data_point(c::same_handle(file.as_raw_handle()), rdb)?; + helpers::get_reparse_data_point(file.as_raw_handle() as isize, rdb)?; // SAFETY: rdb should be initialized now let rdb = unsafe { &*rdb }; if rdb.reparse_tag == c::IO_REPARSE_TAG_MOUNT_POINT { diff --git a/src/internals/c.rs b/src/internals/c.rs index 831dbd20..8cc7e8d3 100644 --- a/src/internals/c.rs +++ b/src/internals/c.rs @@ -1,5 +1,4 @@ use std::alloc::Layout; -use std::mem::transmute; use std::os::windows::io::RawHandle; pub use windows_sys::Win32::Foundation::{ @@ -18,19 +17,14 @@ pub use windows_sys::Win32::System::SystemServices::IO_REPARSE_TAG_MOUNT_POINT; pub use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken}; pub use windows_sys::Win32::System::IO::DeviceIoControl; -pub fn same_handle(h: RawHandle) -> HANDLE { - // Makes sure layout of RawHandle and windows-sys's HANDLE are the same - // for pointer casts between them. - // CLIPPY: nonsense suggestions for assert! - #[allow(clippy::unnecessary_operation)] - const _: () = { - let std_layout = Layout::new::(); - let winapi_layout = Layout::new::(); - // MSVR(Rust v1.57): use assert! instead - [(); 1][!(std_layout.size() == winapi_layout.size()) as usize]; - [(); 1][!(std_layout.align() == winapi_layout.align()) as usize]; - }; - - // SAFETY: assured by above comparisons - unsafe { transmute::(h) } -} +// Makes sure layout of RawHandle and windows-sys's HANDLE are the same +// for pointer casts between them. +// CLIPPY: nonsense suggestions for assert! +#[allow(clippy::unnecessary_operation)] +const _: () = { + let std_layout = Layout::new::(); + let winapi_layout = Layout::new::(); + // MSVR(Rust v1.57): use assert! instead + [(); 1][!(std_layout.size() == winapi_layout.size()) as usize]; + [(); 1][!(std_layout.align() == winapi_layout.align()) as usize]; +}; diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 3ea19e6b..c9506db5 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -56,15 +56,7 @@ fn set_privilege(rdwr: bool) -> io::Result<()> { } tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = c::SE_PRIVILEGE_ENABLED; - if c::AdjustTokenPrivileges( - *handle, - 0, - &mut tp, - TOKEN_PRIVILEGES_SIZE, - ptr::null_mut(), - ptr::null_mut(), - ) == 0 - { + if c::AdjustTokenPrivileges(*handle, 0, &tp, TOKEN_PRIVILEGES_SIZE, ptr::null_mut(), ptr::null_mut()) == 0 { return Err(io::Error::last_os_error()); } if c::GetLastError() == ERROR_NOT_ALL_ASSIGNED { diff --git a/src/internals/types.rs b/src/internals/types.rs index d344f7ef..42dd8bb6 100644 --- a/src/internals/types.rs +++ b/src/internals/types.rs @@ -59,7 +59,7 @@ pub struct GenericReparseBuffer { } #[repr(C)] -pub struct GUID { +pub struct Guid { pub a: c_ulong, pub b: c_ushort, pub c: c_ushort, @@ -87,7 +87,7 @@ pub struct ReparseGuidDataBuffer { /// point, the application must provide a non-`NULL` `GUID` in the `reparse_guid` /// member. When retrieving a reparse point from the file system, `reparse_guid` /// is the `GUID` assigned when the reparse point was set. - pub reparse_guid: GUID, + pub reparse_guid: Guid, /// The user-defined data for the reparse point. The contents are determined by /// the reparse point implementer. The tag in the `reparse_tag` member and the /// `GUID` in the `reparse_guid` member indicate how the data is to be interpreted. @@ -96,7 +96,7 @@ pub struct ReparseGuidDataBuffer { impl std::fmt::Debug for ReparseGuidDataBuffer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let GUID { a, b, c, d } = self.reparse_guid; + let Guid { a, b, c, d } = self.reparse_guid; f.debug_struct("ReparseGuidDataBuffer") .field("reparse_tag", &self.reparse_tag) .field("reparse_data_length", &self.reparse_data_length) From aa70ff410f17700bdd1258ca2743ddf9d88b9116 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 17:12:27 +0700 Subject: [PATCH 18/46] Shorten import paths --- src/internals.rs | 21 +++++++++++---------- src/internals/helpers.rs | 23 ++++++++++++----------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 1985076c..163f0265 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -8,7 +8,8 @@ use std::mem::size_of; use std::os::windows::ffi::OsStringExt; use std::os::windows::io::AsRawHandle; use std::path::{Path, PathBuf}; -use std::{cmp, fs, io, ptr, slice}; +use std::ptr::{addr_of_mut, copy_nonoverlapping}; +use std::{cmp, fs, io, slice}; use cast::BytesAsReparseDataBuffer; use types::{ReparseDataBuffer, MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE, REPARSE_DATA_BUFFER_HEADER_SIZE}; @@ -53,27 +54,27 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { let rdb = data.as_mut_ptr(); let in_buffer_size: u16 = unsafe { // Set the type of reparse point we are creating - ptr::addr_of_mut!((*rdb).reparse_tag).write(c::IO_REPARSE_TAG_MOUNT_POINT); - ptr::addr_of_mut!((*rdb).reserved).write(0); + addr_of_mut!((*rdb).reparse_tag).write(c::IO_REPARSE_TAG_MOUNT_POINT); + addr_of_mut!((*rdb).reserved).write(0); // Copy the junction's target - ptr::addr_of_mut!((*rdb).reparse_buffer.substitute_name_offset).write(0); - ptr::addr_of_mut!((*rdb).reparse_buffer.substitute_name_length).write(target_len_in_bytes); + addr_of_mut!((*rdb).reparse_buffer.substitute_name_offset).write(0); + addr_of_mut!((*rdb).reparse_buffer.substitute_name_length).write(target_len_in_bytes); // Copy the junction's link name - ptr::addr_of_mut!((*rdb).reparse_buffer.print_name_offset).write(target_len_in_bytes + UNICODE_NULL_SIZE); - ptr::addr_of_mut!((*rdb).reparse_buffer.print_name_length).write(0); + addr_of_mut!((*rdb).reparse_buffer.print_name_offset).write(target_len_in_bytes + UNICODE_NULL_SIZE); + addr_of_mut!((*rdb).reparse_buffer.print_name_length).write(0); // Safe because we checked `MAX_AVAILABLE_PATH_BUFFER` - ptr::copy_nonoverlapping( + copy_nonoverlapping( target_wchar.as_ptr().cast::(), - ptr::addr_of_mut!((*rdb).reparse_buffer.path_buffer).cast(), + addr_of_mut!((*rdb).reparse_buffer.path_buffer).cast(), target_wchar.len(), ); // Set the total size of the data buffer let size = target_len_in_bytes.wrapping_add(MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE + 2 * UNICODE_NULL_SIZE); - ptr::addr_of_mut!((*rdb).reparse_data_length).write(size); + addr_of_mut!((*rdb).reparse_data_length).write(size); size.wrapping_add(REPARSE_DATA_BUFFER_HEADER_SIZE) }; diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index c9506db5..4004fa10 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -2,11 +2,12 @@ mod utf16; use std::ffi::OsStr; use std::fs::{File, OpenOptions}; +use std::io; use std::mem::{self, MaybeUninit}; use std::os::windows::ffi::OsStrExt; use std::os::windows::fs::OpenOptionsExt; use std::path::Path; -use std::{io, ptr}; +use std::ptr::{addr_of_mut, null, null_mut}; use scopeguard::ScopeGuard; pub(crate) use utf16::utf16s; @@ -51,12 +52,12 @@ fn set_privilege(rdwr: bool) -> io::Result<()> { } else { SE_BACKUP_NAME.as_ptr() }; - if c::LookupPrivilegeValueW(ptr::null(), name, &mut tp.Privileges[0].Luid) == 0 { + if c::LookupPrivilegeValueW(null(), name, &mut tp.Privileges[0].Luid) == 0 { return Err(io::Error::last_os_error()); } tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = c::SE_PRIVILEGE_ENABLED; - if c::AdjustTokenPrivileges(*handle, 0, &tp, TOKEN_PRIVILEGES_SIZE, ptr::null_mut(), ptr::null_mut()) == 0 { + if c::AdjustTokenPrivileges(*handle, 0, &tp, TOKEN_PRIVILEGES_SIZE, null_mut(), null_mut()) == 0 { return Err(io::Error::last_os_error()); } if c::GetLastError() == ERROR_NOT_ALL_ASSIGNED { @@ -79,12 +80,12 @@ pub fn get_reparse_data_point(handle: c::HANDLE, rdb: *mut ReparseDataBuffer) -> c::DeviceIoControl( handle, c::FSCTL_GET_REPARSE_POINT, - ptr::null_mut(), + null_mut(), 0, rdb.cast(), c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &mut bytes_returned, - ptr::null_mut(), + null_mut(), ) } == 0 { @@ -101,10 +102,10 @@ pub fn set_reparse_point(handle: c::HANDLE, rdb: *mut ReparseDataBuffer, len: u3 c::FSCTL_SET_REPARSE_POINT, rdb.cast(), len, - ptr::null_mut(), + null_mut(), 0, &mut bytes_returned, - ptr::null_mut(), + null_mut(), ) } == 0 { @@ -123,12 +124,12 @@ pub fn delete_reparse_point(handle: c::HANDLE) -> io::Result<()> { c::DeviceIoControl( handle, c::FSCTL_DELETE_REPARSE_POINT, - ptr::addr_of_mut!(rgdb).cast(), + addr_of_mut!(rgdb).cast(), u32::from(REPARSE_GUID_DATA_BUFFER_HEADER_SIZE), - ptr::null_mut(), + null_mut(), 0, &mut bytes_returned, - ptr::null_mut(), + null_mut(), ) } == 0 { @@ -146,7 +147,7 @@ type MaybeU16 = MaybeUninit; // Ref: . pub fn get_full_path(target: &Path) -> io::Result> { let path = os_str_to_utf16(target.as_os_str()); - let file_part = ptr::null_mut(); + let file_part = null_mut(); const U16_UNINIT: MaybeU16 = MaybeU16::uninit(); const ERROR_INSUFFICIENT_BUFFER: u32 = 122; // Start off with a stack buf but then spill over to the heap if we end up From 79811a79528cd73604d4eb0945c0e91f83a54352 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 17:22:00 +0700 Subject: [PATCH 19/46] Stable toolchain now supports sparse registry --- .github/workflows/ci.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b101220a..1b41d4a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,9 +7,6 @@ on: - '!gh-pages' pull_request: -env: - SPARSE_TOOLCHAIN: stable - jobs: build: runs-on: windows-latest @@ -31,8 +28,8 @@ jobs: steps: - uses: actions/checkout@v3 - run: | - rustup toolchain install "${{ env.SPARSE_TOOLCHAIN }}-${{ matrix.target }}" - rustup default "${{ env.SPARSE_TOOLCHAIN }}-${{ matrix.target }}" + rustup toolchain install stable-${{ matrix.target }} + rustup default stable-${{ matrix.target }} - name: Check out MinGW toolchain run: | set -x @@ -75,11 +72,10 @@ jobs: - uses: actions/checkout@v3 - run: | rustup toolchain install ${{ env.MSRV }} - rustup toolchain install ${{ env.SPARSE_TOOLCHAIN }} rustup default ${{ env.MSRV }} - run: | - rustup run ${{ env.SPARSE_TOOLCHAIN }} cargo generate-lockfile - rustup run ${{ env.SPARSE_TOOLCHAIN }} cargo fetch + rustup run stable cargo generate-lockfile + rustup run stable cargo fetch - run: | cargo build --all-targets --locked - run: | From 63c1d7daa9ab9db69cb6e230e8ca694f16e883d3 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 17:50:57 +0700 Subject: [PATCH 20/46] Remove complex pieces --- src/internals/helpers.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 4004fa10..311629ce 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -38,11 +38,10 @@ fn set_privilege(rdwr: bool) -> io::Result<()> { const ERROR_NOT_ALL_ASSIGNED: u32 = 1300; const TOKEN_PRIVILEGES_SIZE: u32 = mem::size_of::() as _; unsafe { - let mut handle = >::uninit(); - if c::OpenProcessToken(c::GetCurrentProcess(), c::TOKEN_ADJUST_PRIVILEGES, handle.as_mut_ptr()) == 0 { + let mut handle: c::HANDLE = 0; + if c::OpenProcessToken(c::GetCurrentProcess(), c::TOKEN_ADJUST_PRIVILEGES, &mut handle) == 0 { return Err(io::Error::last_os_error()); } - let handle = handle.assume_init(); let handle = scopeguard::guard(handle, |h| { c::CloseHandle(h); }); From 392516ba2943b3a3bd61e555e500314db777895c Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 17:56:29 +0700 Subject: [PATCH 21/46] Casting from Maybe and T is safe --- src/internals/helpers.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 311629ce..fd01c864 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -197,11 +197,12 @@ pub fn get_full_path(target: &Path) -> io::Result> { } } -unsafe fn maybe_slice_to_ptr(s: &mut [MaybeU16]) -> *mut u16 { +fn maybe_slice_to_ptr(s: &mut [MaybeU16]) -> *mut u16 { + // SAFETY: `MaybeUninit` and T are guaranteed to have the same layout s.as_mut_ptr() as *mut u16 } -unsafe fn maybe_slice_assume_init(s: &[MaybeU16]) -> &[u16] { +fn maybe_slice_assume_init(s: &[MaybeU16]) -> &[u16] { // SAFETY: `MaybeUninit` and T are guaranteed to have the same layout - &*(s as *const [MaybeU16] as *const [u16]) + unsafe { &*(s as *const [MaybeU16] as *const [u16]) } } From 92286e92dd15374eea9656bc092130817cfe3043 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 21:19:26 +0700 Subject: [PATCH 22/46] Merge module types to c.rs --- src/internals.rs | 10 ++-- src/internals/c.rs | 108 +++++++++++++++++++++++++++++++++++++++ src/internals/cast.rs | 3 +- src/internals/helpers.rs | 2 +- src/internals/types.rs | 108 --------------------------------------- 5 files changed, 114 insertions(+), 117 deletions(-) delete mode 100644 src/internals/types.rs diff --git a/src/internals.rs b/src/internals.rs index 163f0265..1d312103 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -1,7 +1,6 @@ mod c; mod cast; mod helpers; -mod types; use std::ffi::OsString; use std::mem::size_of; @@ -12,7 +11,6 @@ use std::ptr::{addr_of_mut, copy_nonoverlapping}; use std::{cmp, fs, io, slice}; use cast::BytesAsReparseDataBuffer; -use types::{ReparseDataBuffer, MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE, REPARSE_DATA_BUFFER_HEADER_SIZE}; /// This prefix indicates to NTFS that the path is to be treated as a non-interpreted /// path in the virtual file system. @@ -23,8 +21,8 @@ const WCHAR_SIZE: u16 = size_of::() as _; pub fn create(target: &Path, junction: &Path) -> io::Result<()> { const UNICODE_NULL_SIZE: u16 = WCHAR_SIZE; const MAX_AVAILABLE_PATH_BUFFER: u16 = c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as u16 - - REPARSE_DATA_BUFFER_HEADER_SIZE - - MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE + - c::REPARSE_DATA_BUFFER_HEADER_SIZE + - c::MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE - 2 * UNICODE_NULL_SIZE; // We're using low-level APIs to create the junction, and these are more picky about paths. @@ -73,9 +71,9 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { ); // Set the total size of the data buffer - let size = target_len_in_bytes.wrapping_add(MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE + 2 * UNICODE_NULL_SIZE); + let size = target_len_in_bytes.wrapping_add(c::MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE + 2 * UNICODE_NULL_SIZE); addr_of_mut!((*rdb).reparse_data_length).write(size); - size.wrapping_add(REPARSE_DATA_BUFFER_HEADER_SIZE) + size.wrapping_add(c::REPARSE_DATA_BUFFER_HEADER_SIZE) }; helpers::set_reparse_point(file.as_raw_handle() as isize, rdb, u32::from(in_buffer_size)) diff --git a/src/internals/c.rs b/src/internals/c.rs index 8cc7e8d3..56849bfc 100644 --- a/src/internals/c.rs +++ b/src/internals/c.rs @@ -1,4 +1,5 @@ use std::alloc::Layout; +use std::os::raw::{c_uchar, c_ulong, c_ushort}; use std::os::windows::io::RawHandle; pub use windows_sys::Win32::Foundation::{ @@ -28,3 +29,110 @@ const _: () = { [(); 1][!(std_layout.size() == winapi_layout.size()) as usize]; [(); 1][!(std_layout.align() == winapi_layout.align()) as usize]; }; + +// NOTE: to use `size_of` operator, below structs should be packed. +/// Reparse Data Buffer header size = `sizeof(u32) + 2 * sizeof(u16)` +pub const REPARSE_DATA_BUFFER_HEADER_SIZE: u16 = 8; +/// Reparse GUID Data Buffer header size = `sizeof(u32) + 2*sizeof(u16) + sizeof(GUID)` +pub const REPARSE_GUID_DATA_BUFFER_HEADER_SIZE: u16 = 24; +/// MountPointReparseBuffer header size = `4 * sizeof(u16)` +pub const MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE: u16 = 8; + +type VarLenArr = [T; 1]; + +#[repr(C)] +#[derive(Debug)] +pub struct MountPointReparseBuffer { + /// Offset, in bytes, of the substitute name string in the `path_buffer` array. + /// Note that this offset must be divided by `sizeof(u16)` to get the array index. + pub substitute_name_offset: u16, + /// Length, in bytes, of the substitute name string. If this string is `NULL`-terminated, + /// it does not include space for the `UNICODE_NULL` character. + pub substitute_name_length: u16, + /// Offset, in bytes, of the print name string in the `path_buffer` array. + /// Note that this offset must be divided by `sizeof(u16)` to get the array index. + pub print_name_offset: u16, + /// Length, in bytes, of the print name string. If this string is `NULL`-terminated, + /// it does not include space for the `UNICODE_NULL` character. + pub print_name_length: u16, + /// A buffer containing the Unicode-encoded path string. The path string contains the + /// substitute name string and print name string. The substitute name and print name strings + /// can appear in any order in the path_buffer. (To locate the substitute name and print name + /// strings in the path_buffer, use the `substitute_name_offset`, `substitute_name_length`, + /// `print_name_offset`, and `print_name_length` members.) + pub path_buffer: VarLenArr, +} + +/// This structure contains reparse point data for a Microsoft reparse point. +/// +/// Read more: +/// * https://msdn.microsoft.com/en-us/windows/desktop/ff552012 +/// * https://www.pinvoke.net/default.aspx/Structures.REPARSE_DATA_BUFFER +#[repr(C)] +#[derive(Debug)] +pub struct ReparseDataBuffer { + /// Reparse point tag. Must be a Microsoft reparse point tag. + pub reparse_tag: u32, + /// Size, in bytes, of the reparse data in the `data_buffer` member. + /// Or the size of the `path_buffer` field, in bytes, plus 8 (= 4 * sizeof(u16)) + pub reparse_data_length: u16, + /// Reversed. It SHOULD be set to 0, and MUST be ignored. + pub reserved: u16, + pub reparse_buffer: MountPointReparseBuffer, +} + +#[repr(C)] +#[derive(Debug)] +pub struct GenericReparseBuffer { + /// Microsoft-defined data for the reparse point. + pub data_buffer: VarLenArr, +} + +#[repr(C)] +pub struct Guid { + pub a: c_ulong, + pub b: c_ushort, + pub c: c_ushort, + pub d: [c_uchar; 8], +} + +/// Used by all third-party layered drivers to store data for a reparse point. +/// +/// Each reparse point contains one instance of a `ReparseGuidDataBuffer` structure. +/// +/// Read more: +/// * +#[repr(C)] +pub struct ReparseGuidDataBuffer { + /// Reparse point tag. This member identifies the structure of the user-defined + /// reparse data. + pub reparse_tag: u32, + /// The size of the reparse data in the `data_buffer` member, in bytes. This + /// value may vary with different tags and may vary between two uses of the + /// same tag. + pub reparse_data_length: u16, + /// Reserved; do not use. + pub reserved: u16, + /// A `GUID` that uniquely identifies the reparse point. When setting a reparse + /// point, the application must provide a non-`NULL` `GUID` in the `reparse_guid` + /// member. When retrieving a reparse point from the file system, `reparse_guid` + /// is the `GUID` assigned when the reparse point was set. + pub reparse_guid: Guid, + /// The user-defined data for the reparse point. The contents are determined by + /// the reparse point implementer. The tag in the `reparse_tag` member and the + /// `GUID` in the `reparse_guid` member indicate how the data is to be interpreted. + pub generic: GenericReparseBuffer, +} + +impl std::fmt::Debug for ReparseGuidDataBuffer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Guid { a, b, c, d } = self.reparse_guid; + f.debug_struct("ReparseGuidDataBuffer") + .field("reparse_tag", &self.reparse_tag) + .field("reparse_data_length", &self.reparse_data_length) + .field("reserved", &self.reserved) + .field("reparse_guid", &format_args!("{}:{}:{}:{:?}", a, b, c, d)) + .field("generic", &self.generic.data_buffer) + .finish() + } +} diff --git a/src/internals/cast.rs b/src/internals/cast.rs index b5f72aa4..b9233157 100644 --- a/src/internals/cast.rs +++ b/src/internals/cast.rs @@ -1,8 +1,7 @@ use std::alloc::{alloc, handle_alloc_error, Layout}; use std::mem::align_of; -use super::c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE; -use crate::internals::ReparseDataBuffer; +use super::c::{ReparseDataBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE}; type MaybeU8 = std::mem::MaybeUninit; diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index fd01c864..3ed490a3 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -13,7 +13,7 @@ use scopeguard::ScopeGuard; pub(crate) use utf16::utf16s; use super::c; -use super::types::{ReparseDataBuffer, ReparseGuidDataBuffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE}; +use super::c::{ReparseDataBuffer, ReparseGuidDataBuffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE}; pub static SE_RESTORE_NAME: [u16; 19] = utf16s(b"SeRestorePrivilege\0"); pub static SE_BACKUP_NAME: [u16; 18] = utf16s(b"SeBackupPrivilege\0"); diff --git a/src/internals/types.rs b/src/internals/types.rs deleted file mode 100644 index 42dd8bb6..00000000 --- a/src/internals/types.rs +++ /dev/null @@ -1,108 +0,0 @@ -use std::os::raw::{c_uchar, c_ulong, c_ushort}; - -// NOTE: to use `size_of` operator, below structs should be packed. -/// Reparse Data Buffer header size = `sizeof(u32) + 2 * sizeof(u16)` -pub const REPARSE_DATA_BUFFER_HEADER_SIZE: u16 = 8; -/// Reparse GUID Data Buffer header size = `sizeof(u32) + 2*sizeof(u16) + sizeof(GUID)` -pub const REPARSE_GUID_DATA_BUFFER_HEADER_SIZE: u16 = 24; -/// MountPointReparseBuffer header size = `4 * sizeof(u16)` -pub const MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE: u16 = 8; - -type VarLenArr = [T; 1]; - -#[repr(C)] -#[derive(Debug)] -pub struct MountPointReparseBuffer { - /// Offset, in bytes, of the substitute name string in the `path_buffer` array. - /// Note that this offset must be divided by `sizeof(u16)` to get the array index. - pub substitute_name_offset: u16, - /// Length, in bytes, of the substitute name string. If this string is `NULL`-terminated, - /// it does not include space for the `UNICODE_NULL` character. - pub substitute_name_length: u16, - /// Offset, in bytes, of the print name string in the `path_buffer` array. - /// Note that this offset must be divided by `sizeof(u16)` to get the array index. - pub print_name_offset: u16, - /// Length, in bytes, of the print name string. If this string is `NULL`-terminated, - /// it does not include space for the `UNICODE_NULL` character. - pub print_name_length: u16, - /// A buffer containing the Unicode-encoded path string. The path string contains the - /// substitute name string and print name string. The substitute name and print name strings - /// can appear in any order in the path_buffer. (To locate the substitute name and print name - /// strings in the path_buffer, use the `substitute_name_offset`, `substitute_name_length`, - /// `print_name_offset`, and `print_name_length` members.) - pub path_buffer: VarLenArr, -} - -/// This structure contains reparse point data for a Microsoft reparse point. -/// -/// Read more: -/// * https://msdn.microsoft.com/en-us/windows/desktop/ff552012 -/// * https://www.pinvoke.net/default.aspx/Structures.REPARSE_DATA_BUFFER -#[repr(C)] -#[derive(Debug)] -pub struct ReparseDataBuffer { - /// Reparse point tag. Must be a Microsoft reparse point tag. - pub reparse_tag: u32, - /// Size, in bytes, of the reparse data in the `data_buffer` member. - /// Or the size of the `path_buffer` field, in bytes, plus 8 (= 4 * sizeof(u16)) - pub reparse_data_length: u16, - /// Reversed. It SHOULD be set to 0, and MUST be ignored. - pub reserved: u16, - pub reparse_buffer: MountPointReparseBuffer, -} - -#[repr(C)] -#[derive(Debug)] -pub struct GenericReparseBuffer { - /// Microsoft-defined data for the reparse point. - pub data_buffer: VarLenArr, -} - -#[repr(C)] -pub struct Guid { - pub a: c_ulong, - pub b: c_ushort, - pub c: c_ushort, - pub d: [c_uchar; 8], -} - -/// Used by all third-party layered drivers to store data for a reparse point. -/// -/// Each reparse point contains one instance of a `ReparseGuidDataBuffer` structure. -/// -/// Read more: -/// * -#[repr(C)] -pub struct ReparseGuidDataBuffer { - /// Reparse point tag. This member identifies the structure of the user-defined - /// reparse data. - pub reparse_tag: u32, - /// The size of the reparse data in the `data_buffer` member, in bytes. This - /// value may vary with different tags and may vary between two uses of the - /// same tag. - pub reparse_data_length: u16, - /// Reserved; do not use. - pub reserved: u16, - /// A `GUID` that uniquely identifies the reparse point. When setting a reparse - /// point, the application must provide a non-`NULL` `GUID` in the `reparse_guid` - /// member. When retrieving a reparse point from the file system, `reparse_guid` - /// is the `GUID` assigned when the reparse point was set. - pub reparse_guid: Guid, - /// The user-defined data for the reparse point. The contents are determined by - /// the reparse point implementer. The tag in the `reparse_tag` member and the - /// `GUID` in the `reparse_guid` member indicate how the data is to be interpreted. - pub generic: GenericReparseBuffer, -} - -impl std::fmt::Debug for ReparseGuidDataBuffer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Guid { a, b, c, d } = self.reparse_guid; - f.debug_struct("ReparseGuidDataBuffer") - .field("reparse_tag", &self.reparse_tag) - .field("reparse_data_length", &self.reparse_data_length) - .field("reserved", &self.reserved) - .field("reparse_guid", &format_args!("{}:{}:{}:{:?}", a, b, c, d)) - .field("generic", &self.generic.data_buffer) - .finish() - } -} From 2cdf0a66211f02c304af42689f6a9421228e2149 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 21:41:32 +0700 Subject: [PATCH 23/46] Convert c.rs to c idioms For FFI calls using the same style as C++ really makes it more readable. --- src/internals.rs | 30 ++++++++++----------- src/internals/c.rs | 56 +++++++++++++++++++++------------------- src/internals/cast.rs | 8 +++--- src/internals/helpers.rs | 6 ++--- 4 files changed, 52 insertions(+), 48 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 1d312103..2d350585 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -21,7 +21,7 @@ const WCHAR_SIZE: u16 = size_of::() as _; pub fn create(target: &Path, junction: &Path) -> io::Result<()> { const UNICODE_NULL_SIZE: u16 = WCHAR_SIZE; const MAX_AVAILABLE_PATH_BUFFER: u16 = c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as u16 - - c::REPARSE_DATA_BUFFER_HEADER_SIZE + - c::REPARSE_DATA_BUFFER::HEADER_SIZE - c::MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE - 2 * UNICODE_NULL_SIZE; @@ -52,28 +52,28 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { let rdb = data.as_mut_ptr(); let in_buffer_size: u16 = unsafe { // Set the type of reparse point we are creating - addr_of_mut!((*rdb).reparse_tag).write(c::IO_REPARSE_TAG_MOUNT_POINT); - addr_of_mut!((*rdb).reserved).write(0); + addr_of_mut!((*rdb).ReparseTag).write(c::IO_REPARSE_TAG_MOUNT_POINT); + addr_of_mut!((*rdb).Reserved).write(0); // Copy the junction's target - addr_of_mut!((*rdb).reparse_buffer.substitute_name_offset).write(0); - addr_of_mut!((*rdb).reparse_buffer.substitute_name_length).write(target_len_in_bytes); + addr_of_mut!((*rdb).ReparseBuffer.SubstituteNameOffset).write(0); + addr_of_mut!((*rdb).ReparseBuffer.SubstituteNameLength).write(target_len_in_bytes); // Copy the junction's link name - addr_of_mut!((*rdb).reparse_buffer.print_name_offset).write(target_len_in_bytes + UNICODE_NULL_SIZE); - addr_of_mut!((*rdb).reparse_buffer.print_name_length).write(0); + addr_of_mut!((*rdb).ReparseBuffer.PrintNameOffset).write(target_len_in_bytes + UNICODE_NULL_SIZE); + addr_of_mut!((*rdb).ReparseBuffer.PrintNameLength).write(0); // Safe because we checked `MAX_AVAILABLE_PATH_BUFFER` copy_nonoverlapping( target_wchar.as_ptr().cast::(), - addr_of_mut!((*rdb).reparse_buffer.path_buffer).cast(), + addr_of_mut!((*rdb).ReparseBuffer.PathBuffer).cast(), target_wchar.len(), ); // Set the total size of the data buffer let size = target_len_in_bytes.wrapping_add(c::MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE + 2 * UNICODE_NULL_SIZE); - addr_of_mut!((*rdb).reparse_data_length).write(size); - size.wrapping_add(c::REPARSE_DATA_BUFFER_HEADER_SIZE) + addr_of_mut!((*rdb).ReparseDataLength).write(size); + size.wrapping_add(c::REPARSE_DATA_BUFFER::HEADER_SIZE) }; helpers::set_reparse_point(file.as_raw_handle() as isize, rdb, u32::from(in_buffer_size)) @@ -94,7 +94,7 @@ pub fn exists(junction: &Path) -> io::Result { let rdb = data.as_mut_ptr(); helpers::get_reparse_data_point(file.as_raw_handle() as isize, rdb)?; // The reparse tag indicates if this is a junction or not - Ok(unsafe { (*rdb).reparse_tag } == c::IO_REPARSE_TAG_MOUNT_POINT) + Ok(unsafe { (*rdb).ReparseTag } == c::IO_REPARSE_TAG_MOUNT_POINT) } pub fn get_target(junction: &Path) -> io::Result { @@ -108,11 +108,11 @@ pub fn get_target(junction: &Path) -> io::Result { helpers::get_reparse_data_point(file.as_raw_handle() as isize, rdb)?; // SAFETY: rdb should be initialized now let rdb = unsafe { &*rdb }; - if rdb.reparse_tag == c::IO_REPARSE_TAG_MOUNT_POINT { - let offset = rdb.reparse_buffer.substitute_name_offset / WCHAR_SIZE; - let len = rdb.reparse_buffer.substitute_name_length / WCHAR_SIZE; + if rdb.ReparseTag == c::IO_REPARSE_TAG_MOUNT_POINT { + let offset = rdb.ReparseBuffer.SubstituteNameOffset / WCHAR_SIZE; + let len = rdb.ReparseBuffer.SubstituteNameLength / WCHAR_SIZE; let wide = unsafe { - let buf = rdb.reparse_buffer.path_buffer.as_ptr().add(offset as usize); + let buf = rdb.ReparseBuffer.PathBuffer.as_ptr().add(offset as usize); slice::from_raw_parts(buf, len as usize) }; // In case of "\??\C:\foo\bar" diff --git a/src/internals/c.rs b/src/internals/c.rs index 56849bfc..3aaa6ee1 100644 --- a/src/internals/c.rs +++ b/src/internals/c.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + use std::alloc::Layout; use std::os::raw::{c_uchar, c_ulong, c_ushort}; use std::os::windows::io::RawHandle; @@ -30,55 +32,57 @@ const _: () = { [(); 1][!(std_layout.align() == winapi_layout.align()) as usize]; }; -// NOTE: to use `size_of` operator, below structs should be packed. -/// Reparse Data Buffer header size = `sizeof(u32) + 2 * sizeof(u16)` -pub const REPARSE_DATA_BUFFER_HEADER_SIZE: u16 = 8; /// Reparse GUID Data Buffer header size = `sizeof(u32) + 2*sizeof(u16) + sizeof(GUID)` pub const REPARSE_GUID_DATA_BUFFER_HEADER_SIZE: u16 = 24; /// MountPointReparseBuffer header size = `4 * sizeof(u16)` pub const MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE: u16 = 8; type VarLenArr = [T; 1]; +/// This structure contains reparse point data for a Microsoft reparse point. +/// +/// Read more: +/// * https://msdn.microsoft.com/en-us/windows/desktop/ff552012 +/// * https://www.pinvoke.net/default.aspx/Structures.REPARSE_DATA_BUFFER +#[repr(C)] +#[derive(Debug)] +pub struct REPARSE_DATA_BUFFER { + /// Reparse point tag. Must be a Microsoft reparse point tag. + pub ReparseTag: c_ulong, + /// Size, in bytes, of the reparse data in the `data_buffer` member. + /// Or the size of the `path_buffer` field, in bytes, plus 8 (= 4 * sizeof(u16)) + pub ReparseDataLength: c_ushort, + /// Reversed. It SHOULD be set to 0, and MUST be ignored. + pub Reserved: c_ushort, + pub ReparseBuffer: MountPointReparseBuffer, +} + +impl REPARSE_DATA_BUFFER { + // NOTE: to use `size_of` operator, below structs should be packed. + /// Reparse Data Buffer header size = `sizeof(u32) + 2 * sizeof(u16)` + pub const HEADER_SIZE: u16 = 8; +} #[repr(C)] #[derive(Debug)] pub struct MountPointReparseBuffer { /// Offset, in bytes, of the substitute name string in the `path_buffer` array. /// Note that this offset must be divided by `sizeof(u16)` to get the array index. - pub substitute_name_offset: u16, + pub SubstituteNameOffset: c_ushort, /// Length, in bytes, of the substitute name string. If this string is `NULL`-terminated, /// it does not include space for the `UNICODE_NULL` character. - pub substitute_name_length: u16, + pub SubstituteNameLength: c_ushort, /// Offset, in bytes, of the print name string in the `path_buffer` array. /// Note that this offset must be divided by `sizeof(u16)` to get the array index. - pub print_name_offset: u16, + pub PrintNameOffset: c_ushort, /// Length, in bytes, of the print name string. If this string is `NULL`-terminated, /// it does not include space for the `UNICODE_NULL` character. - pub print_name_length: u16, + pub PrintNameLength: c_ushort, /// A buffer containing the Unicode-encoded path string. The path string contains the /// substitute name string and print name string. The substitute name and print name strings /// can appear in any order in the path_buffer. (To locate the substitute name and print name /// strings in the path_buffer, use the `substitute_name_offset`, `substitute_name_length`, /// `print_name_offset`, and `print_name_length` members.) - pub path_buffer: VarLenArr, -} - -/// This structure contains reparse point data for a Microsoft reparse point. -/// -/// Read more: -/// * https://msdn.microsoft.com/en-us/windows/desktop/ff552012 -/// * https://www.pinvoke.net/default.aspx/Structures.REPARSE_DATA_BUFFER -#[repr(C)] -#[derive(Debug)] -pub struct ReparseDataBuffer { - /// Reparse point tag. Must be a Microsoft reparse point tag. - pub reparse_tag: u32, - /// Size, in bytes, of the reparse data in the `data_buffer` member. - /// Or the size of the `path_buffer` field, in bytes, plus 8 (= 4 * sizeof(u16)) - pub reparse_data_length: u16, - /// Reversed. It SHOULD be set to 0, and MUST be ignored. - pub reserved: u16, - pub reparse_buffer: MountPointReparseBuffer, + pub PathBuffer: VarLenArr, } #[repr(C)] diff --git a/src/internals/cast.rs b/src/internals/cast.rs index b9233157..cf7d6582 100644 --- a/src/internals/cast.rs +++ b/src/internals/cast.rs @@ -1,7 +1,7 @@ use std::alloc::{alloc, handle_alloc_error, Layout}; use std::mem::align_of; -use super::c::{ReparseDataBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE}; +use super::c::{MAXIMUM_REPARSE_DATA_BUFFER_SIZE, REPARSE_DATA_BUFFER}; type MaybeU8 = std::mem::MaybeUninit; @@ -12,7 +12,7 @@ pub struct BytesAsReparseDataBuffer { const _: () = { let a = align_of::(); - let b = align_of::(); + let b = align_of::(); [(); 1][!((a % b) == 0) as usize] }; @@ -30,7 +30,7 @@ impl BytesAsReparseDataBuffer { Self { value: boxed } } - pub fn as_mut_ptr(&mut self) -> *mut ReparseDataBuffer { - self.value.as_mut_ptr().cast::() + pub fn as_mut_ptr(&mut self) -> *mut REPARSE_DATA_BUFFER { + self.value.as_mut_ptr().cast::() } } diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 3ed490a3..a87b7f93 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -13,7 +13,7 @@ use scopeguard::ScopeGuard; pub(crate) use utf16::utf16s; use super::c; -use super::c::{ReparseDataBuffer, ReparseGuidDataBuffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE}; +use super::c::{ReparseGuidDataBuffer, REPARSE_DATA_BUFFER, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE}; pub static SE_RESTORE_NAME: [u16; 19] = utf16s(b"SeRestorePrivilege\0"); pub static SE_BACKUP_NAME: [u16; 18] = utf16s(b"SeBackupPrivilege\0"); @@ -72,7 +72,7 @@ fn set_privilege(rdwr: bool) -> io::Result<()> { } } -pub fn get_reparse_data_point(handle: c::HANDLE, rdb: *mut ReparseDataBuffer) -> io::Result<()> { +pub fn get_reparse_data_point(handle: c::HANDLE, rdb: *mut REPARSE_DATA_BUFFER) -> io::Result<()> { // Call DeviceIoControl to get the reparse point data let mut bytes_returned: u32 = 0; if unsafe { @@ -93,7 +93,7 @@ pub fn get_reparse_data_point(handle: c::HANDLE, rdb: *mut ReparseDataBuffer) -> Ok(()) } -pub fn set_reparse_point(handle: c::HANDLE, rdb: *mut ReparseDataBuffer, len: u32) -> io::Result<()> { +pub fn set_reparse_point(handle: c::HANDLE, rdb: *mut REPARSE_DATA_BUFFER, len: u32) -> io::Result<()> { let mut bytes_returned: u32 = 0; if unsafe { c::DeviceIoControl( From 90eb9304f9ffe402cc05c1423b89b130807d109d Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 21:55:19 +0700 Subject: [PATCH 24/46] Do not redefine struct from windows-sys --- src/internals.rs | 4 +-- src/internals/c.rs | 75 +++++----------------------------------- src/internals/helpers.rs | 11 +++--- 3 files changed, 16 insertions(+), 74 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 2d350585..c6815735 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -21,7 +21,7 @@ const WCHAR_SIZE: u16 = size_of::() as _; pub fn create(target: &Path, junction: &Path) -> io::Result<()> { const UNICODE_NULL_SIZE: u16 = WCHAR_SIZE; const MAX_AVAILABLE_PATH_BUFFER: u16 = c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as u16 - - c::REPARSE_DATA_BUFFER::HEADER_SIZE + - c::REPARSE_DATA_BUFFER_HEADER_SIZE - c::MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE - 2 * UNICODE_NULL_SIZE; @@ -73,7 +73,7 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { // Set the total size of the data buffer let size = target_len_in_bytes.wrapping_add(c::MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE + 2 * UNICODE_NULL_SIZE); addr_of_mut!((*rdb).ReparseDataLength).write(size); - size.wrapping_add(c::REPARSE_DATA_BUFFER::HEADER_SIZE) + size.wrapping_add(c::REPARSE_DATA_BUFFER_HEADER_SIZE) }; helpers::set_reparse_point(file.as_raw_handle() as isize, rdb, u32::from(in_buffer_size)) diff --git a/src/internals/c.rs b/src/internals/c.rs index 3aaa6ee1..bdd4e267 100644 --- a/src/internals/c.rs +++ b/src/internals/c.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use std::alloc::Layout; -use std::os::raw::{c_uchar, c_ulong, c_ushort}; +use std::os::raw::{c_ulong, c_ushort}; use std::os::windows::io::RawHandle; pub use windows_sys::Win32::Foundation::{ @@ -12,6 +12,7 @@ pub use windows_sys::Win32::Security::{ }; pub use windows_sys::Win32::Storage::FileSystem::{ GetFullPathNameW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, + REPARSE_GUID_DATA_BUFFER, }; pub use windows_sys::Win32::System::Ioctl::{ FSCTL_DELETE_REPARSE_POINT, FSCTL_GET_REPARSE_POINT, FSCTL_SET_REPARSE_POINT, @@ -26,18 +27,22 @@ pub use windows_sys::Win32::System::IO::DeviceIoControl; #[allow(clippy::unnecessary_operation)] const _: () = { let std_layout = Layout::new::(); - let winapi_layout = Layout::new::(); + let win_sys_layout = Layout::new::(); // MSVR(Rust v1.57): use assert! instead - [(); 1][!(std_layout.size() == winapi_layout.size()) as usize]; - [(); 1][!(std_layout.align() == winapi_layout.align()) as usize]; + [(); 1][!(std_layout.size() == win_sys_layout.size()) as usize]; + [(); 1][!(std_layout.align() == win_sys_layout.align()) as usize]; }; +// NOTE: to use `size_of` operator, below structs should be packed. +/// Reparse Data Buffer header size = `sizeof(u32) + 2 * sizeof(u16)` +pub const REPARSE_DATA_BUFFER_HEADER_SIZE: u16 = 8; /// Reparse GUID Data Buffer header size = `sizeof(u32) + 2*sizeof(u16) + sizeof(GUID)` pub const REPARSE_GUID_DATA_BUFFER_HEADER_SIZE: u16 = 24; /// MountPointReparseBuffer header size = `4 * sizeof(u16)` pub const MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE: u16 = 8; type VarLenArr = [T; 1]; + /// This structure contains reparse point data for a Microsoft reparse point. /// /// Read more: @@ -56,12 +61,6 @@ pub struct REPARSE_DATA_BUFFER { pub ReparseBuffer: MountPointReparseBuffer, } -impl REPARSE_DATA_BUFFER { - // NOTE: to use `size_of` operator, below structs should be packed. - /// Reparse Data Buffer header size = `sizeof(u32) + 2 * sizeof(u16)` - pub const HEADER_SIZE: u16 = 8; -} - #[repr(C)] #[derive(Debug)] pub struct MountPointReparseBuffer { @@ -84,59 +83,3 @@ pub struct MountPointReparseBuffer { /// `print_name_offset`, and `print_name_length` members.) pub PathBuffer: VarLenArr, } - -#[repr(C)] -#[derive(Debug)] -pub struct GenericReparseBuffer { - /// Microsoft-defined data for the reparse point. - pub data_buffer: VarLenArr, -} - -#[repr(C)] -pub struct Guid { - pub a: c_ulong, - pub b: c_ushort, - pub c: c_ushort, - pub d: [c_uchar; 8], -} - -/// Used by all third-party layered drivers to store data for a reparse point. -/// -/// Each reparse point contains one instance of a `ReparseGuidDataBuffer` structure. -/// -/// Read more: -/// * -#[repr(C)] -pub struct ReparseGuidDataBuffer { - /// Reparse point tag. This member identifies the structure of the user-defined - /// reparse data. - pub reparse_tag: u32, - /// The size of the reparse data in the `data_buffer` member, in bytes. This - /// value may vary with different tags and may vary between two uses of the - /// same tag. - pub reparse_data_length: u16, - /// Reserved; do not use. - pub reserved: u16, - /// A `GUID` that uniquely identifies the reparse point. When setting a reparse - /// point, the application must provide a non-`NULL` `GUID` in the `reparse_guid` - /// member. When retrieving a reparse point from the file system, `reparse_guid` - /// is the `GUID` assigned when the reparse point was set. - pub reparse_guid: Guid, - /// The user-defined data for the reparse point. The contents are determined by - /// the reparse point implementer. The tag in the `reparse_tag` member and the - /// `GUID` in the `reparse_guid` member indicate how the data is to be interpreted. - pub generic: GenericReparseBuffer, -} - -impl std::fmt::Debug for ReparseGuidDataBuffer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Guid { a, b, c, d } = self.reparse_guid; - f.debug_struct("ReparseGuidDataBuffer") - .field("reparse_tag", &self.reparse_tag) - .field("reparse_data_length", &self.reparse_data_length) - .field("reserved", &self.reserved) - .field("reparse_guid", &format_args!("{}:{}:{}:{:?}", a, b, c, d)) - .field("generic", &self.generic.data_buffer) - .finish() - } -} diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index a87b7f93..87a5b30d 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -13,7 +13,6 @@ use scopeguard::ScopeGuard; pub(crate) use utf16::utf16s; use super::c; -use super::c::{ReparseGuidDataBuffer, REPARSE_DATA_BUFFER, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE}; pub static SE_RESTORE_NAME: [u16; 19] = utf16s(b"SeRestorePrivilege\0"); pub static SE_BACKUP_NAME: [u16; 18] = utf16s(b"SeBackupPrivilege\0"); @@ -72,7 +71,7 @@ fn set_privilege(rdwr: bool) -> io::Result<()> { } } -pub fn get_reparse_data_point(handle: c::HANDLE, rdb: *mut REPARSE_DATA_BUFFER) -> io::Result<()> { +pub fn get_reparse_data_point(handle: c::HANDLE, rdb: *mut c::REPARSE_DATA_BUFFER) -> io::Result<()> { // Call DeviceIoControl to get the reparse point data let mut bytes_returned: u32 = 0; if unsafe { @@ -93,7 +92,7 @@ pub fn get_reparse_data_point(handle: c::HANDLE, rdb: *mut REPARSE_DATA_BUFFER) Ok(()) } -pub fn set_reparse_point(handle: c::HANDLE, rdb: *mut REPARSE_DATA_BUFFER, len: u32) -> io::Result<()> { +pub fn set_reparse_point(handle: c::HANDLE, rdb: *mut c::REPARSE_DATA_BUFFER, len: u32) -> io::Result<()> { let mut bytes_returned: u32 = 0; if unsafe { c::DeviceIoControl( @@ -115,8 +114,8 @@ pub fn set_reparse_point(handle: c::HANDLE, rdb: *mut REPARSE_DATA_BUFFER, len: // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa364560(v=vs.85).aspx pub fn delete_reparse_point(handle: c::HANDLE) -> io::Result<()> { - let mut rgdb: ReparseGuidDataBuffer = unsafe { mem::zeroed() }; - rgdb.reparse_tag = c::IO_REPARSE_TAG_MOUNT_POINT; + let mut rgdb: c::REPARSE_GUID_DATA_BUFFER = unsafe { mem::zeroed() }; + rgdb.ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT; let mut bytes_returned: u32 = 0; if unsafe { @@ -124,7 +123,7 @@ pub fn delete_reparse_point(handle: c::HANDLE) -> io::Result<()> { handle, c::FSCTL_DELETE_REPARSE_POINT, addr_of_mut!(rgdb).cast(), - u32::from(REPARSE_GUID_DATA_BUFFER_HEADER_SIZE), + u32::from(c::REPARSE_GUID_DATA_BUFFER_HEADER_SIZE), null_mut(), 0, &mut bytes_returned, From 5fcb5f03cf3120d3f698b9ee7defdcaaeb9f2ad3 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 22:50:44 +0700 Subject: [PATCH 25/46] Breaking changes: only set SE_CREATE_SYMBOLIC_LINK_NAME privilege --- src/internals.rs | 2 +- src/internals/c.rs | 2 +- src/internals/helpers.rs | 24 ++++++++++-------------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index c6815735..7525263e 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -80,7 +80,7 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { } pub fn delete(junction: &Path) -> io::Result<()> { - let file = helpers::open_reparse_point(junction, true)?; + let file = helpers::open_reparse_point(junction, false)?; helpers::delete_reparse_point(file.as_raw_handle() as isize) } diff --git a/src/internals/c.rs b/src/internals/c.rs index bdd4e267..7b0edfcc 100644 --- a/src/internals/c.rs +++ b/src/internals/c.rs @@ -5,7 +5,7 @@ use std::os::raw::{c_ulong, c_ushort}; use std::os::windows::io::RawHandle; pub use windows_sys::Win32::Foundation::{ - CloseHandle, GetLastError, SetLastError, GENERIC_READ, GENERIC_WRITE, HANDLE, + CloseHandle, GetLastError, SetLastError, FALSE, GENERIC_READ, GENERIC_WRITE, HANDLE, TRUE, }; pub use windows_sys::Win32::Security::{ AdjustTokenPrivileges, LookupPrivilegeValueW, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 87a5b30d..a8411272 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -14,26 +14,26 @@ pub(crate) use utf16::utf16s; use super::c; -pub static SE_RESTORE_NAME: [u16; 19] = utf16s(b"SeRestorePrivilege\0"); -pub static SE_BACKUP_NAME: [u16; 18] = utf16s(b"SeBackupPrivilege\0"); +// See more in . +pub static SE_CREATE_SYMBOLIC_LINK_NAME: [u16; 30] = utf16s(b"SeCreateSymbolicLinkPrivilege\0"); -pub fn open_reparse_point(reparse_point: &Path, rdwr: bool) -> io::Result { - let access = c::GENERIC_READ | if rdwr { c::GENERIC_WRITE } else { 0 }; +pub fn open_reparse_point(reparse_point: &Path, write: bool) -> io::Result { + let access = c::GENERIC_READ | if write { c::GENERIC_WRITE } else { 0 }; let mut opts = OpenOptions::new(); opts.access_mode(access) .share_mode(0) .custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); match opts.open(reparse_point) { - Err(e) if e.kind() == io::ErrorKind::PermissionDenied => { - // Obtain privilege in case we don't have it yet - set_privilege(rdwr)?; + Err(e) if e.kind() == io::ErrorKind::PermissionDenied && write => { + // FSCTL_SET_REPARSE_POINT requires SE_CREATE_SYMBOLIC_LINK_NAME privilege + set_privilege()?; opts.open(reparse_point) } other => other, } } -fn set_privilege(rdwr: bool) -> io::Result<()> { +fn set_privilege() -> io::Result<()> { const ERROR_NOT_ALL_ASSIGNED: u32 = 1300; const TOKEN_PRIVILEGES_SIZE: u32 = mem::size_of::() as _; unsafe { @@ -45,17 +45,13 @@ fn set_privilege(rdwr: bool) -> io::Result<()> { c::CloseHandle(h); }); let mut tp: c::TOKEN_PRIVILEGES = mem::zeroed(); - let name = if rdwr { - SE_RESTORE_NAME.as_ptr() - } else { - SE_BACKUP_NAME.as_ptr() - }; + let name = SE_CREATE_SYMBOLIC_LINK_NAME.as_ptr(); if c::LookupPrivilegeValueW(null(), name, &mut tp.Privileges[0].Luid) == 0 { return Err(io::Error::last_os_error()); } tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = c::SE_PRIVILEGE_ENABLED; - if c::AdjustTokenPrivileges(*handle, 0, &tp, TOKEN_PRIVILEGES_SIZE, null_mut(), null_mut()) == 0 { + if c::AdjustTokenPrivileges(*handle, c::FALSE, &tp, TOKEN_PRIVILEGES_SIZE, null_mut(), null_mut()) == 0 { return Err(io::Error::last_os_error()); } if c::GetLastError() == ERROR_NOT_ALL_ASSIGNED { From f242061b1fcd0240bb60ad087c0b89cc190eee12 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sat, 15 Jul 2023 23:12:57 +0700 Subject: [PATCH 26/46] test: print error on panics --- src/internals.rs | 2 +- src/internals/helpers.rs | 2 +- src/tests.rs | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 7525263e..c6815735 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -80,7 +80,7 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { } pub fn delete(junction: &Path) -> io::Result<()> { - let file = helpers::open_reparse_point(junction, false)?; + let file = helpers::open_reparse_point(junction, true)?; helpers::delete_reparse_point(file.as_raw_handle() as isize) } diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index a8411272..25748b43 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -24,7 +24,7 @@ pub fn open_reparse_point(reparse_point: &Path, write: bool) -> io::Result .share_mode(0) .custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); match opts.open(reparse_point) { - Err(e) if e.kind() == io::ErrorKind::PermissionDenied && write => { + Err(e) if e.kind() == io::ErrorKind::PermissionDenied => { // FSCTL_SET_REPARSE_POINT requires SE_CREATE_SYMBOLIC_LINK_NAME privilege set_privilege()?; opts.open(reparse_point) diff --git a/src/tests.rs b/src/tests.rs index 961e5e45..698012ee 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -105,7 +105,7 @@ fn create_directory_exist_before() { match super::create(target, &junction) { Err(ref e) if e.raw_os_error() == Some(ERROR_ALREADY_EXISTS) => {} - _ => panic!("directory exists before creating"), + other => panic!("directory exists before creating: {:?}", other), } } @@ -118,7 +118,7 @@ fn create_target_no_exist() { match super::create(target, junction) { Ok(()) => {} - _ => panic!("junction should point to non exist target path"), + other => panic!("junction should point to non exist target path: {:?}", other), } } @@ -129,21 +129,21 @@ fn delete_junctions() { let non_existence_dir = tmpdir.path().join("non_existence_dir"); match super::delete(non_existence_dir) { Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} - _ => panic!("target path does not exist or is not a directory"), + e => panic!("target path does not exist or is not a directory: {:?}", e), } let dir_not_junction = tmpdir.path().join("dir_not_junction"); fs::create_dir_all(&dir_not_junction).unwrap(); match super::delete(dir_not_junction) { Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} - _ => panic!("target path is not a junction point"), + e => panic!("target path is not a junction point: {:?}", e), } let file = tmpdir.path().join("foo-file"); File::create(&file).unwrap().write_all(b"foo").unwrap(); match super::delete(&file) { Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} - _ => panic!("target path is not a junction point"), + e => panic!("target path is not a junction point: {:?}", e), } } @@ -160,7 +160,7 @@ fn exists_verify() { File::create(&no_such_file).unwrap().write_all(b"foo").unwrap(); match super::exists(&no_such_file) { Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} - _ => panic!("target exists but not a junction"), + other => panic!("target exists but not a junction: {:?}", other), } let target = tmpdir.path().join("target"); @@ -185,7 +185,7 @@ fn exists_verify() { super::delete(&junction).unwrap(); match super::exists(&junction) { Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} - _ => panic!("junction had been deleted"), + other => panic!("junction had been deleted: {:?}", other), } assert!( !junction_file.exists(), @@ -212,20 +212,20 @@ fn get_target_user_dirs() { let non_existence_dir = tmpdir.path().join("non_existence_dir"); match super::get_target(non_existence_dir) { Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} - _ => panic!("target path does not exist or is not a directory"), + other => panic!("target path does not exist or is not a directory: {:?}", other), } let dir_not_junction = tmpdir.path().join("dir_not_junction"); fs::create_dir_all(&dir_not_junction).unwrap(); match super::get_target(dir_not_junction) { Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} - _ => panic!("target path is not a junction point"), + other => panic!("target path is not a junction point: {:?}", other), } let file = tmpdir.path().join("foo-file"); File::create(&file).unwrap().write_all(b"foo").unwrap(); match super::get_target(file) { Err(ref e) if e.raw_os_error() == Some(ERROR_NOT_A_REPARSE_POINT) => {} - _ => panic!("target path is not a junction point"), + other => panic!("target path is not a junction point: {:?}", other), } } From b4dbb44fa42e48de4de081e5596cb59daa936fb9 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 17 Jul 2023 20:27:19 +0700 Subject: [PATCH 27/46] readme: more about junctions --- README.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8e8db085..29370dc4 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,25 @@ -# junction - -Library for working with NTFS junctions. - [![Build Status][actions-badge]][actions-url] [![Documentation](https://docs.rs/junction/badge.svg)](https://docs.rs/junction) [![Crates.io](https://img.shields.io/crates/v/junction.svg)](https://crates.io/crates/junction) +# junction + +Library for working with [NTFS junctions][junction]. + +As opposed to symlinks, junction does not require administrator privileges during creation.[perm] +(*However starting with Windows 10 Insiders build 14972, symlinks +can be created without needing to elevate the console as +administrator*)[^1][improvement]. + +Quoted from [Computer Hope](https://www.computerhope.com/jargon/j/junction.htm): + +> A junction, also called an NTFS junction point, is a feature of the +> NTFS file system. It is pointer to a directory on the local volume, +> similar to a symlink. It can be accessed through the Windows GUI in +> addition to the Windows command line. Junction points were first +> introduced with Windows 2000 and NTFS 3.0, and are supported in all +> subsequent versions of Windows. + ### Minimal Supported Rust versions 1.51.0 @@ -14,7 +28,7 @@ Library for working with NTFS junctions. * https://www.codeproject.com/Articles/194/Windows-2000-Junction-Points#The_Solution * https://www.codeproject.com/Articles/15633/Manipulating-NTFS-Junction-Points-in-NET -* http://www.flexhex.com/docs/articles/hard-links.phtml +* https://web.archive.org/web/20230411010804/http://www.flexhex.com/docs/articles/hard-links.phtml * https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html * https://github.com/googleprojectzero/symboliclink-testing-tools/blob/master/DumpReparsePoint/DumpReparsePoint.cpp * https://github.com/googleprojectzero/symboliclink-testing-tools/blob/master/CommonUtils/ReparsePoint.cpp @@ -27,3 +41,6 @@ for more information, please read COPYRIGHT file. [actions-badge]: https://github.com/lzutao/junction/workflows/Rust/badge.svg?branchName=master [actions-url]: https://github.com/lzutao/junction/actions +[junction]: https://learn.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions#junctions +[perm]: https://en.wikipedia.org/wiki/NTFS_links#Restrictions_and_drawbacks +[improvement]: https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/#Ed9Olhkz6hJp4KWV.97 From e45538162bf3d8d8a9f773e14e5887b8203d9b97 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 17 Jul 2023 22:27:05 +0700 Subject: [PATCH 28/46] size safety checks --- src/internals/c.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/internals/c.rs b/src/internals/c.rs index 7b0edfcc..ff87f425 100644 --- a/src/internals/c.rs +++ b/src/internals/c.rs @@ -1,9 +1,11 @@ #![allow(non_snake_case)] use std::alloc::Layout; +use std::mem::size_of; use std::os::raw::{c_ulong, c_ushort}; use std::os::windows::io::RawHandle; +use windows_sys::core::GUID; pub use windows_sys::Win32::Foundation::{ CloseHandle, GetLastError, SetLastError, FALSE, GENERIC_READ, GENERIC_WRITE, HANDLE, TRUE, }; @@ -34,13 +36,26 @@ const _: () = { }; // NOTE: to use `size_of` operator, below structs should be packed. -/// Reparse Data Buffer header size = `sizeof(u32) + 2 * sizeof(u16)` +// TODO: use `offset_of!` when stabilized. +/// Reparse Data Buffer header size pub const REPARSE_DATA_BUFFER_HEADER_SIZE: u16 = 8; -/// Reparse GUID Data Buffer header size = `sizeof(u32) + 2*sizeof(u16) + sizeof(GUID)` +/// Reparse GUID Data Buffer header size pub const REPARSE_GUID_DATA_BUFFER_HEADER_SIZE: u16 = 24; -/// MountPointReparseBuffer header size = `4 * sizeof(u16)` +/// MountPointReparseBuffer header size pub const MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE: u16 = 8; +// Safety checks for correct header size due to the lacks of `offset_of!`. +const _: () = { + let rdb_header_size = size_of::() + size_of::() * 2; + assert!(rdb_header_size == REPARSE_DATA_BUFFER_HEADER_SIZE as _); + + let mprb_header_size = size_of::() * 4; + assert!(mprb_header_size == MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE as _); + + let rgdb_header_size = size_of::() + size_of::() * 2 + size_of::(); + assert!(rgdb_header_size == REPARSE_GUID_DATA_BUFFER_HEADER_SIZE as _); +}; + type VarLenArr = [T; 1]; /// This structure contains reparse point data for a Microsoft reparse point. From eb35602ec41027149197649ef0f75533a5555a3e Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 17 Jul 2023 22:28:35 +0700 Subject: [PATCH 29/46] style: shorten imports --- src/internals/helpers.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 25748b43..db7fa0ac 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -3,7 +3,7 @@ mod utf16; use std::ffi::OsStr; use std::fs::{File, OpenOptions}; use std::io; -use std::mem::{self, MaybeUninit}; +use std::mem::{size_of, zeroed, MaybeUninit}; use std::os::windows::ffi::OsStrExt; use std::os::windows::fs::OpenOptionsExt; use std::path::Path; @@ -35,7 +35,7 @@ pub fn open_reparse_point(reparse_point: &Path, write: bool) -> io::Result fn set_privilege() -> io::Result<()> { const ERROR_NOT_ALL_ASSIGNED: u32 = 1300; - const TOKEN_PRIVILEGES_SIZE: u32 = mem::size_of::() as _; + const TOKEN_PRIVILEGES_SIZE: u32 = size_of::() as _; unsafe { let mut handle: c::HANDLE = 0; if c::OpenProcessToken(c::GetCurrentProcess(), c::TOKEN_ADJUST_PRIVILEGES, &mut handle) == 0 { @@ -44,7 +44,7 @@ fn set_privilege() -> io::Result<()> { let handle = scopeguard::guard(handle, |h| { c::CloseHandle(h); }); - let mut tp: c::TOKEN_PRIVILEGES = mem::zeroed(); + let mut tp: c::TOKEN_PRIVILEGES = zeroed(); let name = SE_CREATE_SYMBOLIC_LINK_NAME.as_ptr(); if c::LookupPrivilegeValueW(null(), name, &mut tp.Privileges[0].Luid) == 0 { return Err(io::Error::last_os_error()); @@ -110,7 +110,7 @@ pub fn set_reparse_point(handle: c::HANDLE, rdb: *mut c::REPARSE_DATA_BUFFER, le // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa364560(v=vs.85).aspx pub fn delete_reparse_point(handle: c::HANDLE) -> io::Result<()> { - let mut rgdb: c::REPARSE_GUID_DATA_BUFFER = unsafe { mem::zeroed() }; + let mut rgdb: c::REPARSE_GUID_DATA_BUFFER = unsafe { zeroed() }; rgdb.ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT; let mut bytes_returned: u32 = 0; From 57df55abfa7c0c1b92aa6af936cdf3a36d66f0a6 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 17 Jul 2023 22:55:51 +0700 Subject: [PATCH 30/46] closehandle may fail but can we handle it nicely? --- src/internals/helpers.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index db7fa0ac..aa7d57c3 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -9,7 +9,6 @@ use std::os::windows::fs::OpenOptionsExt; use std::path::Path; use std::ptr::{addr_of_mut, null, null_mut}; -use scopeguard::ScopeGuard; pub(crate) use utf16::utf16s; use super::c; @@ -57,14 +56,8 @@ fn set_privilege() -> io::Result<()> { if c::GetLastError() == ERROR_NOT_ALL_ASSIGNED { return Err(io::Error::from_raw_os_error(ERROR_NOT_ALL_ASSIGNED as i32)); } - - let handle = ScopeGuard::into_inner(handle); - if c::CloseHandle(handle) == 0 { - Err(io::Error::last_os_error()) - } else { - Ok(()) - } } + Ok(()) } pub fn get_reparse_data_point(handle: c::HANDLE, rdb: *mut c::REPARSE_DATA_BUFFER) -> io::Result<()> { From 649e274a01e00ba7053bb94d9ad3d16542ea1b2f Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 17 Jul 2023 22:58:59 +0700 Subject: [PATCH 31/46] use windows-sys consts --- src/internals/c.rs | 5 ++++- src/internals/helpers.rs | 14 ++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/internals/c.rs b/src/internals/c.rs index ff87f425..27bf2b04 100644 --- a/src/internals/c.rs +++ b/src/internals/c.rs @@ -7,11 +7,14 @@ use std::os::windows::io::RawHandle; use windows_sys::core::GUID; pub use windows_sys::Win32::Foundation::{ - CloseHandle, GetLastError, SetLastError, FALSE, GENERIC_READ, GENERIC_WRITE, HANDLE, TRUE, + CloseHandle, GetLastError, SetLastError, ERROR_INSUFFICIENT_BUFFER, FALSE, GENERIC_READ, GENERIC_WRITE, HANDLE, + INVALID_HANDLE_VALUE, }; pub use windows_sys::Win32::Security::{ AdjustTokenPrivileges, LookupPrivilegeValueW, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, }; +// See more in . +pub use windows_sys::Win32::Security::{SE_BACKUP_NAME, SE_CREATE_SYMBOLIC_LINK_NAME, SE_RESTORE_NAME}; pub use windows_sys::Win32::Storage::FileSystem::{ GetFullPathNameW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, REPARSE_GUID_DATA_BUFFER, diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index aa7d57c3..0fef099c 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -13,9 +13,6 @@ pub(crate) use utf16::utf16s; use super::c; -// See more in . -pub static SE_CREATE_SYMBOLIC_LINK_NAME: [u16; 30] = utf16s(b"SeCreateSymbolicLinkPrivilege\0"); - pub fn open_reparse_point(reparse_point: &Path, write: bool) -> io::Result { let access = c::GENERIC_READ | if write { c::GENERIC_WRITE } else { 0 }; let mut opts = OpenOptions::new(); @@ -24,7 +21,6 @@ pub fn open_reparse_point(reparse_point: &Path, write: bool) -> io::Result .custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); match opts.open(reparse_point) { Err(e) if e.kind() == io::ErrorKind::PermissionDenied => { - // FSCTL_SET_REPARSE_POINT requires SE_CREATE_SYMBOLIC_LINK_NAME privilege set_privilege()?; opts.open(reparse_point) } @@ -36,7 +32,7 @@ fn set_privilege() -> io::Result<()> { const ERROR_NOT_ALL_ASSIGNED: u32 = 1300; const TOKEN_PRIVILEGES_SIZE: u32 = size_of::() as _; unsafe { - let mut handle: c::HANDLE = 0; + let mut handle: c::HANDLE = c::INVALID_HANDLE_VALUE; if c::OpenProcessToken(c::GetCurrentProcess(), c::TOKEN_ADJUST_PRIVILEGES, &mut handle) == 0 { return Err(io::Error::last_os_error()); } @@ -44,7 +40,9 @@ fn set_privilege() -> io::Result<()> { c::CloseHandle(h); }); let mut tp: c::TOKEN_PRIVILEGES = zeroed(); - let name = SE_CREATE_SYMBOLIC_LINK_NAME.as_ptr(); + // FSCTL_SET_REPARSE_POINT requires SE_CREATE_SYMBOLIC_LINK_NAME privilege + // Ref + let name = c::SE_CREATE_SYMBOLIC_LINK_NAME; if c::LookupPrivilegeValueW(null(), name, &mut tp.Privileges[0].Luid) == 0 { return Err(io::Error::last_os_error()); } @@ -136,7 +134,6 @@ pub fn get_full_path(target: &Path) -> io::Result> { let path = os_str_to_utf16(target.as_os_str()); let file_part = null_mut(); const U16_UNINIT: MaybeU16 = MaybeU16::uninit(); - const ERROR_INSUFFICIENT_BUFFER: u32 = 122; // Start off with a stack buf but then spill over to the heap if we end up // needing more space. // @@ -172,11 +169,12 @@ pub fn get_full_path(target: &Path) -> io::Result> { if k == 0 { return Err(crate::io::Error::last_os_error()); } - if c::GetLastError() == ERROR_INSUFFICIENT_BUFFER { + if c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER { n = n.saturating_mul(2).min(u32::MAX as usize); } else if k > n { n = k; } else { + // TODO(perf): reduce an allocation by using `heap_buf.set_len(k)` // Safety: First `k` values are initialized. let slice: &[u16] = maybe_slice_assume_init(&buf[..k]); return Ok(slice.into()); From 8450e60b0a1ce71f96f5e94b6baeba3afe4602ec Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 17 Jul 2023 23:23:11 +0700 Subject: [PATCH 32/46] docs: Add more documentation and safety comments --- src/internals.rs | 10 ++++++---- src/internals/c.rs | 16 +++++++++------- src/internals/helpers.rs | 23 +++++++++++------------ src/lib.rs | 8 ++++++-- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index c6815735..d9033a5d 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -37,7 +37,7 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { let min_len = cmp::min(len, u16::MAX as usize) as u16; // Len without `UNICODE_NULL` at the end let target_len_in_bytes = min_len.saturating_mul(WCHAR_SIZE); - // Check if `target_wchar.len()` may lead to a buffer overflow. + // Check for buffer overflow. if target_len_in_bytes > MAX_AVAILABLE_PATH_BUFFER { return Err(io::Error::new(io::ErrorKind::InvalidInput, "`target` is too long")); } @@ -55,11 +55,11 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { addr_of_mut!((*rdb).ReparseTag).write(c::IO_REPARSE_TAG_MOUNT_POINT); addr_of_mut!((*rdb).Reserved).write(0); - // Copy the junction's target + // We write target at offset 0 of PathBuffer addr_of_mut!((*rdb).ReparseBuffer.SubstituteNameOffset).write(0); addr_of_mut!((*rdb).ReparseBuffer.SubstituteNameLength).write(target_len_in_bytes); - // Copy the junction's link name + // We do not use PrintName. However let's set its offset correctly right after SubstituteName addr_of_mut!((*rdb).ReparseBuffer.PrintNameOffset).write(target_len_in_bytes + UNICODE_NULL_SIZE); addr_of_mut!((*rdb).ReparseBuffer.PrintNameLength).write(0); @@ -93,8 +93,10 @@ pub fn exists(junction: &Path) -> io::Result { let mut data = BytesAsReparseDataBuffer::new(); let rdb = data.as_mut_ptr(); helpers::get_reparse_data_point(file.as_raw_handle() as isize, rdb)?; + // SATETY: rdb should be initialized now + let rdb = unsafe { &*rdb }; // The reparse tag indicates if this is a junction or not - Ok(unsafe { (*rdb).ReparseTag } == c::IO_REPARSE_TAG_MOUNT_POINT) + Ok(rdb.ReparseTag == c::IO_REPARSE_TAG_MOUNT_POINT) } pub fn get_target(junction: &Path) -> io::Result { diff --git a/src/internals/c.rs b/src/internals/c.rs index 27bf2b04..0bf7b2c5 100644 --- a/src/internals/c.rs +++ b/src/internals/c.rs @@ -71,8 +71,10 @@ type VarLenArr = [T; 1]; pub struct REPARSE_DATA_BUFFER { /// Reparse point tag. Must be a Microsoft reparse point tag. pub ReparseTag: c_ulong, - /// Size, in bytes, of the reparse data in the `data_buffer` member. - /// Or the size of the `path_buffer` field, in bytes, plus 8 (= 4 * sizeof(u16)) + // Size, in bytes, of the data after the Reserved member. + // This can be calculated by: + // MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE + SubstituteNameLength + // + PrintNameLength + (names.nul_terminated() ? 2 * sizeof(char) : 0); pub ReparseDataLength: c_ushort, /// Reversed. It SHOULD be set to 0, and MUST be ignored. pub Reserved: c_ushort, @@ -82,13 +84,13 @@ pub struct REPARSE_DATA_BUFFER { #[repr(C)] #[derive(Debug)] pub struct MountPointReparseBuffer { - /// Offset, in bytes, of the substitute name string in the `path_buffer` array. + /// Offset, in bytes, of the substitute name string in the `PathBuffer` array. /// Note that this offset must be divided by `sizeof(u16)` to get the array index. pub SubstituteNameOffset: c_ushort, /// Length, in bytes, of the substitute name string. If this string is `NULL`-terminated, /// it does not include space for the `UNICODE_NULL` character. pub SubstituteNameLength: c_ushort, - /// Offset, in bytes, of the print name string in the `path_buffer` array. + /// Offset, in bytes, of the print name string in the `PathBuffer` array. /// Note that this offset must be divided by `sizeof(u16)` to get the array index. pub PrintNameOffset: c_ushort, /// Length, in bytes, of the print name string. If this string is `NULL`-terminated, @@ -96,8 +98,8 @@ pub struct MountPointReparseBuffer { pub PrintNameLength: c_ushort, /// A buffer containing the Unicode-encoded path string. The path string contains the /// substitute name string and print name string. The substitute name and print name strings - /// can appear in any order in the path_buffer. (To locate the substitute name and print name - /// strings in the path_buffer, use the `substitute_name_offset`, `substitute_name_length`, - /// `print_name_offset`, and `print_name_length` members.) + /// can appear in any order in the PathBuffer. (To locate the substitute name and print name + /// strings in the PathBuffer, use the `SubstituteNameOffset`, `SubstituteNameLength`, + /// `PrintNameOffset`, and `PrintNameLength` members.) pub PathBuffer: VarLenArr, } diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 0fef099c..a36decff 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -15,10 +15,14 @@ use super::c; pub fn open_reparse_point(reparse_point: &Path, write: bool) -> io::Result { let access = c::GENERIC_READ | if write { c::GENERIC_WRITE } else { 0 }; + // Set this flag to obtain a handle to a directory. Appropriate security checks + // still apply when this flag is used without SE_BACKUP_NAME and SE_RESTORE_NAME + // privileges. + // Ref + let dir_attrs = c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS; let mut opts = OpenOptions::new(); - opts.access_mode(access) - .share_mode(0) - .custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); + opts.access_mode(access).share_mode(0).custom_flags(dir_attrs); + // Opens existing directory path match opts.open(reparse_point) { Err(e) if e.kind() == io::ErrorKind::PermissionDenied => { set_privilege()?; @@ -128,11 +132,11 @@ fn os_str_to_utf16(s: &OsStr) -> Vec { } type MaybeU16 = MaybeUninit; -// Returns the len of buf when success. -// Ref: . +// Returns canonical path without the terminating null character. +// Ref: rust-lang/rust/blob/master/library/std/src/sys/windows/mod.rs#L198 pub fn get_full_path(target: &Path) -> io::Result> { let path = os_str_to_utf16(target.as_os_str()); - let file_part = null_mut(); + let path = path.as_ptr().cast::(); const U16_UNINIT: MaybeU16 = MaybeU16::uninit(); // Start off with a stack buf but then spill over to the heap if we end up // needing more space. @@ -160,12 +164,7 @@ pub fn get_full_path(target: &Path) -> io::Result> { }; c::SetLastError(0); - let k = c::GetFullPathNameW( - path.as_ptr().cast::(), - n as u32, - maybe_slice_to_ptr(buf), - file_part, - ) as usize; + let k = c::GetFullPathNameW(path, n as u32, maybe_slice_to_ptr(buf), null_mut()) as usize; if k == 0 { return Err(crate::io::Error::last_os_error()); } diff --git a/src/lib.rs b/src/lib.rs index 5f86f792..b32ca321 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,10 @@ use std::path::{Path, PathBuf}; /// /// N.B. Only works on NTFS. /// +/// # Error +/// +/// This function may error if the `junction` path already exists. +/// /// # Example /// /// ```rust @@ -53,8 +57,8 @@ where /// /// N.B. Only works on NTFS. /// -/// This function does not delete the file or directory. Also it does nothing -/// if the `junction` point does not exist. +/// This function delete the junction point only, leaving the target directory +/// and its content as is. It does nothing if the `junction` point does not exist. /// /// # Example /// From bbf144ae6613e9cb63234f9b3c1df4745298ba8f Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 18 Jul 2023 17:26:28 +0700 Subject: [PATCH 33/46] doc: add some todo! for later reconsideration --- src/internals.rs | 2 ++ src/internals/helpers.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/internals.rs b/src/internals.rs index d9033a5d..e9746faa 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -92,6 +92,8 @@ pub fn exists(junction: &Path) -> io::Result { // Allocate enough space to fit the maximum sized reparse data buffer let mut data = BytesAsReparseDataBuffer::new(); let rdb = data.as_mut_ptr(); + // XXX: Could also use FindFirstFile to read the reparse point type + // Ref https://learn.microsoft.com/en-us/windows/win32/fileio/reparse-point-tags helpers::get_reparse_data_point(file.as_raw_handle() as isize, rdb)?; // SATETY: rdb should be initialized now let rdb = unsafe { &*rdb }; diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index a36decff..2150f88f 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -105,6 +105,7 @@ pub fn set_reparse_point(handle: c::HANDLE, rdb: *mut c::REPARSE_DATA_BUFFER, le // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa364560(v=vs.85).aspx pub fn delete_reparse_point(handle: c::HANDLE) -> io::Result<()> { + // TODO: Should we use REPARSE_DATA_BUFFER instead? let mut rgdb: c::REPARSE_GUID_DATA_BUFFER = unsafe { zeroed() }; rgdb.ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT; let mut bytes_returned: u32 = 0; From b3d109d319351f75d08cf663de31d2c79f7eb8e4 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 18 Jul 2023 17:27:49 +0700 Subject: [PATCH 34/46] nits: reduce one alloc --- src/internals.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index e9746faa..b32a7eef 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -28,12 +28,12 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { // We're using low-level APIs to create the junction, and these are more picky about paths. // For example, forward slashes cannot be used as a path separator, so we should try to // canonicalize the path first. - let mut target = helpers::get_full_path(target)?; + let target = helpers::get_full_path(target)?; fs::create_dir(junction)?; let file = helpers::open_reparse_point(junction, true)?; - // "\??\" + target - let len = NON_INTERPRETED_PATH_PREFIX.len().saturating_add(target.len()); let target_len_in_bytes = { + // "\??\" + target + let len = NON_INTERPRETED_PATH_PREFIX.len().saturating_add(target.len()); let min_len = cmp::min(len, u16::MAX as usize) as u16; // Len without `UNICODE_NULL` at the end let target_len_in_bytes = min_len.saturating_mul(WCHAR_SIZE); @@ -43,9 +43,6 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { } target_len_in_bytes }; - let mut target_wchar: Vec = Vec::with_capacity(len); - target_wchar.extend(&NON_INTERPRETED_PATH_PREFIX); - target_wchar.append(&mut target); // Redefine the above char array into a ReparseDataBuffer we can work with let mut data = BytesAsReparseDataBuffer::new(); @@ -65,9 +62,18 @@ pub fn create(target: &Path, junction: &Path) -> io::Result<()> { // Safe because we checked `MAX_AVAILABLE_PATH_BUFFER` copy_nonoverlapping( - target_wchar.as_ptr().cast::(), - addr_of_mut!((*rdb).ReparseBuffer.PathBuffer).cast(), - target_wchar.len(), + NON_INTERPRETED_PATH_PREFIX.as_ptr(), + addr_of_mut!((*rdb).ReparseBuffer.PathBuffer).cast::(), + NON_INTERPRETED_PATH_PREFIX.len(), + ); + // TODO: Do we need to write the NULL-terminator byte? + // It looks like libuv does that. + copy_nonoverlapping( + target.as_ptr(), + addr_of_mut!((*rdb).ReparseBuffer.PathBuffer) + .cast::() + .add(NON_INTERPRETED_PATH_PREFIX.len()), + target.len(), ); // Set the total size of the data buffer From f6a4163ebe9745efcdc73343f3c7d99bd6a36635 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 18 Jul 2023 23:56:29 +0700 Subject: [PATCH 35/46] feat, breaking: add a new feature flag unstable_admin To use special Windows privileges to read/change reparse point information of some system directory junctions. Users build with `default-features` on will not be affected. --- Cargo.toml | 16 ++++++++++++++++ src/internals/helpers.rs | 21 +++++++++++++++------ src/lib.rs | 2 ++ src/tests.rs | 1 + 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3eb39d49..a491c434 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,22 @@ readme = "README.md" repository = "https://github.com/lzutao/junction" description = "library for working with NTFS junctions" +[features] +default = ["unstable_admin"] +# # Unstable flag +# +# Enable the SE_BACKUP_NAME and SE_RESTORE_NAME access privileges. +# They are needed to open system directory junctions (such as +# "C:\Documents and Settings") to read. For more info, please read +# . +# +# Why is this flag unstable? +# +# Maybe it would be better to save and restore the old privileges +# after setting SE_RESTORE_NAME. A user with SE_RESTORE_NAME privilege +# could easily overwrite almost any file on the system. +unstable_admin = [] + [package.metadata.docs.rs] targets = ["x86_64-pc-windows-msvc"] diff --git a/src/internals/helpers.rs b/src/internals/helpers.rs index 2150f88f..7b74c538 100644 --- a/src/internals/helpers.rs +++ b/src/internals/helpers.rs @@ -25,14 +25,14 @@ pub fn open_reparse_point(reparse_point: &Path, write: bool) -> io::Result // Opens existing directory path match opts.open(reparse_point) { Err(e) if e.kind() == io::ErrorKind::PermissionDenied => { - set_privilege()?; + set_privilege(write)?; opts.open(reparse_point) } other => other, } } -fn set_privilege() -> io::Result<()> { +fn set_privilege(write: bool) -> io::Result<()> { const ERROR_NOT_ALL_ASSIGNED: u32 = 1300; const TOKEN_PRIVILEGES_SIZE: u32 = size_of::() as _; unsafe { @@ -43,15 +43,24 @@ fn set_privilege() -> io::Result<()> { let handle = scopeguard::guard(handle, |h| { c::CloseHandle(h); }); + let name = if cfg!(feature = "unstable_admin") { + if write { + c::SE_RESTORE_NAME + } else { + c::SE_BACKUP_NAME + } + } else { + // FSCTL_SET_REPARSE_POINT requires SE_CREATE_SYMBOLIC_LINK_NAME privilege + // Ref + c::SE_CREATE_SYMBOLIC_LINK_NAME + }; let mut tp: c::TOKEN_PRIVILEGES = zeroed(); - // FSCTL_SET_REPARSE_POINT requires SE_CREATE_SYMBOLIC_LINK_NAME privilege - // Ref - let name = c::SE_CREATE_SYMBOLIC_LINK_NAME; if c::LookupPrivilegeValueW(null(), name, &mut tp.Privileges[0].Luid) == 0 { return Err(io::Error::last_os_error()); } - tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = c::SE_PRIVILEGE_ENABLED; + tp.PrivilegeCount = 1; + if c::AdjustTokenPrivileges(*handle, c::FALSE, &tp, TOKEN_PRIVILEGES_SIZE, null_mut(), null_mut()) == 0 { return Err(io::Error::last_os_error()); } diff --git a/src/lib.rs b/src/lib.rs index b32ca321..fa518329 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,6 +88,7 @@ pub fn delete>(junction: P) -> io::Result<()> { /// use std::io; /// # use junction::exists; /// fn main() -> io::Result<()> { +/// # #[cfg(feature = "unstable_admin")] /// assert!(exists(r"C:\Users\Default User")?); /// Ok(()) /// } @@ -106,6 +107,7 @@ pub fn exists>(junction: P) -> io::Result { /// use std::io; /// # use junction::get_target; /// fn main() -> io::Result<()> { +/// # #[cfg(feature = "unstable_admin")] /// assert_eq!(get_target(r"C:\Users\Default User")?.to_str(), Some(r"C:\Users\Default")); /// Ok(()) /// } diff --git a/src/tests.rs b/src/tests.rs index 698012ee..e5d59129 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -196,6 +196,7 @@ fn exists_verify() { #[test] fn get_target_user_dirs() { + #[cfg(feature = "unstable_admin")] // junction assert_eq!( super::get_target(r"C:\Users\Default User").unwrap().to_str(), From 21a0b3e5292320bc47ff17fbc557b85d2df6b056 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 19 Jul 2023 00:03:38 +0700 Subject: [PATCH 36/46] msrv: fallback to trick for const asserts --- src/internals/c.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/internals/c.rs b/src/internals/c.rs index 0bf7b2c5..5169efbf 100644 --- a/src/internals/c.rs +++ b/src/internals/c.rs @@ -48,15 +48,17 @@ pub const REPARSE_GUID_DATA_BUFFER_HEADER_SIZE: u16 = 24; pub const MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE: u16 = 8; // Safety checks for correct header size due to the lacks of `offset_of!`. +// MSRV(1.57): for const assert! +#[allow(clippy::no_effect)] // noisy clippy lints const _: () = { let rdb_header_size = size_of::() + size_of::() * 2; - assert!(rdb_header_size == REPARSE_DATA_BUFFER_HEADER_SIZE as _); + [(); 1][!(rdb_header_size == REPARSE_DATA_BUFFER_HEADER_SIZE as _) as usize]; let mprb_header_size = size_of::() * 4; - assert!(mprb_header_size == MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE as _); + [(); 1][!(mprb_header_size == MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE as _) as usize]; let rgdb_header_size = size_of::() + size_of::() * 2 + size_of::(); - assert!(rgdb_header_size == REPARSE_GUID_DATA_BUFFER_HEADER_SIZE as _); + [(); 1][!(rgdb_header_size == REPARSE_GUID_DATA_BUFFER_HEADER_SIZE as _) as usize]; }; type VarLenArr = [T; 1]; From b7aa095bfdbbfba60a3627f0bb1d2d14adb17283 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 19 Jul 2023 00:06:11 +0700 Subject: [PATCH 37/46] ci: also test for no-default-features --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b41d4a1..fee4f67b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,7 @@ jobs: - run: cargo build - run: cargo build --all-targets - run: cargo test + - run: cargo test --no-default-features # NOTE: miri still needs to support more Windows API shims - if: false run: | From aacb607b8f4045fbc2c8c3cc21b4ffafcc912694 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 19 Jul 2023 00:22:13 +0700 Subject: [PATCH 38/46] test: add more system directory junction paths --- src/tests.rs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/tests.rs b/src/tests.rs index e5d59129..debb095b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -196,17 +196,32 @@ fn exists_verify() { #[test] fn get_target_user_dirs() { - #[cfg(feature = "unstable_admin")] - // junction - assert_eq!( - super::get_target(r"C:\Users\Default User").unwrap().to_str(), - Some(r"C:\Users\Default"), - ); - // junction with special permissions - assert_eq!( - super::get_target(r"C:\Documents and Settings\").unwrap().to_str(), - Some(r"C:\Users"), - ); + use std::env; + if cfg!(feature = "unstable_admin") { + // These special system directory junction are from + // + assert_eq!( + super::get_target(r"C:\Users\Default User").unwrap().to_str(), + Some(r"C:\Users\Default"), + ); + assert_eq!( + super::get_target(r"C:\Documents and Settings\").unwrap().to_str(), + Some(r"C:\Users"), + ); + let user_profile = env::var("USERPROFILE").unwrap(); + assert_eq!( + super::get_target(format!("{user}\\Application Data", user = user_profile)) + .unwrap() + .to_str(), + Some(format!("{user}\\AppData\\Roaming", user = user_profile).as_str()), + ); + assert_eq!( + super::get_target(format!("{user}\\My Documents\\My Pictures", user = user_profile)) + .unwrap() + .to_str(), + Some(format!("{user}\\Pictures", user = user_profile).as_str()), + ); + } let tmpdir = create_tempdir(); From 1f80abf2a17aaff668d76223bb5496adf0b7140d Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 19 Jul 2023 22:36:16 +0700 Subject: [PATCH 39/46] Set MSRV in cargo.toml --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a491c434..08935344 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ version = "1.0.0" # Also update `html_root_url` in lib.rs authors = ["Lzu Tao "] categories = ["api-bindings", "os::windows-apis"] documentation = "https://docs.rs/junction/*/x86_64-pc-windows-msvc/junction/" -edition = "2018" +edition = "2018" # edition 2021 released in 1.56.0 +rust-version = "1.51" exclude = [ "/.github", "/HOW-TO-RELEASE.md", From cb077bbc02fee05c1e91022d4f877fee1b9d920b Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 19 Jul 2023 22:37:07 +0700 Subject: [PATCH 40/46] cargo: Move compulsory features of windows-sys to top --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 08935344..8cab377d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,13 +41,14 @@ default-features = false [target.'cfg(windows)'.dependencies.windows-sys] version = "0.48" +default-features = false features = [ + "Win32_System_Ioctl", + "Win32_System_SystemServices", "Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_IO", - "Win32_System_Ioctl", - "Win32_System_SystemServices", "Win32_System_Threading", ] From 5bc26fd709a6a0d62fb3ce3c03a9470db6808f42 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sun, 21 Apr 2024 23:12:12 +0700 Subject: [PATCH 41/46] upload lock file as new guidelines --- .gitignore | 1 - Cargo.lock | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index 2f88dbac..53eaa219 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target **/*.rs.bk -Cargo.lock \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..0f158831 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,222 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "junction" +version = "1.0.0" +dependencies = [ + "scopeguard", + "tempfile", + "windows-sys 0.48.0", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "rustix" +version = "0.38.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" From 146056c7f24efe40b7e094606ccac7d8b981e036 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 30 Apr 2024 19:28:03 +0700 Subject: [PATCH 42/46] add assume_init helper --- src/internals.rs | 10 ++++------ src/internals/cast.rs | 4 ++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index b32a7eef..6fe7dbd0 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -97,12 +97,11 @@ pub fn exists(junction: &Path) -> io::Result { let file = helpers::open_reparse_point(junction, false)?; // Allocate enough space to fit the maximum sized reparse data buffer let mut data = BytesAsReparseDataBuffer::new(); - let rdb = data.as_mut_ptr(); // XXX: Could also use FindFirstFile to read the reparse point type // Ref https://learn.microsoft.com/en-us/windows/win32/fileio/reparse-point-tags - helpers::get_reparse_data_point(file.as_raw_handle() as isize, rdb)?; + helpers::get_reparse_data_point(file.as_raw_handle() as isize, data.as_mut_ptr())?; // SATETY: rdb should be initialized now - let rdb = unsafe { &*rdb }; + let rdb = unsafe { data.assume_init() }; // The reparse tag indicates if this is a junction or not Ok(rdb.ReparseTag == c::IO_REPARSE_TAG_MOUNT_POINT) } @@ -114,10 +113,9 @@ pub fn get_target(junction: &Path) -> io::Result { } let file = helpers::open_reparse_point(junction, false)?; let mut data = BytesAsReparseDataBuffer::new(); - let rdb = data.as_mut_ptr(); - helpers::get_reparse_data_point(file.as_raw_handle() as isize, rdb)?; + helpers::get_reparse_data_point(file.as_raw_handle() as isize, data.as_mut_ptr())?; // SAFETY: rdb should be initialized now - let rdb = unsafe { &*rdb }; + let rdb = unsafe { data.assume_init() }; if rdb.ReparseTag == c::IO_REPARSE_TAG_MOUNT_POINT { let offset = rdb.ReparseBuffer.SubstituteNameOffset / WCHAR_SIZE; let len = rdb.ReparseBuffer.SubstituteNameLength / WCHAR_SIZE; diff --git a/src/internals/cast.rs b/src/internals/cast.rs index cf7d6582..54b6cbd6 100644 --- a/src/internals/cast.rs +++ b/src/internals/cast.rs @@ -33,4 +33,8 @@ impl BytesAsReparseDataBuffer { pub fn as_mut_ptr(&mut self) -> *mut REPARSE_DATA_BUFFER { self.value.as_mut_ptr().cast::() } + + pub unsafe fn assume_init(&mut self) -> &REPARSE_DATA_BUFFER { + &*self.as_mut_ptr() + } } From 3ed1f8630ade94c93fbe163b43261cc53b436b44 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 30 Apr 2024 19:35:37 +0700 Subject: [PATCH 43/46] bummp windows-sys to 0.52 --- Cargo.lock | 102 ++++++++++------------------------------------------- Cargo.toml | 2 +- 2 files changed, 19 insertions(+), 85 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f158831..38f1dc1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,14 +21,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "junction" @@ -36,14 +36,14 @@ version = "1.0.0" dependencies = [ "scopeguard", "tempfile", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "linux-raw-sys" @@ -53,15 +53,15 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "rustix" -version = "0.38.33" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -79,16 +79,7 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-sys", ] [[package]] @@ -97,22 +88,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -121,46 +97,28 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -173,48 +131,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/Cargo.toml b/Cargo.toml index 8cab377d..4eeb03eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ version = "1" default-features = false [target.'cfg(windows)'.dependencies.windows-sys] -version = "0.48" +version = "0.52" default-features = false features = [ "Win32_System_Ioctl", From e19b28658d7a3bf88ded56fb2c02b45ea632b3d2 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sun, 7 Apr 2024 18:57:26 +0700 Subject: [PATCH 44/46] use offset_of in 1.75 --- .github/workflows/ci.yml | 2 +- Cargo.toml | 2 ++ src/internals/c.rs | 22 ++++++++-------------- src/internals/c/nightly.rs | 11 +++++++++++ 4 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 src/internals/c/nightly.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fee4f67b..6af57739 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -134,7 +134,7 @@ jobs: - run: | rustup toolchain install nightly -c clippy rustup default nightly - - run: cargo clippy --all-targets --all-features -- -Dwarnings + - run: cargo clippy --all-targets --all-features -- -Dwarnings -A clippy::assertions-on-constants # Use static analyzer Rudra . diff --git a/Cargo.toml b/Cargo.toml index 4eeb03eb..1de777f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ description = "library for working with NTFS junctions" [features] default = ["unstable_admin"] +# Flag for trying out new rust language features +nightly = [] # # Unstable flag # # Enable the SE_BACKUP_NAME and SE_RESTORE_NAME access privileges. diff --git a/src/internals/c.rs b/src/internals/c.rs index 5169efbf..30deb605 100644 --- a/src/internals/c.rs +++ b/src/internals/c.rs @@ -1,11 +1,13 @@ #![allow(non_snake_case)] +// MSRV(1.75): use `offset_of!` when stabilized. +#[cfg(feature = "nightly")] +mod nightly; + use std::alloc::Layout; -use std::mem::size_of; use std::os::raw::{c_ulong, c_ushort}; use std::os::windows::io::RawHandle; -use windows_sys::core::GUID; pub use windows_sys::Win32::Foundation::{ CloseHandle, GetLastError, SetLastError, ERROR_INSUFFICIENT_BUFFER, FALSE, GENERIC_READ, GENERIC_WRITE, HANDLE, INVALID_HANDLE_VALUE, @@ -39,7 +41,6 @@ const _: () = { }; // NOTE: to use `size_of` operator, below structs should be packed. -// TODO: use `offset_of!` when stabilized. /// Reparse Data Buffer header size pub const REPARSE_DATA_BUFFER_HEADER_SIZE: u16 = 8; /// Reparse GUID Data Buffer header size @@ -47,18 +48,11 @@ pub const REPARSE_GUID_DATA_BUFFER_HEADER_SIZE: u16 = 24; /// MountPointReparseBuffer header size pub const MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE: u16 = 8; -// Safety checks for correct header size due to the lacks of `offset_of!`. -// MSRV(1.57): for const assert! -#[allow(clippy::no_effect)] // noisy clippy lints +#[cfg(feature = "nightly")] const _: () = { - let rdb_header_size = size_of::() + size_of::() * 2; - [(); 1][!(rdb_header_size == REPARSE_DATA_BUFFER_HEADER_SIZE as _) as usize]; - - let mprb_header_size = size_of::() * 4; - [(); 1][!(mprb_header_size == MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE as _) as usize]; - - let rgdb_header_size = size_of::() + size_of::() * 2 + size_of::(); - [(); 1][!(rgdb_header_size == REPARSE_GUID_DATA_BUFFER_HEADER_SIZE as _) as usize]; + assert!(REPARSE_DATA_BUFFER_HEADER_SIZE == nightly::REPARSE_DATA_BUFFER_HEADER_SIZE); + assert!(REPARSE_GUID_DATA_BUFFER_HEADER_SIZE == nightly::REPARSE_GUID_DATA_BUFFER_HEADER_SIZE); + assert!(MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE == nightly::MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE); }; type VarLenArr = [T; 1]; diff --git a/src/internals/c/nightly.rs b/src/internals/c/nightly.rs new file mode 100644 index 00000000..90591e5a --- /dev/null +++ b/src/internals/c/nightly.rs @@ -0,0 +1,11 @@ +#![allow(unused)] + +use std::mem::offset_of; + +/// Reparse Data Buffer header size +pub const REPARSE_DATA_BUFFER_HEADER_SIZE: u16 = offset_of!(super::REPARSE_DATA_BUFFER, ReparseBuffer) as u16; +/// Reparse GUID Data Buffer header size +pub const REPARSE_GUID_DATA_BUFFER_HEADER_SIZE: u16 = + offset_of!(super::REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer) as u16; +/// MountPointReparseBuffer header size +pub const MOUNT_POINT_REPARSE_BUFFER_HEADER_SIZE: u16 = offset_of!(super::MountPointReparseBuffer, PathBuffer) as u16; From 504356b6ec889728de52f111950d55ae71997527 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 30 Apr 2024 21:18:34 +0700 Subject: [PATCH 45/46] ci: build all branches bump msrv to 1.56 as windows-sys required --- .github/workflows/ci.yml | 9 ++- CHANGELOG.md | 6 ++ Cargo.lock | 117 +++++++++++++++++++++++++++++++++------ Cargo.toml | 10 +++- HOW-TO-RELEASE.md | 6 +- README.md | 2 +- 6 files changed, 120 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6af57739..5d8f88b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: Rust on: push: branches: - - 'master' + - '*' - '!gh-pages' pull_request: @@ -40,6 +40,7 @@ jobs: shell: bash if: matrix.mingw_dir - run: cargo build + - run: cargo build --features nightly - run: cargo build --all-targets - run: cargo test - run: cargo test --no-default-features @@ -68,7 +69,7 @@ jobs: env: CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse # add_of_mut! requires for soundness - MSRV: 1.51.0 + MSRV: 1.56.0 steps: - uses: actions/checkout@v3 - run: | @@ -78,9 +79,7 @@ jobs: rustup run stable cargo generate-lockfile rustup run stable cargo fetch - run: | - cargo build --all-targets --locked - - run: | - cargo test + cargo build --locked rustfmt: runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index f6aa7fda..7f72b162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `Fixed` for any bug fixes. * `Security` in case of vulnerabilities. --> +## [v1.1.0] - 2024-04-30 +### Change MSRV from 1.51 to 1.56 + +windows-sys bump to 1.56 when they switch to 2021 edition. +As a dependent of that crate, we have no choice but to follow. ## [v1.0.0] - 2023-02-26 ### First major version @@ -57,6 +62,7 @@ It signals that the API is mature enough to be stable for a long time. First release +[v1.0.0]: https://github.com/lzutao/junction/compare/v1.0.0...v1.1.0 [v1.0.0]: https://github.com/lzutao/junction/compare/v0.2.1...v1.0.0 [v0.2.1]: https://github.com/lzutao/junction/compare/v0.2.0...v0.2.1 [v0.2.0]: https://github.com/lzutao/junction/compare/v0.1.0...v0.2.0 diff --git a/Cargo.lock b/Cargo.lock index 38f1dc1e..90417533 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,10 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" @@ -21,7 +25,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -34,9 +38,10 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" name = "junction" version = "1.0.0" dependencies = [ + "rustix", "scopeguard", "tempfile", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -51,17 +56,26 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "9bfe0f2582b4931a45d1fa608f8a8722e8b3c7ac54dd6d5f3b3212791fedef49" dependencies = [ - "bitflags", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -72,14 +86,24 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", + "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -88,7 +112,22 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -97,28 +136,46 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -131,24 +188,48 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/Cargo.toml b/Cargo.toml index 1de777f6..fd6a592c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "junction" -version = "1.0.0" # Also update `html_root_url` in lib.rs +version = "1.1.0" # Also update `html_root_url` in lib.rs authors = ["Lzu Tao "] categories = ["api-bindings", "os::windows-apis"] documentation = "https://docs.rs/junction/*/x86_64-pc-windows-msvc/junction/" edition = "2018" # edition 2021 released in 1.56.0 -rust-version = "1.51" +rust-version = "1.56" exclude = [ "/.github", "/HOW-TO-RELEASE.md", @@ -55,4 +55,8 @@ features = [ ] [dev-dependencies] -tempfile = "3" +tempfile = "=3.8.0" +# NOTE: rustix is used by tempfile, to force tempfile use an old version +# of it that is compatible with MSRV. +# MSRV: v0.38.9+ has newer dep:core syntax that old rustc (<1.60) confuses. +rustix = "=0.38.9" diff --git a/HOW-TO-RELEASE.md b/HOW-TO-RELEASE.md index b3947ac5..eee2a9c6 100644 --- a/HOW-TO-RELEASE.md +++ b/HOW-TO-RELEASE.md @@ -17,8 +17,8 @@ 7. Create tag and publish to remote ```bash - VER_NUM=v0.x.x - git tag -as ${VER_NUM} -m ${VER_NUM} + VER_NUM=v1.x.x + git tag -as $VER_NUM -m $VER_NUM git push origin master - git push origin ${VER_NUM} + git push origin $VER_NUM ``` diff --git a/README.md b/README.md index 29370dc4..411e8aee 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Quoted from [Computer Hope](https://www.computerhope.com/jargon/j/junction.htm): ### Minimal Supported Rust versions -1.51.0 +1.56.0 ## All relevant references From 2452651547b5272ce260530dee6d32db89db1a9b Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 30 Apr 2024 21:25:03 +0700 Subject: [PATCH 46/46] publish --- Cargo.lock | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 90417533..c29dd169 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "bitflags" version = "1.3.2" @@ -36,7 +38,7 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "junction" -version = "1.0.0" +version = "1.1.0" dependencies = [ "rustix", "scopeguard",