Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions cap-primitives/src/windows/fs/create_file_at_w.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,12 @@ const FILE_ATTRIBUTE_VALID_FLAGS: FILE_FLAGS_AND_ATTRIBUTES = FILE_ATTRIBUTE_EA
/// Like Windows' `CreateFileW`, but takes a `dir` argument to use as the
/// root directory.
///
/// Also, the `lpfilename` is a Rust slice instead of a C-style NUL-terminated
/// array, because that's what our callers have and it's closer to what
/// `NtCreatePath` takes.
/// Also, the `lpfilename` is a Rust slice ending in a NUL terminator. The
/// terminator is not part of the name passed to `NtCreateFile`, but it is
/// kept in memory right after it, as `RtlInitUnicodeString` does: filter
/// drivers and user-mode hooks that read `ObjectName->Buffer` up to a NUL
/// instead of `Length` bytes would otherwise read past the end of the
/// allocation.
#[allow(non_snake_case)]
pub unsafe fn CreateFileAtW(
dir: HANDLE,
Expand Down Expand Up @@ -118,19 +121,24 @@ pub unsafe fn CreateFileAtW(
}
};

// Convert `lpfilename` to a `UNICODE_STRING`.
// Convert `lpfilename` to a `UNICODE_STRING`, keeping the NUL terminator
// out of `Length` but inside `MaximumLength`.
let Some((&0, name)) = lpfilename.split_last() else {
SetLastError(ERROR_INVALID_PARAMETER);
return HandleOrInvalid::from_raw_handle(INVALID_HANDLE_VALUE as _);
};
let byte_length = lpfilename.len() * mem::size_of::<u16>();
let length: u16 = match byte_length.try_into() {
let maximum_length: u16 = match byte_length.try_into() {
Ok(length) => length,
Err(_) => {
SetLastError(ERROR_INVALID_NAME);
return HandleOrInvalid::from_raw_handle(INVALID_HANDLE_VALUE as _);
}
};
let mut unicode_string = UNICODE_STRING {
Buffer: lpfilename.as_ptr() as *mut u16,
Length: length,
MaximumLength: length,
Buffer: name.as_ptr() as *mut u16,
Length: maximum_length - mem::size_of::<u16>() as u16,
MaximumLength: maximum_length,
};

let mut handle = INVALID_HANDLE_VALUE;
Expand Down
13 changes: 5 additions & 8 deletions cap-primitives/src/windows/fs/open_unchecked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,16 @@ fn open_at(start: &fs::File, path: &Path, opts: &OpenOptions) -> io::Result<fs::
}
}

let mut wide = OsStr::encode_wide(rebuilt.as_os_str()).collect::<Vec<u16>>();
// Both `CreateFileW` and our own `CreateFileAtW` take a NUL-terminated
// filename.
let wide = OsStr::encode_wide(rebuilt.as_os_str())
.chain(Some(0))
.collect::<Vec<u16>>();

// If we ended up re-rooting, use Windows' `CreateFileW` instead of our
// own `CreateFileAtW` so that it does the requisite magic for absolute
// paths.
if dir == 0 as HANDLE {
// We're calling the windows-sys `CreateFileW` which expects a
// NUL-terminated filename, so add a NUL terminator.
wide.push(0);

let handle = unsafe {
CreateFileW(
wide.as_ptr(),
Expand All @@ -115,9 +115,6 @@ fn open_at(start: &fs::File, path: &Path, opts: &OpenOptions) -> io::Result<fs::
Err(io::Error::last_os_error())
}
} else {
// Our own `CreateFileAtW` is similar to `CreateFileW` except it
// takes the filename as a Rust slice directly, so we can skip
// the NUL terminator.
let handle = unsafe {
CreateFileAtW(
dir,
Expand Down