From 245e84d1a5590294acbf97269c651ca81980d1d6 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 10 Feb 2023 12:46:25 -0800 Subject: [PATCH 01/15] Use `NtCreateFile` to implement `open_unchecked` on Windows. (#293) Windows' `NtCreateFile` has an ability to take a directory and a relative path, so use that to implement `open_unchecked` instead of using path concatenation. We still use concatenation for other functions, but this is the first step to rewriting those to avoid it. Fixes #226. --- cap-primitives/Cargo.toml | 5 +- .../src/fs/manually/canonicalize.rs | 31 +- cap-primitives/src/fs/maybe_owned_file.rs | 1 + .../src/windows/fs/create_file_at_w.rs | 270 ++++++++++++++++++ cap-primitives/src/windows/fs/dir_utils.rs | 2 +- cap-primitives/src/windows/fs/mod.rs | 2 +- cap-primitives/src/windows/fs/oflags.rs | 26 +- .../src/windows/fs/open_options_ext.rs | 75 ++++- .../src/windows/fs/open_unchecked.rs | 141 ++++++++- cap-primitives/src/windows/fs/reopen_impl.rs | 39 +-- tests/fs_additional.rs | 24 +- tests/paths-containing-nul.rs | 19 +- tests/reopendir.rs | 66 +++++ 13 files changed, 631 insertions(+), 70 deletions(-) create mode 100644 cap-primitives/src/windows/fs/create_file_at_w.rs create mode 100644 tests/reopendir.rs diff --git a/cap-primitives/Cargo.toml b/cap-primitives/Cargo.toml index 5c24177b6..dcf69af99 100644 --- a/cap-primitives/Cargo.toml +++ b/cap-primitives/Cargo.toml @@ -33,7 +33,10 @@ winx = "0.35.0" [target.'cfg(windows)'.dependencies.windows-sys] version = "0.45.0" features = [ - "Win32_Storage_FileSystem", "Win32_Foundation", + "Win32_Security", + "Win32_Storage_FileSystem", + "Win32_System_Kernel", "Win32_System_SystemServices", + "Win32_System_WindowsProgramming", ] diff --git a/cap-primitives/src/fs/manually/canonicalize.rs b/cap-primitives/src/fs/manually/canonicalize.rs index a1e395ecf..e0d8b552f 100644 --- a/cap-primitives/src/fs/manually/canonicalize.rs +++ b/cap-primitives/src/fs/manually/canonicalize.rs @@ -24,15 +24,40 @@ pub(crate) fn canonicalize_with( let mut canonical_path = PathBuf::new(); let start = MaybeOwnedFile::borrowed(start); - if let Err(e) = internal_open( + match internal_open( start, path, canonicalize_options().follow(follow), &mut symlink_count, Some(&mut canonical_path), ) { - if canonical_path.as_os_str().is_empty() { - return Err(e); + // If the open succeeded, we got our path. + Ok(_) => (), + + // If it failed due to an invalid argument or filename, report it. + Err(err) if err.kind() == io::ErrorKind::InvalidInput => { + return Err(err); + } + #[cfg(io_error_more)] + Err(err) if err.kind() == io::ErrorKind::InvalidFilename => { + return Err(err); + } + #[cfg(windows)] + Err(err) + if err.raw_os_error() + == Some(windows_sys::Win32::Foundation::ERROR_INVALID_NAME as _) + || err.raw_os_error() + == Some(windows_sys::Win32::Foundation::ERROR_DIRECTORY as _) => + { + return Err(err); + } + + // For any other error, like permission denied, it's ok as long as + // we got our path. + Err(err) => { + if canonical_path.as_os_str().is_empty() { + return Err(err); + } } } diff --git a/cap-primitives/src/fs/maybe_owned_file.rs b/cap-primitives/src/fs/maybe_owned_file.rs index aa525b1df..2e1a888c0 100644 --- a/cap-primitives/src/fs/maybe_owned_file.rs +++ b/cap-primitives/src/fs/maybe_owned_file.rs @@ -106,6 +106,7 @@ impl<'borrow> MaybeOwnedFile<'borrow> { /// Produce an owned `File`. This uses `open` on "." if needed to convert a /// borrowed `File` to an owned one. + #[cfg_attr(windows, allow(dead_code))] pub(super) fn into_file(self, options: &OpenOptions) -> io::Result { match self.inner { MaybeOwned::Owned(file) => Ok(file), diff --git a/cap-primitives/src/windows/fs/create_file_at_w.rs b/cap-primitives/src/windows/fs/create_file_at_w.rs new file mode 100644 index 000000000..4d1804c97 --- /dev/null +++ b/cap-primitives/src/windows/fs/create_file_at_w.rs @@ -0,0 +1,270 @@ +#![allow(unsafe_code)] + +use std::convert::TryInto; +use std::mem; +use std::os::windows::io::HandleOrInvalid; +use std::ptr::null_mut; +use windows_sys::Win32::Foundation::{ + RtlNtStatusToDosError, SetLastError, ERROR_ALREADY_EXISTS, ERROR_FILE_EXISTS, + ERROR_INVALID_NAME, ERROR_INVALID_PARAMETER, ERROR_NOT_SUPPORTED, HANDLE, INVALID_HANDLE_VALUE, + STATUS_OBJECT_NAME_COLLISION, STATUS_PENDING, STATUS_SUCCESS, SUCCESS, UNICODE_STRING, +}; +use windows_sys::Win32::Security::{ + SECURITY_ATTRIBUTES, SECURITY_DYNAMIC_TRACKING, SECURITY_QUALITY_OF_SERVICE, + SECURITY_STATIC_TRACKING, +}; +use windows_sys::Win32::Storage::FileSystem::{ + NtCreateFile, CREATE_ALWAYS, CREATE_NEW, DELETE, FILE_ACCESS_FLAGS, FILE_ATTRIBUTE_ARCHIVE, + FILE_ATTRIBUTE_COMPRESSED, FILE_ATTRIBUTE_DEVICE, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_EA, + FILE_ATTRIBUTE_ENCRYPTED, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_INTEGRITY_STREAM, + FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, FILE_ATTRIBUTE_NO_SCRUB_DATA, + FILE_ATTRIBUTE_OFFLINE, FILE_ATTRIBUTE_PINNED, FILE_ATTRIBUTE_READONLY, + FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS, FILE_ATTRIBUTE_RECALL_ON_OPEN, + FILE_ATTRIBUTE_REPARSE_POINT, FILE_ATTRIBUTE_SPARSE_FILE, FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_TEMPORARY, FILE_ATTRIBUTE_UNPINNED, FILE_ATTRIBUTE_VIRTUAL, FILE_CREATE, + FILE_CREATION_DISPOSITION, FILE_FLAGS_AND_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS, + FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_NO_BUFFERING, FILE_FLAG_OPEN_NO_RECALL, + FILE_FLAG_OPEN_REPARSE_POINT, FILE_FLAG_OVERLAPPED, FILE_FLAG_POSIX_SEMANTICS, + FILE_FLAG_RANDOM_ACCESS, FILE_FLAG_SEQUENTIAL_SCAN, FILE_FLAG_SESSION_AWARE, + FILE_FLAG_WRITE_THROUGH, FILE_OPEN, FILE_OPEN_IF, FILE_OVERWRITE, FILE_OVERWRITE_IF, + FILE_READ_ATTRIBUTES, FILE_SHARE_MODE, OPEN_ALWAYS, OPEN_EXISTING, SECURITY_CONTEXT_TRACKING, + SECURITY_EFFECTIVE_ONLY, SECURITY_SQOS_PRESENT, SYNCHRONIZE, TRUNCATE_EXISTING, +}; +use windows_sys::Win32::System::Kernel::{OBJ_CASE_INSENSITIVE, OBJ_INHERIT}; +use windows_sys::Win32::System::SystemServices::{GENERIC_ALL, GENERIC_READ, GENERIC_WRITE}; +use windows_sys::Win32::System::WindowsProgramming::{ + FILE_DELETE_ON_CLOSE, FILE_NON_DIRECTORY_FILE, FILE_NO_INTERMEDIATE_BUFFERING, FILE_OPENED, + FILE_OPEN_FOR_BACKUP_INTENT, FILE_OPEN_NO_RECALL, FILE_OPEN_REMOTE_INSTANCE, + FILE_OPEN_REPARSE_POINT, FILE_OVERWRITTEN, FILE_RANDOM_ACCESS, FILE_SEQUENTIAL_ONLY, + FILE_SYNCHRONOUS_IO_NONALERT, FILE_WRITE_THROUGH, IO_STATUS_BLOCK, OBJECT_ATTRIBUTES, +}; + +// All currently known `FILE_ATTRIBUTE_*` constants, according to +// windows-sys' documentation. +const FILE_ATTRIBUTE_VALID_FLAGS: FILE_FLAGS_AND_ATTRIBUTES = FILE_ATTRIBUTE_EA + | FILE_ATTRIBUTE_DEVICE + | FILE_ATTRIBUTE_HIDDEN + | FILE_ATTRIBUTE_NORMAL + | FILE_ATTRIBUTE_PINNED + | FILE_ATTRIBUTE_SYSTEM + | FILE_ATTRIBUTE_ARCHIVE + | FILE_ATTRIBUTE_OFFLINE + | FILE_ATTRIBUTE_VIRTUAL + | FILE_ATTRIBUTE_READONLY + | FILE_ATTRIBUTE_UNPINNED + | FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_ENCRYPTED + | FILE_ATTRIBUTE_TEMPORARY + | FILE_ATTRIBUTE_COMPRESSED + | FILE_ATTRIBUTE_SPARSE_FILE + | FILE_ATTRIBUTE_NO_SCRUB_DATA + | FILE_ATTRIBUTE_REPARSE_POINT + | FILE_ATTRIBUTE_RECALL_ON_OPEN + | FILE_ATTRIBUTE_INTEGRITY_STREAM + | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED + | FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS; + +/// Like Windows' `CreateFileW`, but takes a `dir` argument to use as the +/// root directory. +#[allow(non_snake_case)] +pub unsafe fn CreateFileAtW( + dir: HANDLE, + lpfilename: &[u16], + dwdesiredaccess: FILE_ACCESS_FLAGS, + dwsharemode: FILE_SHARE_MODE, + lpsecurityattributes: *const SECURITY_ATTRIBUTES, + dwcreationdisposition: FILE_CREATION_DISPOSITION, + dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, + htemplatefile: HANDLE, +) -> HandleOrInvalid { + // Absolute paths are not yet implemented here. + // + // It seems like `NtCreatePath` needs the apparently NT-internal `\??\` + // prefix prepended to absolute paths. It's possible it needs other + // path transforms as well. `RtlDosPathNameToNtPathName_U` may be a + // function that does these things, though it's not available in + // windows-sys and not documented, though one can find + // [unofficial blog posts], though even they say things like "I`m + // sorry that I cannot give more details on these functions". + // + // [unofficial blog posts]: https://mecanik.dev/en/posts/convert-dos-and-nt-paths-using-rtl-functions/ + assert!(dir != 0); + + // Extended attributes are not implemented yet. + if htemplatefile != 0 { + SetLastError(ERROR_NOT_SUPPORTED); + return HandleOrInvalid::from_raw_handle(INVALID_HANDLE_VALUE as _); + } + + // Convert `dwcreationdisposition` to the `createdisposition` argument + // to `NtCreateFile`. Do this before converting `lpfilename` so that + // we can return early on failure. + let createdisposition = match dwcreationdisposition { + CREATE_NEW => FILE_CREATE, + CREATE_ALWAYS => FILE_OVERWRITE_IF, + OPEN_EXISTING => FILE_OPEN, + OPEN_ALWAYS => FILE_OPEN_IF, + TRUNCATE_EXISTING => FILE_OVERWRITE, + _ => { + SetLastError(ERROR_INVALID_PARAMETER); + return HandleOrInvalid::from_raw_handle(INVALID_HANDLE_VALUE as _); + } + }; + + // Convert `lpfilename` to a `UNICODE_STRING`. + let byte_length = lpfilename.len() * mem::size_of::(); + let 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, + }; + + let mut handle = INVALID_HANDLE_VALUE; + + // Convert `dwdesiredaccess` and `dwflagsandattributes` to the + // `desiredaccess` argument to `NtCreateFile`. + let mut desiredaccess = dwdesiredaccess | SYNCHRONIZE | FILE_READ_ATTRIBUTES; + if dwflagsandattributes & FILE_FLAG_DELETE_ON_CLOSE != 0 { + desiredaccess |= DELETE; + } + + // Compute `objectattributes`' `Attributes` field. Case-insensitive is + // the expected behavior on Windows. + let mut attributes = 0; + if dwflagsandattributes & FILE_FLAG_POSIX_SEMANTICS != 0 { + attributes |= OBJ_CASE_INSENSITIVE as u32; + }; + if !lpsecurityattributes.is_null() && (*lpsecurityattributes).bInheritHandle != 0 { + attributes |= OBJ_INHERIT as u32; + } + + // Compute the `objectattributes` argument to `NtCreateFile`. + let mut objectattributes = mem::zeroed::(); + objectattributes.Length = mem::size_of::() as _; + objectattributes.RootDirectory = dir; + objectattributes.ObjectName = &mut unicode_string; + objectattributes.Attributes = attributes; + if !lpsecurityattributes.is_null() { + objectattributes.SecurityDescriptor = (*lpsecurityattributes).lpSecurityDescriptor; + } + + // If needed, set `objectattributes`' `SecurityQualityOfService` field. + let mut qos; + if dwflagsandattributes & SECURITY_SQOS_PRESENT != 0 { + qos = mem::zeroed::(); + qos.Length = mem::size_of::() as _; + qos.ImpersonationLevel = ((dwflagsandattributes >> 16) & 0x3) as _; + qos.ContextTrackingMode = if dwflagsandattributes & SECURITY_CONTEXT_TRACKING != 0 { + SECURITY_DYNAMIC_TRACKING + } else { + SECURITY_STATIC_TRACKING + }; + qos.EffectiveOnly = ((dwflagsandattributes & SECURITY_EFFECTIVE_ONLY) != 0) as _; + + objectattributes.SecurityQualityOfService = + (&mut qos as *mut SECURITY_QUALITY_OF_SERVICE).cast(); + } + + let mut iostatusblock = mem::zeroed::(); + iostatusblock.Anonymous.Status = STATUS_PENDING; + + // Compute the `fileattributes` argument to `NtCreateFile`. Mask off + // unrecognized flags. + let mut fileattributes = dwflagsandattributes & FILE_ATTRIBUTE_VALID_FLAGS; + if fileattributes == 0 { + fileattributes = FILE_ATTRIBUTE_NORMAL; + } + + // Compute the `createoptions` argument to `NtCreateFile`. + let mut createoptions = 0; + if dwflagsandattributes & FILE_FLAG_BACKUP_SEMANTICS == 0 { + createoptions |= FILE_NON_DIRECTORY_FILE; + } else { + if dwdesiredaccess & GENERIC_ALL != 0 { + createoptions |= FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REMOTE_INSTANCE; + } else { + if dwdesiredaccess & GENERIC_READ != 0 { + createoptions |= FILE_OPEN_FOR_BACKUP_INTENT; + } + if dwdesiredaccess & GENERIC_WRITE != 0 { + createoptions |= FILE_OPEN_REMOTE_INSTANCE; + } + } + } + if dwflagsandattributes & FILE_FLAG_DELETE_ON_CLOSE != 0 { + createoptions |= FILE_DELETE_ON_CLOSE; + } + if dwflagsandattributes & FILE_FLAG_NO_BUFFERING != 0 { + createoptions |= FILE_NO_INTERMEDIATE_BUFFERING; + } + if dwflagsandattributes & FILE_FLAG_OPEN_NO_RECALL != 0 { + createoptions |= FILE_OPEN_NO_RECALL; + } + if dwflagsandattributes & FILE_FLAG_OPEN_REPARSE_POINT != 0 { + createoptions |= FILE_OPEN_REPARSE_POINT; + } + if dwflagsandattributes & FILE_FLAG_OVERLAPPED == 0 { + createoptions |= FILE_SYNCHRONOUS_IO_NONALERT; + } + // FILE_FLAG_POSIX_SEMANTICS is handled above. + if dwflagsandattributes & FILE_FLAG_RANDOM_ACCESS != 0 { + createoptions |= FILE_RANDOM_ACCESS; + } + if dwflagsandattributes & FILE_FLAG_SESSION_AWARE != 0 { + // TODO: How should we handle FILE_FLAG_SESSION_AWARE? + SetLastError(ERROR_NOT_SUPPORTED); + return HandleOrInvalid::from_raw_handle(INVALID_HANDLE_VALUE as _); + } + if dwflagsandattributes & FILE_FLAG_SEQUENTIAL_SCAN != 0 { + createoptions |= FILE_SEQUENTIAL_ONLY; + } + if dwflagsandattributes & FILE_FLAG_WRITE_THROUGH != 0 { + createoptions |= FILE_WRITE_THROUGH; + } + + // Ok, we have what we need to call `NtCreateFile` now! + let status = NtCreateFile( + &mut handle, + desiredaccess, + &mut objectattributes, + &mut iostatusblock, + null_mut(), + fileattributes, + dwsharemode, + createdisposition, + createoptions, + null_mut(), + 0, + ); + + // Check for errors. + if status != STATUS_SUCCESS { + handle = INVALID_HANDLE_VALUE; + if status == STATUS_OBJECT_NAME_COLLISION { + SetLastError(ERROR_FILE_EXISTS); + } else { + SetLastError(RtlNtStatusToDosError(status)); + } + } else if (dwcreationdisposition == CREATE_ALWAYS + && iostatusblock.Information == FILE_OVERWRITTEN as _) + || (dwcreationdisposition == OPEN_ALWAYS && iostatusblock.Information == FILE_OPENED as _) + { + // Set `ERROR_ALREADY_EXISTS` according to the table for + // `dwCreationDisposition` in the [`CreateFileW` docs]. + // + // [`CreateFileW` docs]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew + SetLastError(ERROR_ALREADY_EXISTS); + } else { + // Otherwise indicate that we succeeded. + SetLastError(SUCCESS); + } + + HandleOrInvalid::from_raw_handle(handle as _) +} diff --git a/cap-primitives/src/windows/fs/dir_utils.rs b/cap-primitives/src/windows/fs/dir_utils.rs index 7931bd4a8..39de7e5bd 100644 --- a/cap-primitives/src/windows/fs/dir_utils.rs +++ b/cap-primitives/src/windows/fs/dir_utils.rs @@ -100,7 +100,7 @@ pub(crate) fn open_ambient_dir_impl(path: &Path, _: AmbientAuthority) -> io::Res .read(true) .custom_flags(FILE_FLAG_BACKUP_SEMANTICS) .share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE) - .open(&path)?; + .open(path)?; // Require a directory. It may seem possible to eliminate this `metadata()` // call by appending a slash to the path before opening it so that the OS diff --git a/cap-primitives/src/windows/fs/mod.rs b/cap-primitives/src/windows/fs/mod.rs index 3cb4382e8..95c5b488a 100644 --- a/cap-primitives/src/windows/fs/mod.rs +++ b/cap-primitives/src/windows/fs/mod.rs @@ -1,5 +1,6 @@ mod copy; mod create_dir_unchecked; +mod create_file_at_w; mod dir_entry_inner; mod dir_options_ext; mod dir_utils; @@ -74,7 +75,6 @@ pub(crate) use symlink_unchecked::*; // pub(crate) const MAX_SYMLINK_EXPANSIONS: u8 = 63; -#[cfg(any(test, racy_asserts))] pub(crate) fn file_path(file: &std::fs::File) -> Option { get_path::get_path(file).ok() } diff --git a/cap-primitives/src/windows/fs/oflags.rs b/cap-primitives/src/windows/fs/oflags.rs index f4a1adec9..ea4e68899 100644 --- a/cap-primitives/src/windows/fs/oflags.rs +++ b/cap-primitives/src/windows/fs/oflags.rs @@ -6,10 +6,11 @@ use windows_sys::Win32::Storage::FileSystem::{ FILE_SHARE_DELETE, }; -/// Translate the given `cap_std` into `std` options. Also return a bool +/// Adjust an `OpenOptions` after all the flags are set, in preparation +/// for the to call a Windows API `open` function. Also return a bool /// indicating that the `trunc` flag was requested but could not be set, /// so the file should be truncated manually after opening. -pub(in super::super) fn open_options_to_std(opts: &OpenOptions) -> (fs::OpenOptions, bool) { +pub(in super::super) fn prepare_open_options_for_open(opts: &mut OpenOptions) -> bool { let mut trunc = opts.truncate; let mut manually_trunc = false; @@ -40,16 +41,31 @@ pub(in super::super) fn open_options_to_std(opts: &OpenOptions) -> (fs::OpenOpti if opts.sync || opts.dsync { custom_flags |= FILE_FLAG_WRITE_THROUGH; } + + opts.truncate(trunc) + .share_mode(share_mode) + .custom_flags(custom_flags); + + manually_trunc +} + +/// Translate the given `cap_std` into `std` options. Also return a bool +/// indicating that the `trunc` flag was requested but could not be set, +/// so the file should be truncated manually after opening. +pub(in super::super) fn open_options_to_std(opts: &OpenOptions) -> (fs::OpenOptions, bool) { + let mut opts = opts.clone(); + let manually_trunc = prepare_open_options_for_open(&mut opts); + let mut std_opts = fs::OpenOptions::new(); std_opts .read(opts.read) .write(opts.write) .append(opts.append) - .truncate(trunc) + .truncate(opts.truncate) .create(opts.create) .create_new(opts.create_new) - .share_mode(share_mode) - .custom_flags(custom_flags) + .share_mode(opts.ext.share_mode) + .custom_flags(opts.ext.custom_flags) .attributes(opts.ext.attributes); // Calling `sequence_qos_flags` with a value of 0 has the side effect diff --git a/cap-primitives/src/windows/fs/open_options_ext.rs b/cap-primitives/src/windows/fs/open_options_ext.rs index 798c88be9..599b51a0d 100644 --- a/cap-primitives/src/windows/fs/open_options_ext.rs +++ b/cap-primitives/src/windows/fs/open_options_ext.rs @@ -1,6 +1,16 @@ +#![allow(unsafe_code)] + +use crate::fs::OpenOptions; +use std::io; +use std::ptr::null_mut; +use windows_sys::Win32::Foundation::ERROR_INVALID_PARAMETER; +use windows_sys::Win32::Security::SECURITY_ATTRIBUTES; use windows_sys::Win32::Storage::FileSystem::{ - FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, SECURITY_SQOS_PRESENT, + CREATE_ALWAYS, CREATE_NEW, FILE_FLAG_OPEN_REPARSE_POINT, FILE_GENERIC_WRITE, FILE_SHARE_DELETE, + FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_WRITE_DATA, OPEN_ALWAYS, OPEN_EXISTING, + SECURITY_SQOS_PRESENT, TRUNCATE_EXISTING, }; +use windows_sys::Win32::System::SystemServices::{GENERIC_READ, GENERIC_WRITE}; #[derive(Debug, Clone)] pub(crate) struct OpenOptionsExt { @@ -8,9 +18,13 @@ pub(crate) struct OpenOptionsExt { pub(super) share_mode: u32, pub(super) custom_flags: u32, pub(super) attributes: u32, + pub(super) security_attributes: *mut SECURITY_ATTRIBUTES, pub(super) security_qos_flags: u32, } +unsafe impl Send for OpenOptionsExt {} +unsafe impl Sync for OpenOptionsExt {} + impl OpenOptionsExt { pub(crate) const fn new() -> Self { Self { @@ -18,6 +32,7 @@ impl OpenOptionsExt { share_mode: FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, custom_flags: 0, attributes: 0, + security_attributes: null_mut(), security_qos_flags: 0, } } @@ -47,3 +62,61 @@ impl OpenOptionsExt { self } } + +pub(crate) fn get_access_mode(options: &OpenOptions) -> io::Result { + match ( + options.read, + options.write, + options.append, + options.ext.access_mode, + ) { + (.., Some(mode)) => Ok(mode), + (true, false, false, None) => Ok(GENERIC_READ), + (false, true, false, None) => Ok(GENERIC_WRITE), + (true, true, false, None) => Ok(GENERIC_READ | GENERIC_WRITE), + (false, _, true, None) => Ok(FILE_GENERIC_WRITE & !FILE_WRITE_DATA), + (true, _, true, None) => Ok(GENERIC_READ | (FILE_GENERIC_WRITE & !FILE_WRITE_DATA)), + (false, false, false, None) => { + Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER as i32)) + } + } +} + +pub(crate) fn get_flags_and_attributes(options: &OpenOptions) -> u32 { + options.ext.custom_flags + | options.ext.attributes + | options.ext.security_qos_flags + | if options.create_new { + FILE_FLAG_OPEN_REPARSE_POINT + } else { + 0 + } +} + +pub(crate) fn get_creation_mode(options: &OpenOptions) -> io::Result { + const ERROR_INVALID_PARAMETER: i32 = 87; + + match (options.write, options.append) { + (true, false) => {} + (false, false) => { + if options.truncate || options.create || options.create_new { + return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); + } + } + (_, true) => { + if options.truncate && !options.create_new { + return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); + } + } + } + + Ok( + match (options.create, options.truncate, options.create_new) { + (false, false, false) => OPEN_EXISTING, + (true, false, false) => OPEN_ALWAYS, + (false, true, false) => TRUNCATE_EXISTING, + (true, true, false) => CREATE_ALWAYS, + (_, _, true) => CREATE_NEW, + }, + ) +} diff --git a/cap-primitives/src/windows/fs/open_unchecked.rs b/cap-primitives/src/windows/fs/open_unchecked.rs index 2aa3ab260..1f57f8ed7 100644 --- a/cap-primitives/src/windows/fs/open_unchecked.rs +++ b/cap-primitives/src/windows/fs/open_unchecked.rs @@ -1,13 +1,24 @@ -use super::get_path::concatenate; -use super::open_options_to_std; -use crate::fs::{errors, FollowSymlinks, OpenOptions, OpenUncheckedError, SymlinkKind}; +//! Windows implementation of `openat` functionality. + +#![allow(unsafe_code)] + +use super::create_file_at_w::CreateFileAtW; +use super::{open_options_to_std, prepare_open_options_for_open}; +use crate::fs::{ + errors, file_path, get_access_mode, get_creation_mode, get_flags_and_attributes, + FollowSymlinks, OpenOptions, OpenUncheckedError, SymlinkKind, +}; use crate::{ambient_authority, AmbientAuthority}; +use std::convert::TryInto; +use std::ffi::OsStr; +use std::os::windows::ffi::OsStrExt; use std::os::windows::fs::MetadataExt; -use std::path::Path; +use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle}; +use std::path::{Component, Path, PathBuf}; use std::{fs, io}; -use windows_sys::Win32::Foundation; +use windows_sys::Win32::Foundation::{self, ERROR_ACCESS_DENIED, HANDLE, INVALID_HANDLE_VALUE}; use windows_sys::Win32::Storage::FileSystem::{ - FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_OPEN_REPARSE_POINT, + CreateFileW, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_OPEN_REPARSE_POINT, }; /// *Unsandboxed* function similar to `open`, but which does not perform @@ -17,8 +28,110 @@ pub(crate) fn open_unchecked( path: &Path, options: &OpenOptions, ) -> Result { - let full_path = concatenate(start, path).map_err(OpenUncheckedError::Other)?; - open_ambient_impl(&full_path, options, ambient_authority()) + let _ = ambient_authority; + + // We have the final `OpenOptions`; now prepare it for an `open`. + let mut prepared_opts = options.clone(); + let manually_trunc = prepare_open_options_for_open(&mut prepared_opts); + + handle_open_result( + open_at(start, path, &prepared_opts), + options, + manually_trunc, + ) +} + +// The following is derived from Rust's library/std/src/sys/windows/fs.rs +// at revision 56888c1e9b4135b511abd2d8e907099003d12281, except with a +// directory `start` parameter added and using `CreateFileAtW` instead of +// `CreateFileW`. + +fn open_at(start: &fs::File, path: &Path, opts: &OpenOptions) -> io::Result { + let mut dir = start.as_raw_handle() as HANDLE; + + // `PathCchCanonicalizeEx` and friends don't seem to work with relative + // paths. Or at least, when I tried it, they canonicalized "a" to "", + // which isn't what we want. So we manually canonicalize `..` and `.`. + // Hopefully there aren't other mysterious Windows path conventions that + // we're missing here. + let mut rebuilt = PathBuf::new(); + for component in path.components() { + match component { + Component::Prefix(_) | Component::RootDir => { + rebuilt.push(component); + dir = 0; + } + Component::Normal(_) => { + rebuilt.push(component); + } + Component::ParentDir => { + if !rebuilt.pop() { + // We popped past the beginning of `path`. Substitute in + // the path of `start` and convert this to an ambient + // path by dropping the directory base. It's ok to do + // this because we're not sandboxing at this level of the + // code. + if dir == 0 { + return Err(io::Error::from_raw_os_error(ERROR_ACCESS_DENIED as _)); + } + rebuilt = match file_path(start) { + Some(path) => path, + None => { + return Err(io::Error::from_raw_os_error(ERROR_ACCESS_DENIED as _)); + } + }; + dir = 0; + // And then pop the last component of that. + let _ = rebuilt.pop(); + } + } + Component::CurDir => (), + } + } + + let mut wide = OsStr::encode_wide(rebuilt.as_os_str()).collect::>(); + + // 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 { + wide.push(0); + let handle = unsafe { + CreateFileW( + wide.as_ptr(), + get_access_mode(opts)?, + opts.ext.share_mode, + opts.ext.security_attributes, + get_creation_mode(opts)?, + get_flags_and_attributes(opts), + 0 as HANDLE, + ) + }; + if handle != INVALID_HANDLE_VALUE { + Ok(unsafe { fs::File::from_raw_handle(handle as _) }) + } else { + Err(io::Error::last_os_error()) + } + } else { + let handle = unsafe { + CreateFileAtW( + dir, + &wide, + get_access_mode(opts)?, + opts.ext.share_mode, + opts.ext.security_attributes, + get_creation_mode(opts)?, + get_flags_and_attributes(opts), + 0 as HANDLE, + ) + }; + + if let Ok(handle) = handle.try_into() { + Ok(>::from(handle)) + } else { + Err(io::Error::last_os_error()) + } + } } /// *Unsandboxed* function similar to `open_unchecked`, but which just operates @@ -29,8 +142,16 @@ pub(crate) fn open_ambient_impl( ambient_authority: AmbientAuthority, ) -> Result { let _ = ambient_authority; - let (opts, manually_trunc) = open_options_to_std(options); - match opts.open(path) { + let (std_opts, manually_trunc) = open_options_to_std(options); + handle_open_result(std_opts.open(path), options, manually_trunc) +} + +fn handle_open_result( + result: io::Result, + options: &OpenOptions, + manually_trunc: bool, +) -> Result { + match result { Ok(f) => { let enforce_dir = options.dir_required; let enforce_nofollow = options.follow == FollowSymlinks::No diff --git a/cap-primitives/src/windows/fs/reopen_impl.rs b/cap-primitives/src/windows/fs/reopen_impl.rs index eb517da06..d19b4c977 100644 --- a/cap-primitives/src/windows/fs/reopen_impl.rs +++ b/cap-primitives/src/windows/fs/reopen_impl.rs @@ -1,11 +1,10 @@ -use crate::fs::OpenOptions; +use crate::fs::{get_access_mode, get_flags_and_attributes, OpenOptions}; use io_lifetimes::AsHandle; use std::{fs, io}; -use windows_sys::Win32::Foundation::ERROR_INVALID_PARAMETER; use windows_sys::Win32::Storage::FileSystem::{ - FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_OPEN_REPARSE_POINT, FILE_FLAG_WRITE_THROUGH, - FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_WRITE_DATA, SECURITY_CONTEXT_TRACKING, - SECURITY_DELEGATION, SECURITY_EFFECTIVE_ONLY, SECURITY_IDENTIFICATION, SECURITY_IMPERSONATION, + FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_WRITE_THROUGH, FILE_GENERIC_READ, FILE_GENERIC_WRITE, + SECURITY_CONTEXT_TRACKING, SECURITY_DELEGATION, SECURITY_EFFECTIVE_ONLY, + SECURITY_IDENTIFICATION, SECURITY_IMPERSONATION, }; use windows_sys::Win32::System::SystemServices::{GENERIC_READ, GENERIC_WRITE}; use winx::file::{AccessMode, Flags, ShareMode}; @@ -84,33 +83,3 @@ pub(crate) fn reopen_impl(file: &fs::File, options: &OpenOptions) -> io::Result< ShareMode::FILE_SHARE_READ | ShareMode::FILE_SHARE_WRITE | ShareMode::FILE_SHARE_DELETE; winx::file::reopen_file(file.as_handle(), new_access_mode, new_share_mode, flags) } - -fn get_access_mode(options: &OpenOptions) -> io::Result { - match ( - options.read, - options.write, - options.append, - options.ext.access_mode, - ) { - (.., Some(mode)) => Ok(mode), - (true, false, false, None) => Ok(GENERIC_READ), - (false, true, false, None) => Ok(GENERIC_WRITE), - (true, true, false, None) => Ok(GENERIC_READ | GENERIC_WRITE), - (false, _, true, None) => Ok(FILE_GENERIC_WRITE & !FILE_WRITE_DATA), - (true, _, true, None) => Ok(GENERIC_READ | (FILE_GENERIC_WRITE & !FILE_WRITE_DATA)), - (false, false, false, None) => { - Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER as i32)) - } - } -} - -fn get_flags_and_attributes(options: &OpenOptions) -> u32 { - options.ext.custom_flags - | options.ext.attributes - | options.ext.security_qos_flags - | if options.create_new { - FILE_FLAG_OPEN_REPARSE_POINT - } else { - 0 - } -} diff --git a/tests/fs_additional.rs b/tests/fs_additional.rs index 465c861fb..63fd6f0d7 100644 --- a/tests/fs_additional.rs +++ b/tests/fs_additional.rs @@ -127,9 +127,9 @@ fn trailing_slash() { assert!(check!(check!(tmpdir.open("file/../file")).metadata()).is_file()); assert!(check!(check!(tmpdir.open_dir("file/..")).dir_metadata()).is_dir()); assert!(check!(check!(tmpdir.open("file/.")).metadata()).is_file()); - error!(tmpdir.open("file/../file/"), 123); - error!(tmpdir.open("file/"), 123); - error!(tmpdir.open_dir("file/../file/"), 123); + assert!(tmpdir.open_dir("file/../file/").is_err()); + assert!(tmpdir.open_dir("file/./").is_err()); + assert!(tmpdir.open_dir("file//").is_err()); assert!(tmpdir.open_dir("file/../file").is_err()); assert!(tmpdir.open_dir("file/.").is_err()); assert!(tmpdir.open_dir("file/").is_err()); @@ -161,11 +161,22 @@ fn trailing_slash_in_dir() { assert!(check!(check!(tmpdir.open("dir/file/../file")).metadata()).is_file()); assert!(check!(check!(tmpdir.open_dir("dir/file/..")).dir_metadata()).is_dir()); assert!(check!(check!(tmpdir.open("dir/file/.")).metadata()).is_file()); - error!(tmpdir.open("dir/file/../file/"), 123); - error!(tmpdir.open("dir/file/"), 123); - error!(tmpdir.open_dir("dir/file/../file/"), 123); + assert!(tmpdir.open("dir/file/../file/").is_err()); + let _ = check!(tmpdir.open("dir/file/../file/.")); + assert!(tmpdir.open("dir/file/../file/./").is_err()); + assert!(tmpdir.open("dir/file/").is_err()); + let _ = check!(tmpdir.open("dir/file/.")); + let _ = check!(tmpdir.open("dir/file/../file/.")); + assert!(tmpdir.open("dir/file/../file/./").is_err()); + assert!(tmpdir.open("dir/file/").is_err()); + let _ = check!(tmpdir.open("dir/file/.")); + assert!(tmpdir.open("dir/file/./").is_err()); + assert!(tmpdir.open_dir("dir/file/../file/").is_err()); + assert!(tmpdir.open_dir("dir/file/../file/.").is_err()); + assert!(tmpdir.open_dir("dir/file/../file/./").is_err()); assert!(tmpdir.open_dir("dir/file/../file").is_err()); assert!(tmpdir.open_dir("dir/file/.").is_err()); + assert!(tmpdir.open_dir("dir/file/./").is_err()); assert!(tmpdir.open_dir("dir/file/").is_err()); } } @@ -927,7 +938,6 @@ fn sync() { } #[test] -#[cfg(not(windows))] fn reopen_fd() { use io_lifetimes::AsFilelike; let tmpdir = tmpdir(); diff --git a/tests/paths-containing-nul.rs b/tests/paths-containing-nul.rs index 63164fc18..2eefc9c80 100644 --- a/tests/paths-containing-nul.rs +++ b/tests/paths-containing-nul.rs @@ -18,12 +18,19 @@ fn assert_invalid_input(on: &str, result: io::Result) { fn inner(on: &str, result: io::Result<()>) { match result { Ok(()) => panic!("{} didn't return an error on a path with NUL", on), - Err(e) => assert!( - e.kind() == io::ErrorKind::InvalidInput, - "{} returned a strange {:?} on a path with NUL", - on, - e - ), + Err(_e) => { + // TODO: Re-enable this assertion once the `io_error_more` + // feature is available. + /* + assert_eq!( + e.kind(), + io::ErrorKind::InvalidInput || io::ErrorKind::InvalidFilename, + "{} returned a strange {:?} on a path with NUL", + on, + e + ); + */ + } } } inner(on, result.map(drop)) diff --git a/tests/reopendir.rs b/tests/reopendir.rs new file mode 100644 index 000000000..3347ddcf0 --- /dev/null +++ b/tests/reopendir.rs @@ -0,0 +1,66 @@ +//! Tests for various forms of reopening a directory handle. + +#[macro_use] +mod sys_common; + +use sys_common::io::tmpdir; + +#[test] +fn reopendir_a() { + let tmpdir = tmpdir(); + check!(tmpdir.create_dir_all("dir/inner")); + + let inner = check!(tmpdir.open_dir("dir/inner")); + + check!(inner.open_dir(".")); +} + +#[test] +fn reopendir_b() { + let tmpdir = tmpdir(); + check!(tmpdir.create_dir_all("dir/inner")); + + let inner = check!(tmpdir.open_dir("dir/inner")); + + check!(inner.open_dir("./")); +} + +#[test] +fn reopendir_c() { + let tmpdir = tmpdir(); + check!(tmpdir.create_dir_all("dir/inner")); + + let inner = check!(tmpdir.open_dir("dir/inner")); + + check!(inner.open_dir("./.")); +} + +#[test] +fn reopendir_d() { + let tmpdir = tmpdir(); + check!(tmpdir.create_dir_all("dir/inner")); + + let _inner = check!(tmpdir.open_dir("dir/inner")); + + check!(tmpdir.open_dir("dir/inner")); +} + +#[test] +fn reopendir_e() { + let tmpdir = tmpdir(); + check!(tmpdir.create_dir_all("dir/inner")); + + let _inner = check!(tmpdir.open_dir("dir/inner")); + + check!(tmpdir.open_dir("dir/inner/.")); +} + +#[test] +fn reopendir_f() { + let tmpdir = tmpdir(); + check!(tmpdir.create_dir_all("dir/inner")); + + let _inner = check!(tmpdir.open_dir("dir/inner")); + + check!(tmpdir.open_dir("dir/inner/")); +} From e00166883402a2b28e79f00037da8686c2c93df5 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 15 Feb 2023 09:31:48 -0800 Subject: [PATCH 02/15] Use the `NtCreateFile` path for implementing `stat` on Windows. (#295) Always use `open_unchecked` with no access for implementing `stat` on Windows, as that now uses the new `NtCreateFile` path. --- .../src/windows/fs/create_file_at_w.rs | 4 ++ .../src/windows/fs/open_unchecked.rs | 6 ++ .../src/windows/fs/stat_unchecked.rs | 55 +++++++------------ 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/cap-primitives/src/windows/fs/create_file_at_w.rs b/cap-primitives/src/windows/fs/create_file_at_w.rs index 4d1804c97..afe1b43d2 100644 --- a/cap-primitives/src/windows/fs/create_file_at_w.rs +++ b/cap-primitives/src/windows/fs/create_file_at_w.rs @@ -66,6 +66,10 @@ 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. #[allow(non_snake_case)] pub unsafe fn CreateFileAtW( dir: HANDLE, diff --git a/cap-primitives/src/windows/fs/open_unchecked.rs b/cap-primitives/src/windows/fs/open_unchecked.rs index 1f57f8ed7..12d5c445b 100644 --- a/cap-primitives/src/windows/fs/open_unchecked.rs +++ b/cap-primitives/src/windows/fs/open_unchecked.rs @@ -95,7 +95,10 @@ fn open_at(start: &fs::File, path: &Path, opts: &OpenOptions) -> io::Result io::Result io::Result { - // When we have `windows_by_handle`, we just call `fs::metadata` etc. and it - // has everything. - #[cfg(windows_by_handle)] - { - let full_path = concatenate(start, path)?; - match follow { - FollowSymlinks::Yes => fs::metadata(full_path), - FollowSymlinks::No => fs::symlink_metadata(full_path), - } - .map(Metadata::from_just_metadata) - } + // Attempt to open the file to get the metadata that way, as that gives + // us all the info. + let mut opts = OpenOptions::new(); - // Otherwise, attempt to open the file to get the metadata that way, as - // that gives us all the info. - #[cfg(not(windows_by_handle))] - { - let mut opts = OpenOptions::new(); - opts.access_mode(0); - match follow { - FollowSymlinks::Yes => { - opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS); - opts.follow(FollowSymlinks::Yes); - } - FollowSymlinks::No => { - opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS); - opts.follow(FollowSymlinks::No); - } + // Explicitly request no access, because we're just querying metadata. + opts.access_mode(0); + + match follow { + FollowSymlinks::Yes => { + opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS); + opts.follow(FollowSymlinks::Yes); + } + FollowSymlinks::No => { + opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS); + opts.follow(FollowSymlinks::No); } - let file = open_unchecked(start, path, &opts)?; - Metadata::from_file(&file) } + + let file = open_unchecked(start, path, &opts)?; + Metadata::from_file(&file) } From 98affb27cb4a0bd98cb34aab4583b015fbdb1d4a Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 15 Mar 2023 16:32:16 -0700 Subject: [PATCH 03/15] Give ambient authority arguments names. (#298) Use `let _ = ambient_authority;` to suppress unused argument warnings rather than naming arguments `_`, because argument names show up in the documentation, and the fact that these arguments are unused is not part of the public interface. --- cap-directories/src/project_dirs.rs | 3 ++- cap-primitives/src/net/pool.rs | 17 +++++++++++++++-- cap-primitives/src/rustix/fs/dir_utils.rs | 7 ++++++- cap-primitives/src/time/monotonic_clock.rs | 3 ++- cap-primitives/src/time/system_clock.rs | 3 ++- cap-primitives/src/windows/fs/dir_utils.rs | 7 ++++++- cap-rand/src/lib.rs | 12 ++++++++---- 7 files changed, 41 insertions(+), 11 deletions(-) diff --git a/cap-directories/src/project_dirs.rs b/cap-directories/src/project_dirs.rs index 77b3ba98c..0c5ff6c17 100644 --- a/cap-directories/src/project_dirs.rs +++ b/cap-directories/src/project_dirs.rs @@ -34,8 +34,9 @@ impl ProjectDirs { qualifier: &str, organization: &str, application: &str, - _: AmbientAuthority, + ambient_authority: AmbientAuthority, ) -> Option { + let _ = ambient_authority; let inner = directories_next::ProjectDirs::from(qualifier, organization, application)?; Some(Self { inner }) } diff --git a/cap-primitives/src/net/pool.rs b/cap-primitives/src/net/pool.rs index 241d9e885..3e37c211e 100644 --- a/cap-primitives/src/net/pool.rs +++ b/cap-primitives/src/net/pool.rs @@ -51,7 +51,14 @@ impl Pool { /// # Ambient Authority /// /// This function allows ambient access to any IP address. - pub fn insert_ip_net(&mut self, ip_net: ipnet::IpNet, port: u16, _: AmbientAuthority) { + pub fn insert_ip_net( + &mut self, + ip_net: ipnet::IpNet, + port: u16, + ambient_authority: AmbientAuthority, + ) { + let _ = ambient_authority; + self.grants.push(IpGrant { set: AddrSet::Net(ip_net), port, @@ -63,7 +70,13 @@ impl Pool { /// # Ambient Authority /// /// This function allows ambient access to any IP address. - pub fn insert_socket_addr(&mut self, addr: net::SocketAddr, _: AmbientAuthority) { + pub fn insert_socket_addr( + &mut self, + addr: net::SocketAddr, + ambient_authority: AmbientAuthority, + ) { + let _ = ambient_authority; + self.grants.push(IpGrant { set: AddrSet::Net(addr.ip().into()), port: addr.port(), diff --git a/cap-primitives/src/rustix/fs/dir_utils.rs b/cap-primitives/src/rustix/fs/dir_utils.rs index c44fd32f8..63f5e3deb 100644 --- a/cap-primitives/src/rustix/fs/dir_utils.rs +++ b/cap-primitives/src/rustix/fs/dir_utils.rs @@ -102,7 +102,12 @@ pub(crate) fn canonicalize_options() -> OpenOptions { /// /// This function is not sandboxed and may trivially access any path that the /// host process has access to. -pub(crate) fn open_ambient_dir_impl(path: &Path, _: AmbientAuthority) -> io::Result { +pub(crate) fn open_ambient_dir_impl( + path: &Path, + ambient_authority: AmbientAuthority, +) -> io::Result { + let _ = ambient_authority; + let mut options = fs::OpenOptions::new(); options.read(true); diff --git a/cap-primitives/src/time/monotonic_clock.rs b/cap-primitives/src/time/monotonic_clock.rs index 512b6b863..8bc546789 100644 --- a/cap-primitives/src/time/monotonic_clock.rs +++ b/cap-primitives/src/time/monotonic_clock.rs @@ -17,7 +17,8 @@ impl MonotonicClock { /// /// This uses ambient authority to accesses clocks. #[inline] - pub const fn new(_: AmbientAuthority) -> Self { + pub const fn new(ambient_authority: AmbientAuthority) -> Self { + let _ = ambient_authority; Self(()) } diff --git a/cap-primitives/src/time/system_clock.rs b/cap-primitives/src/time/system_clock.rs index e1de98b4e..3dc108f15 100644 --- a/cap-primitives/src/time/system_clock.rs +++ b/cap-primitives/src/time/system_clock.rs @@ -26,7 +26,8 @@ impl SystemClock { /// /// This uses ambient authority to accesses clocks. #[inline] - pub const fn new(_: AmbientAuthority) -> Self { + pub const fn new(ambient_authority: AmbientAuthority) -> Self { + let _ = ambient_authority; Self(()) } diff --git a/cap-primitives/src/windows/fs/dir_utils.rs b/cap-primitives/src/windows/fs/dir_utils.rs index 39de7e5bd..eb8f282da 100644 --- a/cap-primitives/src/windows/fs/dir_utils.rs +++ b/cap-primitives/src/windows/fs/dir_utils.rs @@ -92,7 +92,12 @@ pub(crate) fn canonicalize_options() -> OpenOptions { /// /// This function is not sandboxed and may trivially access any path that the /// host process has access to. -pub(crate) fn open_ambient_dir_impl(path: &Path, _: AmbientAuthority) -> io::Result { +pub(crate) fn open_ambient_dir_impl( + path: &Path, + ambient_authority: AmbientAuthority, +) -> io::Result { + let _ = ambient_authority; + // Set `FILE_FLAG_BACKUP_SEMANTICS` so that we can open directories. Unset // `FILE_SHARE_DELETE` so that directories can't be renamed or deleted // underneath us, since we use paths to implement many directory operations. diff --git a/cap-rand/src/lib.rs b/cap-rand/src/lib.rs index deded5894..d66a7def2 100644 --- a/cap-rand/src/lib.rs +++ b/cap-rand/src/lib.rs @@ -74,7 +74,8 @@ pub mod rngs { /// This function makes use of ambient authority to access the platform /// entropy source. #[inline] - pub const fn default(_: AmbientAuthority) -> Self { + pub const fn default(ambient_authority: AmbientAuthority) -> Self { + let _ = ambient_authority; Self(()) } } @@ -161,7 +162,8 @@ pub mod rngs { /// This function makes use of ambient authority to access the platform entropy /// source. #[inline] -pub fn thread_rng(_: AmbientAuthority) -> rngs::CapRng { +pub fn thread_rng(ambient_authority: AmbientAuthority) -> rngs::CapRng { + let _ = ambient_authority; rngs::CapRng { inner: rand::thread_rng(), } @@ -176,7 +178,8 @@ pub fn thread_rng(_: AmbientAuthority) -> rngs::CapRng { /// This function makes use of ambient authority to access the platform entropy /// source. #[inline] -pub fn std_rng_from_entropy(_: AmbientAuthority) -> rngs::StdRng { +pub fn std_rng_from_entropy(ambient_authority: AmbientAuthority) -> rngs::StdRng { + let _ = ambient_authority; rand::rngs::StdRng::from_entropy() } @@ -189,9 +192,10 @@ pub fn std_rng_from_entropy(_: AmbientAuthority) -> rngs::StdRng { /// This function makes use of ambient authority to access the platform entropy /// source. #[inline] -pub fn random(_: AmbientAuthority) -> T +pub fn random(ambient_authority: AmbientAuthority) -> T where crate::distributions::Standard: crate::distributions::Distribution, { + let _ = ambient_authority; rand::random() } From fd1afe0dc459a0403a37578631643b1d82cdce2b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 16 Mar 2023 06:29:45 -0700 Subject: [PATCH 04/15] Add a `Pool::insert_ip_net_any_port` function. (#299) * Add a `Pool::insert_ip_net_any_port` function. Add a function to `Pool` to allow inserting a network that can accept any port. * Add support for port ranges, and add some unit tests. --- cap-async-std/src/net/pool.rs | 30 +++++++ cap-primitives/src/net/pool.rs | 151 ++++++++++++++++++++++++++++++--- cap-std/src/net/pool.rs | 35 ++++++++ 3 files changed, 203 insertions(+), 13 deletions(-) diff --git a/cap-async-std/src/net/pool.rs b/cap-async-std/src/net/pool.rs index ea16ee4cb..e2aaaf8fd 100644 --- a/cap-async-std/src/net/pool.rs +++ b/cap-async-std/src/net/pool.rs @@ -20,6 +20,36 @@ impl Pool { } } + /// Add a range of network addresses, accepting any port, to the pool. + /// + /// Unlike `insert_ip_net`, this function grants access to any requested + /// port. + /// + /// # Ambient Authority + /// + /// This function allows ambient access to any IP address. + pub fn insert_ip_net_port_any(&mut self, ip_net: ipnet::IpNet, ambient_authority: AmbientAuthority) { + self.cap.insert_ip_net_port_any(ip_net, ambient_authority) + } + + /// Add a range of network addresses, accepting a range of ports, to the pool. + /// + /// This grants access to the port range starting at `ports_start` and, + /// if `ports_end` is provided, ending before `ports_end`. + /// + /// # Ambient Authority + /// + /// This function allows ambient access to any IP address. + pub fn insert_ip_net_port_range( + &mut self, + ip_net: ipnet::IpNet, + ports_start: u16, + ports_end: Option, + ambient_authority: AmbientAuthority, + ) { + self.cap.insert_ip_net_port_range(ip_net, ports_start, ports_end, ambient_authority) + } + /// Add a range of network addresses with a specific port to the pool. /// /// # Ambient Authority diff --git a/cap-primitives/src/net/pool.rs b/cap-primitives/src/net/pool.rs index 3e37c211e..22250fe85 100644 --- a/cap-primitives/src/net/pool.rs +++ b/cap-primitives/src/net/pool.rs @@ -1,5 +1,9 @@ -use ambient_authority::AmbientAuthority; +#[cfg(test)] +use crate::ambient_authority; +use crate::AmbientAuthority; use ipnet::IpNet; +#[cfg(test)] +use std::str::FromStr; use std::{io, net}; // TODO: Perhaps we should have our own version of `ToSocketAddrs` which @@ -21,12 +25,27 @@ impl AddrSet { #[derive(Clone)] struct IpGrant { set: AddrSet, - port: u16, // TODO: IANA port names, TODO: range + ports_start: u16, + ports_end: Option, } impl IpGrant { fn contains(&self, addr: &net::SocketAddr) -> bool { - self.set.contains(addr.ip()) && addr.port() == self.port + if !self.set.contains(addr.ip()) { + return false; + } + + let port = addr.port(); + if port < self.ports_start { + return false; + } + if let Some(ports_end) = self.ports_end { + if port >= ports_end { + return false; + } + } + + true } } @@ -46,25 +65,57 @@ impl Pool { Self { grants: Vec::new() } } - /// Add a range of network addresses with a specific port to the pool. + /// Add a range of network addresses, accepting any port, to the pool. /// /// # Ambient Authority /// /// This function allows ambient access to any IP address. - pub fn insert_ip_net( + pub fn insert_ip_net_port_any( &mut self, ip_net: ipnet::IpNet, - port: u16, + ambient_authority: AmbientAuthority, + ) { + self.insert_ip_net_port_range(ip_net, 0, None, ambient_authority) + } + + /// Add a range of network addresses, accepting a range of ports, to the pool. + /// + /// This grants access to the port range starting at `ports_start` and, + /// if `ports_end` is provided, ending before `ports_end`. + /// + /// # Ambient Authority + /// + /// This function allows ambient access to any IP address. + pub fn insert_ip_net_port_range( + &mut self, + ip_net: ipnet::IpNet, + ports_start: u16, + ports_end: Option, ambient_authority: AmbientAuthority, ) { let _ = ambient_authority; self.grants.push(IpGrant { set: AddrSet::Net(ip_net), - port, + ports_start, + ports_end, }) } + /// Add a range of network addresses with a specific port to the pool. + /// + /// # Ambient Authority + /// + /// This function allows ambient access to any IP address. + pub fn insert_ip_net( + &mut self, + ip_net: ipnet::IpNet, + port: u16, + ambient_authority: AmbientAuthority, + ) { + self.insert_ip_net_port_range(ip_net, port, port.checked_add(1), ambient_authority) + } + /// Add a specific [`net::SocketAddr`] to the pool. /// /// # Ambient Authority @@ -75,12 +126,7 @@ impl Pool { addr: net::SocketAddr, ambient_authority: AmbientAuthority, ) { - let _ = ambient_authority; - - self.grants.push(IpGrant { - set: AddrSet::Net(addr.ip().into()), - port: addr.port(), - }) + self.insert_ip_net(addr.ip().into(), addr.port(), ambient_authority) } /// Check whether the given address is within the pool. @@ -98,3 +144,82 @@ impl Pool { /// An empty array of `SocketAddr`s. pub const NO_SOCKET_ADDRS: &[net::SocketAddr] = &[]; + +#[test] +fn test_empty() { + let p = Pool::new(); + + p.check_addr(&net::SocketAddr::from_str("[::1]:0").unwrap()) + .unwrap_err(); + p.check_addr(&net::SocketAddr::from_str("[::1]:1023").unwrap()) + .unwrap_err(); + p.check_addr(&net::SocketAddr::from_str("[::1]:1024").unwrap()) + .unwrap_err(); + p.check_addr(&net::SocketAddr::from_str("[::1]:8080").unwrap()) + .unwrap_err(); + p.check_addr(&net::SocketAddr::from_str("[::1]:65535").unwrap()) + .unwrap_err(); +} + +#[test] +fn test_port_any() { + let mut p = Pool::new(); + p.insert_ip_net_port_any( + IpNet::new(net::IpAddr::V6(net::Ipv6Addr::LOCALHOST), 48).unwrap(), + ambient_authority(), + ); + + p.check_addr(&net::SocketAddr::from_str("[::1]:0").unwrap()) + .unwrap(); + p.check_addr(&net::SocketAddr::from_str("[::1]:1023").unwrap()) + .unwrap(); + p.check_addr(&net::SocketAddr::from_str("[::1]:1024").unwrap()) + .unwrap(); + p.check_addr(&net::SocketAddr::from_str("[::1]:8080").unwrap()) + .unwrap(); + p.check_addr(&net::SocketAddr::from_str("[::1]:65535").unwrap()) + .unwrap(); +} + +#[test] +fn test_port_range() { + let mut p = Pool::new(); + p.insert_ip_net_port_range( + IpNet::new(net::IpAddr::V6(net::Ipv6Addr::LOCALHOST), 48).unwrap(), + 1024, + Some(9000), + ambient_authority(), + ); + + p.check_addr(&net::SocketAddr::from_str("[::1]:0").unwrap()) + .unwrap_err(); + p.check_addr(&net::SocketAddr::from_str("[::1]:1023").unwrap()) + .unwrap_err(); + p.check_addr(&net::SocketAddr::from_str("[::1]:1024").unwrap()) + .unwrap(); + p.check_addr(&net::SocketAddr::from_str("[::1]:8080").unwrap()) + .unwrap(); + p.check_addr(&net::SocketAddr::from_str("[::1]:65535").unwrap()) + .unwrap_err(); +} + +#[test] +fn test_port_one() { + let mut p = Pool::new(); + p.insert_ip_net( + IpNet::new(net::IpAddr::V6(net::Ipv6Addr::LOCALHOST), 48).unwrap(), + 8080, + ambient_authority(), + ); + + p.check_addr(&net::SocketAddr::from_str("[::1]:0").unwrap()) + .unwrap_err(); + p.check_addr(&net::SocketAddr::from_str("[::1]:1023").unwrap()) + .unwrap_err(); + p.check_addr(&net::SocketAddr::from_str("[::1]:1024").unwrap()) + .unwrap_err(); + p.check_addr(&net::SocketAddr::from_str("[::1]:8080").unwrap()) + .unwrap(); + p.check_addr(&net::SocketAddr::from_str("[::1]:65535").unwrap()) + .unwrap_err(); +} diff --git a/cap-std/src/net/pool.rs b/cap-std/src/net/pool.rs index a9bd54005..084304de8 100644 --- a/cap-std/src/net/pool.rs +++ b/cap-std/src/net/pool.rs @@ -21,6 +21,41 @@ impl Pool { } } + /// Add a range of network addresses, accepting any port, to the pool. + /// + /// Unlike `insert_ip_net`, this function grants access to any requested + /// port. + /// + /// # Ambient Authority + /// + /// This function allows ambient access to any IP address. + pub fn insert_ip_net_port_any( + &mut self, + ip_net: ipnet::IpNet, + ambient_authority: AmbientAuthority, + ) { + self.cap.insert_ip_net_port_any(ip_net, ambient_authority) + } + + /// Add a range of network addresses, accepting a range of ports, to the pool. + /// + /// This grants access to the port range starting at `ports_start` and, + /// if `ports_end` is provided, ending before `ports_end`. + /// + /// # Ambient Authority + /// + /// This function allows ambient access to any IP address. + pub fn insert_ip_net_port_range( + &mut self, + ip_net: ipnet::IpNet, + ports_start: u16, + ports_end: Option, + ambient_authority: AmbientAuthority, + ) { + self.cap + .insert_ip_net_port_range(ip_net, ports_start, ports_end, ambient_authority) + } + /// Add a range of network addresses with a specific port to the pool. /// /// # AmbientAuthority From 2a4e02662150d373b7bd936d78da5f01eb5b998f Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 16 Mar 2023 06:42:31 -0700 Subject: [PATCH 05/15] Add doc links to reference to `std` API items. (#300) --- cap-async-std/src/fs/mod.rs | 2 +- cap-async-std/src/lib.rs | 2 +- cap-async-std/src/net/mod.rs | 2 +- cap-async-std/src/time/mod.rs | 2 +- cap-std/src/fs/mod.rs | 2 +- cap-std/src/lib.rs | 2 +- cap-std/src/net/mod.rs | 2 +- cap-std/src/time/mod.rs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cap-async-std/src/fs/mod.rs b/cap-async-std/src/fs/mod.rs index e7c038b34..1b693128f 100644 --- a/cap-async-std/src/fs/mod.rs +++ b/cap-async-std/src/fs/mod.rs @@ -1,4 +1,4 @@ -//! A capability-based filesystem API modeled after `async_std::fs`. +//! A capability-based filesystem API modeled after [`async_std::fs`]. //! //! This corresponds to [`async_std::fs`]. //! diff --git a/cap-async-std/src/lib.rs b/cap-async-std/src/lib.rs index 5a604329a..ad53195ba 100644 --- a/cap-async-std/src/lib.rs +++ b/cap-async-std/src/lib.rs @@ -1,4 +1,4 @@ -//! A capability-based API modeled after `async_std`. +//! A capability-based API modeled after [`async_std`]. //! //! This corresponds to [`async_std`]. //! diff --git a/cap-async-std/src/net/mod.rs b/cap-async-std/src/net/mod.rs index 7627955c2..24b2e5aea 100644 --- a/cap-async-std/src/net/mod.rs +++ b/cap-async-std/src/net/mod.rs @@ -1,4 +1,4 @@ -//! A capability-based network API modeled after `async_std::net`. +//! A capability-based network API modeled after [`async_std::net`]. //! //! This corresponds to [`async_std::net`]. //! diff --git a/cap-async-std/src/time/mod.rs b/cap-async-std/src/time/mod.rs index 37e75c7dc..ff5d788d1 100644 --- a/cap-async-std/src/time/mod.rs +++ b/cap-async-std/src/time/mod.rs @@ -1,4 +1,4 @@ -//! A capability-based clock API modeled after `std::time`. +//! A capability-based clock API modeled after [`std::time`]. //! //! This corresponds to [`std::time`]. //! diff --git a/cap-std/src/fs/mod.rs b/cap-std/src/fs/mod.rs index 453b682cc..948831127 100644 --- a/cap-std/src/fs/mod.rs +++ b/cap-std/src/fs/mod.rs @@ -1,4 +1,4 @@ -//! A capability-based filesystem API modeled after `std::fs`. +//! A capability-based filesystem API modeled after [`std::fs`]. //! //! This corresponds to [`std::fs`]. //! diff --git a/cap-std/src/lib.rs b/cap-std/src/lib.rs index d5996a5ab..9723bc1df 100644 --- a/cap-std/src/lib.rs +++ b/cap-std/src/lib.rs @@ -1,4 +1,4 @@ -//! A capability-based API modeled after `std`. +//! A capability-based API modeled after [`std`]. //! //! This corresponds to [`std`]. //! diff --git a/cap-std/src/net/mod.rs b/cap-std/src/net/mod.rs index 4c36cab01..75a5b2767 100644 --- a/cap-std/src/net/mod.rs +++ b/cap-std/src/net/mod.rs @@ -1,4 +1,4 @@ -//! A capability-based network API modeled after `std::net`. +//! A capability-based network API modeled after [`std::net`]. //! //! This corresponds to [`std::net`]. //! diff --git a/cap-std/src/time/mod.rs b/cap-std/src/time/mod.rs index 37e75c7dc..ff5d788d1 100644 --- a/cap-std/src/time/mod.rs +++ b/cap-std/src/time/mod.rs @@ -1,4 +1,4 @@ -//! A capability-based clock API modeled after `std::time`. +//! A capability-based clock API modeled after [`std::time`]. //! //! This corresponds to [`std::time`]. //! From 6684aa1b3aa61add54956f2a70e976cdbcee87a2 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 16 Mar 2023 06:59:58 -0700 Subject: [PATCH 06/15] chore: Release --- Cargo.toml | 12 ++++++------ cap-directories/Cargo.toml | 4 ++-- cap-fs-ext/Cargo.toml | 6 +++--- cap-primitives/Cargo.toml | 2 +- cap-rand/Cargo.toml | 2 +- cap-std/Cargo.toml | 4 ++-- cap-tempfile/Cargo.toml | 4 ++-- cap-time-ext/Cargo.toml | 6 +++--- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02214d8ec..4061a83b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-std-workspace" -version = "1.0.5" +version = "1.0.6" description = "Capability-based version of the Rust standard library" authors = [ "Dan Gohman ", @@ -20,11 +20,11 @@ rust-version = "1.56" #async-std = { version = "1.10.0", features = ["attributes"] } anyhow = "1.0.37" #cap-async-std = { path = "cap-async-std", version = "^0.25.0" } -cap-fs-ext = { path = "cap-fs-ext", version = "^1.0.5" } -cap-directories = { path = "cap-directories", version = "^1.0.5" } -cap-std = { path = "cap-std", version = "^1.0.5" } -cap-tempfile = { path = "cap-tempfile", version = "^1.0.5" } -cap-rand = { path = "cap-rand", version = "^1.0.5" } +cap-fs-ext = { path = "cap-fs-ext", version = "^1.0.6" } +cap-directories = { path = "cap-directories", version = "^1.0.6" } +cap-std = { path = "cap-std", version = "^1.0.6" } +cap-tempfile = { path = "cap-tempfile", version = "^1.0.6" } +cap-rand = { path = "cap-rand", version = "^1.0.6" } rand = "0.8.1" tempfile = "3.1.0" camino = "1.0.5" diff --git a/cap-directories/Cargo.toml b/cap-directories/Cargo.toml index 6878cde52..640c63cba 100644 --- a/cap-directories/Cargo.toml +++ b/cap-directories/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-directories" -version = "1.0.5" +version = "1.0.6" description = "Capability-based standard directories for config, cache and other data" authors = [ "Dan Gohman ", @@ -13,7 +13,7 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-std = { path = "../cap-std", version = "^1.0.5" } +cap-std = { path = "../cap-std", version = "^1.0.6" } directories-next = "2.0.0" [target.'cfg(not(windows))'.dependencies] diff --git a/cap-fs-ext/Cargo.toml b/cap-fs-ext/Cargo.toml index 9a8ec4f19..4eac2ef88 100644 --- a/cap-fs-ext/Cargo.toml +++ b/cap-fs-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-fs-ext" -version = "1.0.5" +version = "1.0.6" description = "Extension traits for `Dir`, `File`, etc." authors = [ "Dan Gohman ", @@ -15,8 +15,8 @@ edition = "2018" [dependencies] arf-strings = { version = "0.7.0", optional = true } #cap-async-std = { path = "../cap-async-std", optional = true, version = "^0.25.0" } -cap-std = { path = "../cap-std", optional = true, version = "^1.0.5" } -cap-primitives = { path = "../cap-primitives", version = "^1.0.5" } +cap-std = { path = "../cap-std", optional = true, version = "^1.0.6" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.6" } io-lifetimes = { version = "1.0.0", default-features = false } # Enable "unstable" for `spawn_blocking`. #async-std = { version = "1.10.0", features = ["attributes", "unstable"], optional = true } diff --git a/cap-primitives/Cargo.toml b/cap-primitives/Cargo.toml index dcf69af99..8d15ac90f 100644 --- a/cap-primitives/Cargo.toml +++ b/cap-primitives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-primitives" -version = "1.0.5" +version = "1.0.6" description = "Capability-based primitives" authors = [ "Dan Gohman ", diff --git a/cap-rand/Cargo.toml b/cap-rand/Cargo.toml index 548e4d55f..26ed093bb 100644 --- a/cap-rand/Cargo.toml +++ b/cap-rand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-rand" -version = "1.0.5" +version = "1.0.6" description = "Capability-based random number generators" authors = [ "Dan Gohman ", diff --git a/cap-std/Cargo.toml b/cap-std/Cargo.toml index d20ba5c89..947f1fc78 100644 --- a/cap-std/Cargo.toml +++ b/cap-std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-std" -version = "1.0.5" +version = "1.0.6" description = "Capability-based version of the Rust standard library" authors = [ "Dan Gohman ", @@ -18,7 +18,7 @@ rustdoc-args = ["--cfg=doc_cfg"] [dependencies] arf-strings = { version = "0.7.0", optional = true } -cap-primitives = { path = "../cap-primitives", version = "^1.0.5" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.6" } ipnet = "2.3.0" io-extras = "0.17.0" io-lifetimes = { version = "1.0.0", default-features = false } diff --git a/cap-tempfile/Cargo.toml b/cap-tempfile/Cargo.toml index 80d162aad..77ece99d7 100644 --- a/cap-tempfile/Cargo.toml +++ b/cap-tempfile/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-tempfile" -version = "1.0.5" +version = "1.0.6" description = "Capability-based temporary directories" authors = [ "Dan Gohman ", @@ -13,7 +13,7 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-std = { path = "../cap-std", version = "^1.0.5" } +cap-std = { path = "../cap-std", version = "^1.0.6" } uuid = { version = "1.0.0", features = ["v4"] } camino = { version = "1.0.5", optional = true } diff --git a/cap-time-ext/Cargo.toml b/cap-time-ext/Cargo.toml index 5e52302e0..8e94ec540 100644 --- a/cap-time-ext/Cargo.toml +++ b/cap-time-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-time-ext" -version = "1.0.5" +version = "1.0.6" description = "Extension traits for `SystemClock` and `MonotonicClock`" authors = [ "Dan Gohman ", @@ -13,8 +13,8 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-primitives = { path = "../cap-primitives", version = "^1.0.5" } -cap-std = { path = "../cap-std", optional = true, version = "^1.0.5" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.6" } +cap-std = { path = "../cap-std", optional = true, version = "^1.0.6" } [target.'cfg(not(windows))'.dependencies] rustix = { version = "0.36.0", features = ["time"] } From 9a6f137bf08dc6b844f7765fda3e74023de57654 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sat, 18 Mar 2023 11:48:49 -0700 Subject: [PATCH 07/15] Use `RUSTC_WRAPPER` instead of `CARGO_RUSTC_WRAPPER`. (#303) (#304) Similar to dtolnay/anyhow#248, and as suggested [here], check `RUSTC_WRAPPER` instead of `CARGO_RUSTC_WRAPPER`. [here]: https://github.com/bytecodealliance/rustix/pull/544#discussion_r1140467731 --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 3481199df..280aeddf5 100644 --- a/build.rs +++ b/build.rs @@ -50,7 +50,7 @@ fn can_compile>(test: T) -> bool { let rustc = var("RUSTC").unwrap(); let target = var("TARGET").unwrap(); - let mut cmd = if let Ok(wrapper) = var("CARGO_RUSTC_WRAPPER") { + let mut cmd = if let Ok(wrapper) = var("RUSTC_WRAPPER") { let mut cmd = std::process::Command::new(wrapper); // The wrapper's first argument is supposed to be the path to rustc. cmd.arg(rustc); From 946ffeff567d0efb03a21e41a8c72fde6b652a96 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Sat, 18 Mar 2023 11:50:15 -0700 Subject: [PATCH 08/15] chore: Release --- Cargo.toml | 12 ++++++------ cap-directories/Cargo.toml | 4 ++-- cap-fs-ext/Cargo.toml | 6 +++--- cap-primitives/Cargo.toml | 2 +- cap-rand/Cargo.toml | 2 +- cap-std/Cargo.toml | 4 ++-- cap-tempfile/Cargo.toml | 4 ++-- cap-time-ext/Cargo.toml | 6 +++--- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4061a83b1..b5a877fbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-std-workspace" -version = "1.0.6" +version = "1.0.7" description = "Capability-based version of the Rust standard library" authors = [ "Dan Gohman ", @@ -20,11 +20,11 @@ rust-version = "1.56" #async-std = { version = "1.10.0", features = ["attributes"] } anyhow = "1.0.37" #cap-async-std = { path = "cap-async-std", version = "^0.25.0" } -cap-fs-ext = { path = "cap-fs-ext", version = "^1.0.6" } -cap-directories = { path = "cap-directories", version = "^1.0.6" } -cap-std = { path = "cap-std", version = "^1.0.6" } -cap-tempfile = { path = "cap-tempfile", version = "^1.0.6" } -cap-rand = { path = "cap-rand", version = "^1.0.6" } +cap-fs-ext = { path = "cap-fs-ext", version = "^1.0.7" } +cap-directories = { path = "cap-directories", version = "^1.0.7" } +cap-std = { path = "cap-std", version = "^1.0.7" } +cap-tempfile = { path = "cap-tempfile", version = "^1.0.7" } +cap-rand = { path = "cap-rand", version = "^1.0.7" } rand = "0.8.1" tempfile = "3.1.0" camino = "1.0.5" diff --git a/cap-directories/Cargo.toml b/cap-directories/Cargo.toml index 640c63cba..5ae06e515 100644 --- a/cap-directories/Cargo.toml +++ b/cap-directories/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-directories" -version = "1.0.6" +version = "1.0.7" description = "Capability-based standard directories for config, cache and other data" authors = [ "Dan Gohman ", @@ -13,7 +13,7 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-std = { path = "../cap-std", version = "^1.0.6" } +cap-std = { path = "../cap-std", version = "^1.0.7" } directories-next = "2.0.0" [target.'cfg(not(windows))'.dependencies] diff --git a/cap-fs-ext/Cargo.toml b/cap-fs-ext/Cargo.toml index 4eac2ef88..77fa014e7 100644 --- a/cap-fs-ext/Cargo.toml +++ b/cap-fs-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-fs-ext" -version = "1.0.6" +version = "1.0.7" description = "Extension traits for `Dir`, `File`, etc." authors = [ "Dan Gohman ", @@ -15,8 +15,8 @@ edition = "2018" [dependencies] arf-strings = { version = "0.7.0", optional = true } #cap-async-std = { path = "../cap-async-std", optional = true, version = "^0.25.0" } -cap-std = { path = "../cap-std", optional = true, version = "^1.0.6" } -cap-primitives = { path = "../cap-primitives", version = "^1.0.6" } +cap-std = { path = "../cap-std", optional = true, version = "^1.0.7" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.7" } io-lifetimes = { version = "1.0.0", default-features = false } # Enable "unstable" for `spawn_blocking`. #async-std = { version = "1.10.0", features = ["attributes", "unstable"], optional = true } diff --git a/cap-primitives/Cargo.toml b/cap-primitives/Cargo.toml index 8d15ac90f..e729dbbfc 100644 --- a/cap-primitives/Cargo.toml +++ b/cap-primitives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-primitives" -version = "1.0.6" +version = "1.0.7" description = "Capability-based primitives" authors = [ "Dan Gohman ", diff --git a/cap-rand/Cargo.toml b/cap-rand/Cargo.toml index 26ed093bb..4645d7766 100644 --- a/cap-rand/Cargo.toml +++ b/cap-rand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-rand" -version = "1.0.6" +version = "1.0.7" description = "Capability-based random number generators" authors = [ "Dan Gohman ", diff --git a/cap-std/Cargo.toml b/cap-std/Cargo.toml index 947f1fc78..538d57c79 100644 --- a/cap-std/Cargo.toml +++ b/cap-std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-std" -version = "1.0.6" +version = "1.0.7" description = "Capability-based version of the Rust standard library" authors = [ "Dan Gohman ", @@ -18,7 +18,7 @@ rustdoc-args = ["--cfg=doc_cfg"] [dependencies] arf-strings = { version = "0.7.0", optional = true } -cap-primitives = { path = "../cap-primitives", version = "^1.0.6" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.7" } ipnet = "2.3.0" io-extras = "0.17.0" io-lifetimes = { version = "1.0.0", default-features = false } diff --git a/cap-tempfile/Cargo.toml b/cap-tempfile/Cargo.toml index 77ece99d7..134e321cb 100644 --- a/cap-tempfile/Cargo.toml +++ b/cap-tempfile/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-tempfile" -version = "1.0.6" +version = "1.0.7" description = "Capability-based temporary directories" authors = [ "Dan Gohman ", @@ -13,7 +13,7 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-std = { path = "../cap-std", version = "^1.0.6" } +cap-std = { path = "../cap-std", version = "^1.0.7" } uuid = { version = "1.0.0", features = ["v4"] } camino = { version = "1.0.5", optional = true } diff --git a/cap-time-ext/Cargo.toml b/cap-time-ext/Cargo.toml index 8e94ec540..c5fe5218e 100644 --- a/cap-time-ext/Cargo.toml +++ b/cap-time-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-time-ext" -version = "1.0.6" +version = "1.0.7" description = "Extension traits for `SystemClock` and `MonotonicClock`" authors = [ "Dan Gohman ", @@ -13,8 +13,8 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-primitives = { path = "../cap-primitives", version = "^1.0.6" } -cap-std = { path = "../cap-std", optional = true, version = "^1.0.6" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.7" } +cap-std = { path = "../cap-std", optional = true, version = "^1.0.7" } [target.'cfg(not(windows))'.dependencies] rustix = { version = "0.36.0", features = ["time"] } From 2562c951acad64fc92b8fae0cca1b2806060ab46 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 20 Mar 2023 06:22:36 -0700 Subject: [PATCH 09/15] Don't use RUSTC_WRAPPER if it's empty. (#305) Port bytecodealliance/rustix#565 to cap-std. --- build.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 280aeddf5..1a2cead42 100644 --- a/build.rs +++ b/build.rs @@ -50,7 +50,14 @@ fn can_compile>(test: T) -> bool { let rustc = var("RUSTC").unwrap(); let target = var("TARGET").unwrap(); - let mut cmd = if let Ok(wrapper) = var("RUSTC_WRAPPER") { + // Use `RUSTC_WRAPPER` if it's set, unless it's set to an empty string, + // as documented [here]. + // [here]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads + let wrapper = var("RUSTC_WRAPPER") + .ok() + .and_then(|w| if w.is_empty() { None } else { Some(w) }); + + let mut cmd = if let Some(wrapper) = wrapper { let mut cmd = std::process::Command::new(wrapper); // The wrapper's first argument is supposed to be the path to rustc. cmd.arg(rustc); From 1ee5281067bfc427a9ec479bede796c6f565260b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 28 Mar 2023 09:54:46 -0700 Subject: [PATCH 10/15] Add a `File::create_ambient` function. `File::create_ambient` is to `File::open_ambient` as `std::fs::File::create` is to `std::fs::File::open`. --- cap-async-std/src/fs/file.rs | 25 +++++++++++++++++++++++++ cap-async-std/src/fs_utf8/file.rs | 19 +++++++++++++++++++ cap-std/src/fs/file.rs | 21 +++++++++++++++++++++ cap-std/src/fs_utf8/file.rs | 20 ++++++++++++++++++++ tests/open-ambient.rs | 9 +++++++++ 5 files changed, 94 insertions(+) diff --git a/cap-async-std/src/fs/file.rs b/cap-async-std/src/fs/file.rs index 2b3c19527..383405d67 100644 --- a/cap-async-std/src/fs/file.rs +++ b/cap-async-std/src/fs/file.rs @@ -131,6 +131,31 @@ impl File { .map(|f| Self::from_std(f.into())) } + /// Constructs a new instance of `Self` in write-only mode by opening, + /// creating or truncating, the given path as a file using the host + /// process' ambient authority. + /// + /// # Ambient Authority + /// + /// This function is not sandboxed and may access any path that the host + /// process has access to. + #[inline] + pub async fn create_ambient>( + path: P, + ambient_authority: AmbientAuthority, + ) -> io::Result { + let path = path.as_ref().to_path_buf(); + spawn_blocking(move || { + open_ambient( + path.as_ref(), + OpenOptions::new().write(true).create(true).truncate(true), + ambient_authority, + ) + }) + .await + .map(|f| Self::from_std(f.into())) + } + /// Constructs a new instance of `Self` with the options specified by /// `options` by opening the given path as a file using the host process' /// ambient authority. diff --git a/cap-async-std/src/fs_utf8/file.rs b/cap-async-std/src/fs_utf8/file.rs index 6628fca1e..3bb66cf80 100644 --- a/cap-async-std/src/fs_utf8/file.rs +++ b/cap-async-std/src/fs_utf8/file.rs @@ -125,6 +125,25 @@ impl File { .map(Self::from_cap_std) } + /// Constructs a new instance of `Self` in write-only mode by opening, + /// creating or truncating, the given path as a file using the host + /// process' ambient authority. + /// + /// # Ambient Authority + /// + /// This function is not sandboxed and may access any path that the host + /// process has access to. + #[inline] + pub async fn create_ambient>( + path: P, + ambient_authority: AmbientAuthority, + ) -> io::Result { + let path = from_utf8(path)?; + crate::fs::File::create_ambient(path, ambient_authority) + .await + .map(Self::from_cap_std) + } + /// Constructs a new instance of `Self` with the options specified by /// `options` by opening the given path as a file using the host process' /// ambient authority. diff --git a/cap-std/src/fs/file.rs b/cap-std/src/fs/file.rs index 52421d29e..69afcacbe 100644 --- a/cap-std/src/fs/file.rs +++ b/cap-std/src/fs/file.rs @@ -123,6 +123,27 @@ impl File { Ok(Self::from_std(std)) } + /// Constructs a new instance of `Self` in write-only mode by opening, + /// creating or truncating, the given path as a file using the host + /// process' ambient authority. + /// + /// # Ambient Authority + /// + /// This function is not sandboxed and may access any path that the host + /// process has access to. + #[inline] + pub fn create_ambient>( + path: P, + ambient_authority: AmbientAuthority, + ) -> io::Result { + let std = open_ambient( + path.as_ref(), + OpenOptions::new().write(true).create(true).truncate(true), + ambient_authority, + )?; + Ok(Self::from_std(std)) + } + /// Constructs a new instance of `Self` with the options specified by /// `options` by opening the given path as a file using the host process' /// ambient authority. diff --git a/cap-std/src/fs_utf8/file.rs b/cap-std/src/fs_utf8/file.rs index 4d975bd17..161ad3f89 100644 --- a/cap-std/src/fs_utf8/file.rs +++ b/cap-std/src/fs_utf8/file.rs @@ -128,6 +128,26 @@ impl File { )?)) } + /// Constructs a new instance of `Self` in write-only mode by opening, + /// creating or truncating, the given path as a file using the host + /// process' ambient authority. + /// + /// # Ambient Authority + /// + /// This function is not sandboxed and may access any path that the host + /// process has access to. + #[inline] + pub fn create_ambient>( + path: P, + ambient_authority: AmbientAuthority, + ) -> io::Result { + let path = from_utf8(path.as_ref())?; + Ok(Self::from_cap_std(crate::fs::File::create_ambient( + path, + ambient_authority, + )?)) + } + /// Constructs a new instance of `Self` with the options specified by /// `options` by opening the given path as a file using the host process' /// ambient authority. diff --git a/tests/open-ambient.rs b/tests/open-ambient.rs index e25e013f0..870ea0976 100644 --- a/tests/open-ambient.rs +++ b/tests/open-ambient.rs @@ -9,6 +9,15 @@ fn test_open_ambient() { let _ = File::open_ambient("Cargo.toml", ambient_authority()).unwrap(); } +#[test] +fn test_create_ambient() { + let dir = tempfile::tempdir().unwrap(); + let foo_path = dir.path().join("foo"); + let _ = File::create_ambient(&foo_path, ambient_authority()).unwrap(); + let _ = File::open_ambient(&foo_path, ambient_authority()).unwrap(); + let _ = File::create_ambient(&foo_path, ambient_authority()).unwrap(); +} + #[test] fn test_create_dir_ambient() { let dir = tempfile::tempdir().unwrap(); From f2e65b940498de0e39fa69300155f3f0485c83fd Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 28 Mar 2023 09:59:09 -0700 Subject: [PATCH 11/15] Update CI to Ubuntu 20.04. Ubuntu 18.04 is no longer supported on Github actions. --- .github/workflows/main.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 34af028ed..56e593bf1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -201,7 +201,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - build: [stable, windows-latest, windows-2019, macos-latest, macos-10.15, beta, ubuntu-18.04, aarch64-ubuntu] + build: [stable, windows-latest, windows-2019, macos-latest, macos-10.15, beta, ubuntu-20.04, aarch64-ubuntu] include: - build: stable os: ubuntu-latest @@ -221,8 +221,8 @@ jobs: - build: beta os: ubuntu-latest rust: beta - - build: ubuntu-18.04 - os: ubuntu-18.04 + - build: ubuntu-20.04 + os: ubuntu-20.04 rust: stable - build: aarch64-ubuntu os: ubuntu-latest @@ -332,13 +332,13 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - build: [ubuntu, ubuntu-18.04] + build: [ubuntu, ubuntu-20.04] include: - build: ubuntu os: ubuntu-latest rust: nightly - - build: ubuntu-18.04 - os: ubuntu-18.04 + - build: ubuntu-20.04 + os: ubuntu-20.04 rust: nightly env: From 7d379e94e19896ad68ea8fbe05d251fd6176de81 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 16 Mar 2023 09:28:17 -0700 Subject: [PATCH 12/15] Have cap-std re-export ipnet, which is part of its public API. (#302) --- cap-async-std/Cargo.toml | 1 - cap-async-std/src/lib.rs | 3 ++- cap-async-std/src/net/pool.rs | 2 +- cap-primitives/src/lib.rs | 2 ++ cap-std/Cargo.toml | 1 - cap-std/src/lib.rs | 3 ++- cap-std/src/net/pool.rs | 2 +- 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cap-async-std/Cargo.toml b/cap-async-std/Cargo.toml index 1fbd14a90..ec1396959 100644 --- a/cap-async-std/Cargo.toml +++ b/cap-async-std/Cargo.toml @@ -19,7 +19,6 @@ arf-strings = { version = "0.7.0", optional = true } async-std = { version = "1.10.0", features = ["attributes", "unstable"] } cap-primitives = { path = "../cap-primitives", version = "^0.25.0" } io-lifetimes = { version = "1.0.0", default-features = false, features = ["async-std"] } -ipnet = "2.3.0" io-extras = { version = "0.17.0", features = ["use_async_std"] } camino = { version = "1.0.5", optional = true } diff --git a/cap-async-std/src/lib.rs b/cap-async-std/src/lib.rs index ad53195ba..9912a2bb3 100644 --- a/cap-async-std/src/lib.rs +++ b/cap-async-std/src/lib.rs @@ -50,5 +50,6 @@ pub use cap_primitives::{ambient_authority, AmbientAuthority}; // Re-export `async_std` to make it easy for users to depend on the same // version we do, because we use its types in our public API. pub use async_std; -// And this is also part of our public API +// And these are also part of our public API pub use io_lifetimes; +pub use cap_primitives::ipnet; diff --git a/cap-async-std/src/net/pool.rs b/cap-async-std/src/net/pool.rs index e2aaaf8fd..24106ea31 100644 --- a/cap-async-std/src/net/pool.rs +++ b/cap-async-std/src/net/pool.rs @@ -1,7 +1,7 @@ use crate::net::{TcpListener, TcpStream, ToSocketAddrs, UdpSocket}; use async_std::{io, net}; use cap_primitives::net::NO_SOCKET_ADDRS; -use cap_primitives::AmbientAuthority; +use cap_primitives::{ipnet, AmbientAuthority}; /// A pool of network addresses. /// diff --git a/cap-primitives/src/lib.rs b/cap-primitives/src/lib.rs index 19112cfed..e99439176 100644 --- a/cap-primitives/src/lib.rs +++ b/cap-primitives/src/lib.rs @@ -28,3 +28,5 @@ pub mod time; #[doc(hidden)] pub use ambient_authority::ambient_authority_known_at_compile_time; pub use ambient_authority::{ambient_authority, AmbientAuthority}; +// This is part of our public API. +pub use ipnet; diff --git a/cap-std/Cargo.toml b/cap-std/Cargo.toml index 538d57c79..48dc3b3d9 100644 --- a/cap-std/Cargo.toml +++ b/cap-std/Cargo.toml @@ -19,7 +19,6 @@ rustdoc-args = ["--cfg=doc_cfg"] [dependencies] arf-strings = { version = "0.7.0", optional = true } cap-primitives = { path = "../cap-primitives", version = "^1.0.7" } -ipnet = "2.3.0" io-extras = "0.17.0" io-lifetimes = { version = "1.0.0", default-features = false } camino = { version = "1.0.5", optional = true } diff --git a/cap-std/src/lib.rs b/cap-std/src/lib.rs index 9723bc1df..5ddf9f491 100644 --- a/cap-std/src/lib.rs +++ b/cap-std/src/lib.rs @@ -48,5 +48,6 @@ pub mod time; #[doc(hidden)] pub use cap_primitives::ambient_authority_known_at_compile_time; pub use cap_primitives::{ambient_authority, AmbientAuthority}; -// And this is also part of our public API +// And these are also part of our public API +pub use cap_primitives::ipnet; pub use io_lifetimes; diff --git a/cap-std/src/net/pool.rs b/cap-std/src/net/pool.rs index 084304de8..ac2ae957c 100644 --- a/cap-std/src/net/pool.rs +++ b/cap-std/src/net/pool.rs @@ -1,6 +1,6 @@ use crate::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket}; use cap_primitives::net::NO_SOCKET_ADDRS; -use cap_primitives::AmbientAuthority; +use cap_primitives::{ipnet, AmbientAuthority}; use std::time::Duration; use std::{io, net}; From f4232c55a710379523e56491e0bb525d40f73653 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 28 Mar 2023 10:33:19 -0700 Subject: [PATCH 13/15] chore: Release --- Cargo.toml | 12 ++++++------ cap-directories/Cargo.toml | 4 ++-- cap-fs-ext/Cargo.toml | 6 +++--- cap-primitives/Cargo.toml | 2 +- cap-rand/Cargo.toml | 2 +- cap-std/Cargo.toml | 4 ++-- cap-tempfile/Cargo.toml | 4 ++-- cap-time-ext/Cargo.toml | 6 +++--- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b5a877fbc..2eb3e087a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-std-workspace" -version = "1.0.7" +version = "1.0.8" description = "Capability-based version of the Rust standard library" authors = [ "Dan Gohman ", @@ -20,11 +20,11 @@ rust-version = "1.56" #async-std = { version = "1.10.0", features = ["attributes"] } anyhow = "1.0.37" #cap-async-std = { path = "cap-async-std", version = "^0.25.0" } -cap-fs-ext = { path = "cap-fs-ext", version = "^1.0.7" } -cap-directories = { path = "cap-directories", version = "^1.0.7" } -cap-std = { path = "cap-std", version = "^1.0.7" } -cap-tempfile = { path = "cap-tempfile", version = "^1.0.7" } -cap-rand = { path = "cap-rand", version = "^1.0.7" } +cap-fs-ext = { path = "cap-fs-ext", version = "^1.0.8" } +cap-directories = { path = "cap-directories", version = "^1.0.8" } +cap-std = { path = "cap-std", version = "^1.0.8" } +cap-tempfile = { path = "cap-tempfile", version = "^1.0.8" } +cap-rand = { path = "cap-rand", version = "^1.0.8" } rand = "0.8.1" tempfile = "3.1.0" camino = "1.0.5" diff --git a/cap-directories/Cargo.toml b/cap-directories/Cargo.toml index 5ae06e515..c2613e771 100644 --- a/cap-directories/Cargo.toml +++ b/cap-directories/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-directories" -version = "1.0.7" +version = "1.0.8" description = "Capability-based standard directories for config, cache and other data" authors = [ "Dan Gohman ", @@ -13,7 +13,7 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-std = { path = "../cap-std", version = "^1.0.7" } +cap-std = { path = "../cap-std", version = "^1.0.8" } directories-next = "2.0.0" [target.'cfg(not(windows))'.dependencies] diff --git a/cap-fs-ext/Cargo.toml b/cap-fs-ext/Cargo.toml index 77fa014e7..6fcad6620 100644 --- a/cap-fs-ext/Cargo.toml +++ b/cap-fs-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-fs-ext" -version = "1.0.7" +version = "1.0.8" description = "Extension traits for `Dir`, `File`, etc." authors = [ "Dan Gohman ", @@ -15,8 +15,8 @@ edition = "2018" [dependencies] arf-strings = { version = "0.7.0", optional = true } #cap-async-std = { path = "../cap-async-std", optional = true, version = "^0.25.0" } -cap-std = { path = "../cap-std", optional = true, version = "^1.0.7" } -cap-primitives = { path = "../cap-primitives", version = "^1.0.7" } +cap-std = { path = "../cap-std", optional = true, version = "^1.0.8" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.8" } io-lifetimes = { version = "1.0.0", default-features = false } # Enable "unstable" for `spawn_blocking`. #async-std = { version = "1.10.0", features = ["attributes", "unstable"], optional = true } diff --git a/cap-primitives/Cargo.toml b/cap-primitives/Cargo.toml index e729dbbfc..8698d3c4e 100644 --- a/cap-primitives/Cargo.toml +++ b/cap-primitives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-primitives" -version = "1.0.7" +version = "1.0.8" description = "Capability-based primitives" authors = [ "Dan Gohman ", diff --git a/cap-rand/Cargo.toml b/cap-rand/Cargo.toml index 4645d7766..36a325321 100644 --- a/cap-rand/Cargo.toml +++ b/cap-rand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-rand" -version = "1.0.7" +version = "1.0.8" description = "Capability-based random number generators" authors = [ "Dan Gohman ", diff --git a/cap-std/Cargo.toml b/cap-std/Cargo.toml index 48dc3b3d9..0ac96d790 100644 --- a/cap-std/Cargo.toml +++ b/cap-std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-std" -version = "1.0.7" +version = "1.0.8" description = "Capability-based version of the Rust standard library" authors = [ "Dan Gohman ", @@ -18,7 +18,7 @@ rustdoc-args = ["--cfg=doc_cfg"] [dependencies] arf-strings = { version = "0.7.0", optional = true } -cap-primitives = { path = "../cap-primitives", version = "^1.0.7" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.8" } io-extras = "0.17.0" io-lifetimes = { version = "1.0.0", default-features = false } camino = { version = "1.0.5", optional = true } diff --git a/cap-tempfile/Cargo.toml b/cap-tempfile/Cargo.toml index 134e321cb..99f331bf5 100644 --- a/cap-tempfile/Cargo.toml +++ b/cap-tempfile/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-tempfile" -version = "1.0.7" +version = "1.0.8" description = "Capability-based temporary directories" authors = [ "Dan Gohman ", @@ -13,7 +13,7 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-std = { path = "../cap-std", version = "^1.0.7" } +cap-std = { path = "../cap-std", version = "^1.0.8" } uuid = { version = "1.0.0", features = ["v4"] } camino = { version = "1.0.5", optional = true } diff --git a/cap-time-ext/Cargo.toml b/cap-time-ext/Cargo.toml index c5fe5218e..58eea63c0 100644 --- a/cap-time-ext/Cargo.toml +++ b/cap-time-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-time-ext" -version = "1.0.7" +version = "1.0.8" description = "Extension traits for `SystemClock` and `MonotonicClock`" authors = [ "Dan Gohman ", @@ -13,8 +13,8 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-primitives = { path = "../cap-primitives", version = "^1.0.7" } -cap-std = { path = "../cap-std", optional = true, version = "^1.0.7" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.8" } +cap-std = { path = "../cap-std", optional = true, version = "^1.0.8" } [target.'cfg(not(windows))'.dependencies] rustix = { version = "0.36.0", features = ["time"] } From 6cea7d04385999a021151c850c95c6d1c3783771 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 6 Mar 2023 10:26:41 -0800 Subject: [PATCH 14/15] Update to rustix 0.37. (#297) The only code change here is due to `copy_file_range`'s `len` argument changing from `u64` to `usize`. --- Cargo.toml | 2 +- cap-async-std/Cargo.toml | 2 +- cap-directories/Cargo.toml | 2 +- cap-primitives/Cargo.toml | 4 ++-- cap-primitives/src/rustix/fs/copy_impl.rs | 7 +++++++ cap-std/Cargo.toml | 2 +- cap-tempfile/Cargo.toml | 2 +- cap-time-ext/Cargo.toml | 2 +- 8 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2eb3e087a..9e0978844 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ libc = "0.2.100" io-lifetimes = "1.0.0" [target.'cfg(not(windows))'.dev-dependencies] -rustix = { version = "0.36.0", features = ["fs"] } +rustix = { version = "0.37.0", features = ["fs"] } [target.'cfg(windows)'.dev-dependencies] # nt_version uses internal Windows APIs, however we're only using it diff --git a/cap-async-std/Cargo.toml b/cap-async-std/Cargo.toml index ec1396959..90048a67a 100644 --- a/cap-async-std/Cargo.toml +++ b/cap-async-std/Cargo.toml @@ -23,7 +23,7 @@ io-extras = { version = "0.17.0", features = ["use_async_std"] } camino = { version = "1.0.5", optional = true } [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.36.0", features = ["fs"] } +rustix = { version = "0.37.0", features = ["fs"] } [features] default = [] diff --git a/cap-directories/Cargo.toml b/cap-directories/Cargo.toml index c2613e771..788266067 100644 --- a/cap-directories/Cargo.toml +++ b/cap-directories/Cargo.toml @@ -17,7 +17,7 @@ cap-std = { path = "../cap-std", version = "^1.0.8" } directories-next = "2.0.0" [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.36.0" } +rustix = { version = "0.37.0" } [target.'cfg(windows)'.dependencies.windows-sys] version = "0.45.0" diff --git a/cap-primitives/Cargo.toml b/cap-primitives/Cargo.toml index 8698d3c4e..634a3a670 100644 --- a/cap-primitives/Cargo.toml +++ b/cap-primitives/Cargo.toml @@ -17,7 +17,7 @@ ambient-authority = "0.0.1" arbitrary = { version = "1.0.0", optional = true, features = ["derive"] } ipnet = "2.3.0" maybe-owned = "0.3.4" -fs-set-times = "0.18.0" +fs-set-times = "0.19.0" io-extras = "0.17.0" io-lifetimes = { version = "1.0.0", default-features = false } @@ -25,7 +25,7 @@ io-lifetimes = { version = "1.0.0", default-features = false } cap-tempfile = { path = "../cap-tempfile" } [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.36.0", features = ["fs", "process", "procfs", "termios", "time"] } +rustix = { version = "0.37.0", features = ["fs", "process", "procfs", "termios", "time"] } [target.'cfg(windows)'.dependencies] winx = "0.35.0" diff --git a/cap-primitives/src/rustix/fs/copy_impl.rs b/cap-primitives/src/rustix/fs/copy_impl.rs index 1b661ae2d..6723fd1a2 100644 --- a/cap-primitives/src/rustix/fs/copy_impl.rs +++ b/cap-primitives/src/rustix/fs/copy_impl.rs @@ -10,6 +10,8 @@ use rustix::fs::{ copyfile_state_alloc, copyfile_state_free, copyfile_state_get_copied, copyfile_state_t, fclonefileat, fcopyfile, CloneFlags, CopyfileFlags, }; +#[cfg(any(target_os = "android", target_os = "linux"))] +use std::convert::TryFrom; use std::path::Path; use std::{fs, io}; @@ -115,6 +117,11 @@ pub(crate) fn copy_impl( while written < len { let copy_result = if has_copy_file_range { let bytes_to_copy = cmp::min(len - written, usize::MAX as u64); + + // `copy_file_range` takes a `usize`; convert with saturation so + // that we copy as many bytes as we can. + let bytes_to_copy = usize::try_from(bytes_to_copy).unwrap_or(usize::MAX); + // We actually don't have to adjust the offsets, // because copy_file_range adjusts the file offset automatically let copy_result = copy_file_range(&reader, None, &writer, None, bytes_to_copy); diff --git a/cap-std/Cargo.toml b/cap-std/Cargo.toml index 0ac96d790..371fbb111 100644 --- a/cap-std/Cargo.toml +++ b/cap-std/Cargo.toml @@ -24,7 +24,7 @@ io-lifetimes = { version = "1.0.0", default-features = false } camino = { version = "1.0.5", optional = true } [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.36.0", features = ["fs"] } +rustix = { version = "0.37.0", features = ["fs"] } [features] default = [] diff --git a/cap-tempfile/Cargo.toml b/cap-tempfile/Cargo.toml index 99f331bf5..2587c9a1d 100644 --- a/cap-tempfile/Cargo.toml +++ b/cap-tempfile/Cargo.toml @@ -21,7 +21,7 @@ camino = { version = "1.0.5", optional = true } rand = "0.8.1" [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.36.0", features = ["procfs"] } +rustix = { version = "0.37.0", features = ["procfs"] } [target.'cfg(windows)'.dev-dependencies.windows-sys] version = "0.45.0" diff --git a/cap-time-ext/Cargo.toml b/cap-time-ext/Cargo.toml index 58eea63c0..f8e3abce9 100644 --- a/cap-time-ext/Cargo.toml +++ b/cap-time-ext/Cargo.toml @@ -17,7 +17,7 @@ cap-primitives = { path = "../cap-primitives", version = "^1.0.8" } cap-std = { path = "../cap-std", optional = true, version = "^1.0.8" } [target.'cfg(not(windows))'.dependencies] -rustix = { version = "0.36.0", features = ["time"] } +rustix = { version = "0.37.0", features = ["time"] } [target.'cfg(windows)'.dependencies] once_cell = "1.5.2" From 9e6e17ec1d051f76002c5e02f0ec73cb38b144ca Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 28 Mar 2023 21:37:13 -0700 Subject: [PATCH 15/15] chore: Release --- Cargo.toml | 12 ++++++------ cap-directories/Cargo.toml | 4 ++-- cap-fs-ext/Cargo.toml | 6 +++--- cap-primitives/Cargo.toml | 2 +- cap-rand/Cargo.toml | 2 +- cap-std/Cargo.toml | 4 ++-- cap-tempfile/Cargo.toml | 4 ++-- cap-time-ext/Cargo.toml | 6 +++--- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e0978844..1916f0bdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-std-workspace" -version = "1.0.8" +version = "1.0.9" description = "Capability-based version of the Rust standard library" authors = [ "Dan Gohman ", @@ -20,11 +20,11 @@ rust-version = "1.56" #async-std = { version = "1.10.0", features = ["attributes"] } anyhow = "1.0.37" #cap-async-std = { path = "cap-async-std", version = "^0.25.0" } -cap-fs-ext = { path = "cap-fs-ext", version = "^1.0.8" } -cap-directories = { path = "cap-directories", version = "^1.0.8" } -cap-std = { path = "cap-std", version = "^1.0.8" } -cap-tempfile = { path = "cap-tempfile", version = "^1.0.8" } -cap-rand = { path = "cap-rand", version = "^1.0.8" } +cap-fs-ext = { path = "cap-fs-ext", version = "^1.0.9" } +cap-directories = { path = "cap-directories", version = "^1.0.9" } +cap-std = { path = "cap-std", version = "^1.0.9" } +cap-tempfile = { path = "cap-tempfile", version = "^1.0.9" } +cap-rand = { path = "cap-rand", version = "^1.0.9" } rand = "0.8.1" tempfile = "3.1.0" camino = "1.0.5" diff --git a/cap-directories/Cargo.toml b/cap-directories/Cargo.toml index 788266067..e3a3804a1 100644 --- a/cap-directories/Cargo.toml +++ b/cap-directories/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-directories" -version = "1.0.8" +version = "1.0.9" description = "Capability-based standard directories for config, cache and other data" authors = [ "Dan Gohman ", @@ -13,7 +13,7 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-std = { path = "../cap-std", version = "^1.0.8" } +cap-std = { path = "../cap-std", version = "^1.0.9" } directories-next = "2.0.0" [target.'cfg(not(windows))'.dependencies] diff --git a/cap-fs-ext/Cargo.toml b/cap-fs-ext/Cargo.toml index 6fcad6620..b42fa7718 100644 --- a/cap-fs-ext/Cargo.toml +++ b/cap-fs-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-fs-ext" -version = "1.0.8" +version = "1.0.9" description = "Extension traits for `Dir`, `File`, etc." authors = [ "Dan Gohman ", @@ -15,8 +15,8 @@ edition = "2018" [dependencies] arf-strings = { version = "0.7.0", optional = true } #cap-async-std = { path = "../cap-async-std", optional = true, version = "^0.25.0" } -cap-std = { path = "../cap-std", optional = true, version = "^1.0.8" } -cap-primitives = { path = "../cap-primitives", version = "^1.0.8" } +cap-std = { path = "../cap-std", optional = true, version = "^1.0.9" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.9" } io-lifetimes = { version = "1.0.0", default-features = false } # Enable "unstable" for `spawn_blocking`. #async-std = { version = "1.10.0", features = ["attributes", "unstable"], optional = true } diff --git a/cap-primitives/Cargo.toml b/cap-primitives/Cargo.toml index 634a3a670..80f11559b 100644 --- a/cap-primitives/Cargo.toml +++ b/cap-primitives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-primitives" -version = "1.0.8" +version = "1.0.9" description = "Capability-based primitives" authors = [ "Dan Gohman ", diff --git a/cap-rand/Cargo.toml b/cap-rand/Cargo.toml index 36a325321..61f6e377e 100644 --- a/cap-rand/Cargo.toml +++ b/cap-rand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-rand" -version = "1.0.8" +version = "1.0.9" description = "Capability-based random number generators" authors = [ "Dan Gohman ", diff --git a/cap-std/Cargo.toml b/cap-std/Cargo.toml index 371fbb111..1260ea1f0 100644 --- a/cap-std/Cargo.toml +++ b/cap-std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-std" -version = "1.0.8" +version = "1.0.9" description = "Capability-based version of the Rust standard library" authors = [ "Dan Gohman ", @@ -18,7 +18,7 @@ rustdoc-args = ["--cfg=doc_cfg"] [dependencies] arf-strings = { version = "0.7.0", optional = true } -cap-primitives = { path = "../cap-primitives", version = "^1.0.8" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.9" } io-extras = "0.17.0" io-lifetimes = { version = "1.0.0", default-features = false } camino = { version = "1.0.5", optional = true } diff --git a/cap-tempfile/Cargo.toml b/cap-tempfile/Cargo.toml index 2587c9a1d..106a2cb00 100644 --- a/cap-tempfile/Cargo.toml +++ b/cap-tempfile/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-tempfile" -version = "1.0.8" +version = "1.0.9" description = "Capability-based temporary directories" authors = [ "Dan Gohman ", @@ -13,7 +13,7 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-std = { path = "../cap-std", version = "^1.0.8" } +cap-std = { path = "../cap-std", version = "^1.0.9" } uuid = { version = "1.0.0", features = ["v4"] } camino = { version = "1.0.5", optional = true } diff --git a/cap-time-ext/Cargo.toml b/cap-time-ext/Cargo.toml index f8e3abce9..864893a93 100644 --- a/cap-time-ext/Cargo.toml +++ b/cap-time-ext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-time-ext" -version = "1.0.8" +version = "1.0.9" description = "Extension traits for `SystemClock` and `MonotonicClock`" authors = [ "Dan Gohman ", @@ -13,8 +13,8 @@ repository = "https://github.com/bytecodealliance/cap-std" edition = "2018" [dependencies] -cap-primitives = { path = "../cap-primitives", version = "^1.0.8" } -cap-std = { path = "../cap-std", optional = true, version = "^1.0.8" } +cap-primitives = { path = "../cap-primitives", version = "^1.0.9" } +cap-std = { path = "../cap-std", optional = true, version = "^1.0.9" } [target.'cfg(not(windows))'.dependencies] rustix = { version = "0.37.0", features = ["time"] }