From 56f4e809fd7043b1451defee203cbee776c085d5 Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Thu, 29 Jul 2021 21:36:13 +0800 Subject: [PATCH 001/131] Silently accepts ---presume-input-tty For whatever reason, the following is equivalent, cargo run -- rm --presume-input-tty cargo run -- rm ---presume-input-tty cargo run -- rm -----presume-input-tty cargo run -- rm ---------presume-input-tty Signed-off-by: Hanif Bin Ariffin --- src/uu/rm/src/rm.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 259d1ab39..1fdc59371 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -49,6 +49,7 @@ static OPT_PROMPT_MORE: &str = "prompt-more"; static OPT_RECURSIVE: &str = "recursive"; static OPT_RECURSIVE_R: &str = "recursive_R"; static OPT_VERBOSE: &str = "verbose"; +static PRESUME_INPUT_TTY: &str = "presume-input-tty"; static ARG_FILES: &str = "files"; @@ -206,6 +207,17 @@ pub fn uu_app() -> App<'static, 'static> { .long(OPT_VERBOSE) .help("explain what is being done") ) + // From the GNU source code: + // This is solely for testing. + // Do not document. + // It is relatively difficult to ensure that there is a tty on stdin. + // Since rm acts differently depending on that, without this option, + // it'd be harder to test the parts of rm that depend on that setting. + .arg( + Arg::with_name(PRESUME_INPUT_TTY) + .long(PRESUME_INPUT_TTY) + .hidden(true) + ) .arg( Arg::with_name(ARG_FILES) .multiple(true) From 6fec0bfe246675cc50e843a3154198ef679b3f7c Mon Sep 17 00:00:00 2001 From: Andreas Hartmann Date: Mon, 23 Aug 2021 11:37:25 +0200 Subject: [PATCH 002/131] macros: Add documentation with examples to macros --- src/uucore/src/lib/macros.rs | 244 +++++++++++++++++++++++++++++++++-- 1 file changed, 233 insertions(+), 11 deletions(-) diff --git a/src/uucore/src/lib/macros.rs b/src/uucore/src/lib/macros.rs index c7b15dba3..275b0afe7 100644 --- a/src/uucore/src/lib/macros.rs +++ b/src/uucore/src/lib/macros.rs @@ -1,3 +1,37 @@ +//! Macros for the uucore utilities. +//! +//! This module bundles all macros used across the uucore utilities. These +//! include macros for reporting errors in various formats, aborting program +//! execution and more. +//! +//! To make use of all macros in this module, they must be imported like so: +//! +//! ```ignore +//! #[macro_use] +//! extern crate uucore; +//! ``` +//! +//! Alternatively, you can import single macros by importing them through their +//! fully qualified name like this: +//! +//! ```no_run +//! use uucore::{show, crash}; +//! ``` +//! +//! Here's an overview of the macros sorted by purpose +//! +//! - Print errors +//! - From types implementing [`crate::error::UError`]: [`show!`], +//! [`show_if_err!`] +//! - From custom messages: [`show_error!`], [`show_usage_error!`] +//! - Print warnings: [`show_warning!`] +//! - Terminate util execution +//! - Terminate regularly: [`exit!`], [`return_if_err!`] +//! - Crash program: [`crash!`], [`crash_if_err!`], [`safe_unwrap!`] +//! - Unwrapping result types: [`safe_unwrap!`] + +// spell-checker:ignore sourcepath targetpath + use std::sync::atomic::AtomicBool; // This file is part of the uutils coreutils package. @@ -12,6 +46,45 @@ pub static UTILITY_IS_SECOND_ARG: AtomicBool = AtomicBool::new(false); //==== +/// Display a [`crate::error::UError`] and set global exit code. +/// +/// Prints the error message contained in an [`crate::error::UError`] to stderr +/// and sets the exit code through [`crate::error::set_exit_code`]. The printed +/// error message is prepended with the calling utility's name. A call to this +/// macro will not finish program execution. +/// +/// # Examples +/// +/// The following example would print a message "Some error occurred" and set +/// the utility's exit code to 2. +/// +/// ``` +/// # #[macro_use] +/// # extern crate uucore; +/// +/// use uucore::error::{self, USimpleError}; +/// +/// fn main() { +/// let err = USimpleError::new(2, "Some error occurred."); +/// show!(err); +/// assert_eq!(error::get_exit_code(), 2); +/// } +/// ``` +/// +/// If not using [`crate::error::UError`], one may achieve the same behavior +/// like this: +/// +/// ``` +/// # #[macro_use] +/// # extern crate uucore; +/// +/// use uucore::error::set_exit_code; +/// +/// fn main() { +/// set_exit_code(2); +/// show_error!("Some error occurred."); +/// } +/// ``` #[macro_export] macro_rules! show( ($err:expr) => ({ @@ -21,6 +94,36 @@ macro_rules! show( }) ); +/// Display an error and set global exit code in error case. +/// +/// Wraps around [`show!`] and takes a [`crate::error::UResult`] instead of a +/// [`crate::error::UError`] type. This macro invokes [`show!`] if the +/// [`crate::error::UResult`] is an `Err`-variant. This can be invoked directly +/// on the result of a function call, like in the `install` utility: +/// +/// ```ignore +/// show_if_err!(copy(sourcepath, &targetpath, b)); +/// ``` +/// +/// # Examples +/// +/// ```ignore +/// # #[macro_use] +/// # extern crate uucore; +/// # use uucore::error::{UError, UIoError, UResult, USimpleError}; +/// +/// # fn main() { +/// let is_ok = Ok(1); +/// // This does nothing at all +/// show_if_err!(is_ok); +/// +/// let is_err = Err(USimpleError::new(1, "I'm an error").into()); +/// // Calls `show!` on the contained USimpleError +/// show_if_err!(is_err); +/// # } +/// ``` +/// +/// #[macro_export] macro_rules! show_if_err( ($res:expr) => ({ @@ -31,6 +134,19 @@ macro_rules! show_if_err( ); /// Show an error to stderr in a similar style to GNU coreutils. +/// +/// Takes a [`format!`]-like input and prints it to stderr. The output is +/// prepended with the current utility's name. +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] +/// # extern crate uucore; +/// # fn main() { +/// show_error!("Couldn't apply {} to {}", "foo", "bar"); +/// # } +/// ``` #[macro_export] macro_rules! show_error( ($($args:tt)+) => ({ @@ -40,6 +156,17 @@ macro_rules! show_error( ); /// Show a warning to stderr in a similar style to GNU coreutils. +/// +/// Is this really required? Used in the following locations: +/// +/// ./src/uu/head/src/head.rs:12 +/// ./src/uu/head/src/head.rs:424 +/// ./src/uu/head/src/head.rs:427 +/// ./src/uu/head/src/head.rs:430 +/// ./src/uu/head/src/head.rs:453 +/// ./src/uu/du/src/du.rs:339 +/// ./src/uu/wc/src/wc.rs:270 +/// ./src/uu/wc/src/wc.rs:273 #[macro_export] macro_rules! show_error_custom_description ( ($err:expr,$($args:tt)+) => ({ @@ -48,6 +175,21 @@ macro_rules! show_error_custom_description ( }) ); +/// Print a warning message to stderr. +/// +/// Takes [`format!`]-compatible input and prepends it with the current +/// utility's name and "warning: " before printing to stderr. +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] +/// # extern crate uucore; +/// # fn main() { +/// // outputs : warning: Couldn't apply foo to bar +/// show_warning!("Couldn't apply {} to {}", "foo", "bar"); +/// # } +/// ``` #[macro_export] macro_rules! show_warning( ($($args:tt)+) => ({ @@ -57,6 +199,21 @@ macro_rules! show_warning( ); /// Show a bad invocation help message in a similar style to GNU coreutils. +/// +/// Takes a [`format!`]-compatible input and prepends it with the current +/// utility's name before printing to stderr. +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] +/// # extern crate uucore; +/// # fn main() { +/// // outputs : Couldn't apply foo to bar +/// // Try ' --help' for more information. +/// show_usage_error!("Couldn't apply {} to {}", "foo", "bar"); +/// # } +/// ``` #[macro_export] macro_rules! show_usage_error( ($($args:tt)+) => ({ @@ -68,7 +225,9 @@ macro_rules! show_usage_error( //==== -/// Calls `exit()` with the provided exit code. +/// Calls [`std::process::exit`] with the provided exit code. +/// +/// Why not call exit directly? #[macro_export] macro_rules! exit( ($exit_code:expr) => ({ @@ -76,7 +235,22 @@ macro_rules! exit( }) ); -/// Display the provided error message, then `exit()` with the provided exit code +/// Display an error and [`exit!`] +/// +/// Displays the provided error message using [`show_error!`], then invokes +/// [`exit!`] with the provided exit code. +/// +/// # Examples +/// +/// ```should_panic +/// # #[macro_use] +/// # extern crate uucore; +/// # fn main() { +/// // outputs : Couldn't apply foo to bar +/// // and terminates execution +/// crash!(1, "Couldn't apply {} to {}", "foo", "bar"); +/// # } +/// ``` #[macro_export] macro_rules! crash( ($exit_code:expr, $($args:tt)+) => ({ @@ -85,8 +259,26 @@ macro_rules! crash( }) ); -/// Unwraps the Result. Instead of panicking, it exists the program with the -/// provided exit code. +/// Unwrap a [`std::result::Result`], crashing instead of panicking. +/// +/// If the result is an `Ok`-variant, returns the value contained inside. If it +/// is an `Err`-variant, invokes [`crash!`] with the formatted error instead. +/// +/// # Examples +/// +/// ```should_panic +/// # #[macro_use] +/// # extern crate uucore; +/// # fn main() { +/// let is_ok: Result = Ok(1); +/// // Does nothing +/// crash_if_err!(1, is_ok); +/// +/// let is_err: Result = Err("This didn't work..."); +/// // Calls `crash!` +/// crash_if_err!(1, is_err); +/// # } +/// ``` #[macro_export] macro_rules! crash_if_err( ($exit_code:expr, $exp:expr) => ( @@ -97,18 +289,42 @@ macro_rules! crash_if_err( ) ); -//==== - +/// Unwrap some Result, crashing instead of panicking. +/// +/// Drop this in favor of `crash_if_err!` #[macro_export] -macro_rules! safe_write( - ($fd:expr, $($args:tt)+) => ( - match write!($fd, $($args)+) { - Ok(_) => {} - Err(f) => panic!("{}", f) +macro_rules! safe_unwrap( + ($exp:expr) => ( + match $exp { + Ok(m) => m, + Err(f) => $crate::crash!(1, "{}", f.to_string()) } ) ); +//==== + +/// Unwraps the Result. Instead of panicking, it shows the error and then +/// returns from the function with the provided exit code. +/// Assumes the current function returns an i32 value. +/// +/// Replace with `crash_if_err`? +#[macro_export] +macro_rules! return_if_err( + ($exit_code:expr, $exp:expr) => ( + match $exp { + Ok(m) => m, + Err(f) => { + $crate::show_error!("{}", f); + return $exit_code; + } + } + ) +); + +//==== + +/// This is used exclusively by du... #[macro_export] macro_rules! safe_writeln( ($fd:expr, $($args:tt)+) => ( @@ -123,6 +339,7 @@ macro_rules! safe_writeln( //-- message templates : (join utility sub-macros) +// used only by "cut" #[macro_export] macro_rules! snippet_list_join_oxford_comma { ($conjunction:expr, $valOne:expr, $valTwo:expr) => ( @@ -133,6 +350,7 @@ macro_rules! snippet_list_join_oxford_comma { ); } +// used only by "cut" #[macro_export] macro_rules! snippet_list_join { ($conjunction:expr, $valOne:expr, $valTwo:expr) => ( @@ -167,6 +385,7 @@ macro_rules! msg_invalid_opt_use { }; } +// Only used by "cut" #[macro_export] macro_rules! msg_opt_only_usable_if { ($clause:expr, $flag:expr) => { @@ -181,6 +400,7 @@ macro_rules! msg_opt_only_usable_if { }; } +// Used only by "cut" #[macro_export] macro_rules! msg_opt_invalid_should_be { ($expects:expr, $received:expr, $flag:expr) => { @@ -200,6 +420,7 @@ macro_rules! msg_opt_invalid_should_be { // -- message templates : invalid input : input combinations +// UNUSED! #[macro_export] macro_rules! msg_expects_one_of { ($valOne:expr $(, $remaining_values:expr)*) => ( @@ -207,6 +428,7 @@ macro_rules! msg_expects_one_of { ); } +// Used only by "cut" #[macro_export] macro_rules! msg_expects_no_more_than_one_of { ($valOne:expr $(, $remaining_values:expr)*) => ( From fe3645d4d5c34ef830ef04edc5f5fe50edcdc472 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Fri, 10 Sep 2021 21:33:34 +0200 Subject: [PATCH 003/131] ls: add support for showing SELinux context (--context/-Z) --- Cargo.lock | 1 + Cargo.toml | 2 +- src/uu/ls/Cargo.toml | 4 ++ src/uu/ls/src/ls.rs | 164 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 149 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 704d1eea1..ea3467b5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2632,6 +2632,7 @@ dependencies = [ "lscolors", "number_prefix", "once_cell", + "selinux", "term_grid", "termsize", "unicode-width", diff --git a/Cargo.toml b/Cargo.toml index 3a2c5f12a..8d2f2da58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -147,7 +147,7 @@ feat_os_unix_musl = [ # NOTE: # The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time. # Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time. -feat_selinux = ["cp/selinux", "id/selinux", "selinux", "feat_require_selinux"] +feat_selinux = ["cp/selinux", "id/selinux", "ls/selinux", "selinux", "feat_require_selinux"] # "feat_acl" == set of utilities providing support for acl (access control lists) if enabled with `--features feat_acl`. # NOTE: # On linux, the posix-acl/acl-sys crate requires `libacl` headers and shared library to be accessible in the C toolchain at compile time. diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index dbe6bacaa..62dcd59a4 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -28,6 +28,7 @@ uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", featu uucore_procs = { version=">=0.0.6", package = "uucore_procs", path = "../../uucore_procs" } once_cell = "1.7.2" atty = "0.2" +selinux = { version="0.2.1", optional = true } [target.'cfg(unix)'.dependencies] lazy_static = "1.4.0" @@ -35,3 +36,6 @@ lazy_static = "1.4.0" [[bin]] name = "ls" path = "src/main.rs" + +[features] +feat_selinux = ["selinux"] diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 6f63c2a4a..7b7fd873e 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -47,6 +47,11 @@ use unicode_width::UnicodeWidthStr; use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR}; use uucore::{fs::display_permissions, version_cmp::version_cmp}; +#[cfg(not(feature = "selinux"))] +static CONTEXT_HELP_TEXT: &str = "print any security context of each file (not enabled)"; +#[cfg(feature = "selinux")] +static CONTEXT_HELP_TEXT: &str = "print any security context of each file"; + fn usage() -> String { format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase()) } @@ -126,6 +131,7 @@ pub mod options { pub static FULL_TIME: &str = "full-time"; pub static HIDE: &str = "hide"; pub static IGNORE: &str = "ignore"; + pub static CONTEXT: &str = "context"; } const DEFAULT_TERM_WIDTH: u16 = 80; @@ -236,6 +242,8 @@ struct Config { quoting_style: QuotingStyle, indicator_style: IndicatorStyle, time_style: TimeStyle, + context: bool, + selinux_supported: bool, } // Fields that can be removed or added to the long format @@ -247,9 +255,18 @@ struct LongFormat { numeric_uid_gid: bool, } +struct PaddingCollection { + longest_link_count_len: usize, + longest_uname_len: usize, + longest_group_len: usize, + longest_context_len: usize, + longest_size_len: usize, +} + impl Config { #[allow(clippy::cognitive_complexity)] fn from(options: &clap::ArgMatches) -> UResult { + let context = options.is_present(options::CONTEXT); let (mut format, opt) = if let Some(format_) = options.value_of(options::FORMAT) { ( match format_ { @@ -593,6 +610,17 @@ impl Config { quoting_style, indicator_style, time_style, + context, + selinux_supported: { + #[cfg(feature = "selinux")] + { + selinux::kernel_support() != selinux::KernelSupport::Unsupported + } + #[cfg(not(feature = "selinux"))] + { + false + } + }, }) } } @@ -611,6 +639,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .map(|v| v.map(Path::new).collect()) .unwrap_or_else(|| vec![Path::new(".")]); + if config.context && !config.selinux_supported { + show_warning!("--context (-Z) works only on an SELinux-enabled kernel",); + } + list(locs, config) } @@ -1154,6 +1186,12 @@ only ignore '.' and '..'.", .overrides_with(options::FULL_TIME) .help("like -l --time-style=full-iso"), ) + .arg( + Arg::with_name(options::CONTEXT) + .short("Z") + .long(options::CONTEXT) + .help(CONTEXT_HELP_TEXT), + ) // Positional arguments .arg( Arg::with_name(options::PATHS) @@ -1178,6 +1216,7 @@ struct PathData { // PathBuf that all above data corresponds to p_buf: PathBuf, must_dereference: bool, + security_context: String, } impl PathData { @@ -1221,12 +1260,39 @@ impl PathData { None => OnceCell::new(), }; + let security_context: String = if config.context { + if config.selinux_supported { + #[cfg(feature = "selinux")] + { + if let Ok(Some(context)) = + selinux::SecurityContext::of_path(&p_buf, must_dereference, false) + { + String::from_utf8_lossy(context.as_bytes()) + .trim_end_matches(char::from(0)) + .to_string() + } else { + // TODO: print warning with error to stderr + "?".to_string() + } + } + #[cfg(not(feature = "selinux"))] + { + "?".to_string() + } + } else { + "?".to_string() + } + } else { + String::new() + }; + Self { md: OnceCell::new(), ft, display_name, p_buf, must_dereference, + security_context, } } @@ -1390,7 +1456,7 @@ fn get_metadata(entry: &Path, dereference: bool) -> std::io::Result { } fn display_dir_entry_size(entry: &PathData, config: &Config) -> (usize, usize, usize, usize) { - // TODO: Cache/memoize the display_* results so we don't have to recalculate them. + // TODO: Cache/memorize the display_* results so we don't have to recalculate them. if let Some(md) = entry.md() { ( display_symlink_count(md).len(), @@ -1403,31 +1469,40 @@ fn display_dir_entry_size(entry: &PathData, config: &Config) -> (usize, usize, u } } -fn pad_left(string: String, count: usize) -> String { +fn pad_left(string: &str, count: usize) -> String { format!("{:>width$}", string, width = count) } -fn pad_right(string: String, count: usize) -> String { +fn pad_right(string: &str, count: usize) -> String { format!("{:) { + // `-Z`, `--context`: + // Display the SELinux security context or '?' if none is found. When used with the `-l` + // option, print the security context to the left of the size column. + if config.format == Format::Long { let ( mut longest_link_count_len, mut longest_uname_len, mut longest_group_len, + mut longest_context_len, mut longest_size_len, - ) = (1, 1, 1, 1); + ) = (1, 1, 1, 1, 1); let mut total_size = 0; for item in items { + let context_len = item.security_context.len(); let (link_count_len, uname_len, group_len, size_len) = display_dir_entry_size(item, config); longest_link_count_len = link_count_len.max(longest_link_count_len); longest_size_len = size_len.max(longest_size_len); longest_uname_len = uname_len.max(longest_uname_len); longest_group_len = group_len.max(longest_group_len); + if config.context { + longest_context_len = context_len.max(longest_context_len); + } longest_size_len = size_len.max(longest_size_len); total_size += item.md().map_or(0, |md| get_block_size(md, config)); } @@ -1439,16 +1514,31 @@ fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter display_grid(names, config.width, Direction::TopToBottom, out), @@ -1573,15 +1663,13 @@ fn display_grid( /// longest_link_count_len: usize, /// longest_uname_len: usize, /// longest_group_len: usize, +/// longest_context_len: usize, /// longest_size_len: usize, /// ``` /// that decide the maximum possible character count of each field. fn display_item_long( item: &PathData, - longest_link_count_len: usize, - longest_uname_len: usize, - longest_group_len: usize, - longest_size_len: usize, + padding: PaddingCollection, config: &Config, out: &mut BufWriter, ) { @@ -1602,16 +1690,23 @@ fn display_item_long( let _ = write!( out, - "{} {}", + "{}{} {}", display_permissions(md, true), - pad_left(display_symlink_count(md), longest_link_count_len), + if item.security_context != "?" { + // GNU `ls` uses a "." character to indicate a file with a security context, but not + // other alternate access method. + "." + } else { + "" + }, + pad_left(&display_symlink_count(md), padding.longest_link_count_len), ); if config.long.owner { let _ = write!( out, " {}", - pad_right(display_uname(md, config), longest_uname_len) + pad_right(&display_uname(md, config), padding.longest_uname_len) ); } @@ -1619,7 +1714,15 @@ fn display_item_long( let _ = write!( out, " {}", - pad_right(display_group(md, config), longest_group_len) + pad_right(&display_group(md, config), padding.longest_group_len) + ); + } + + if config.context { + let _ = write!( + out, + " {}", + pad_right(&item.security_context, padding.longest_context_len) ); } @@ -1629,19 +1732,19 @@ fn display_item_long( let _ = write!( out, " {}", - pad_right(display_uname(md, config), longest_uname_len) + pad_right(&display_uname(md, config), padding.longest_uname_len) ); } let _ = writeln!( out, " {} {} {}", - pad_left(display_size_or_rdev(md, config), longest_size_len), + pad_left(&display_size_or_rdev(md, config), padding.longest_size_len), display_date(md, config), // unwrap is fine because it fails when metadata is not available // but we already know that it is because it's checked at the // start of the function. - display_file_name(item, config).unwrap().contents, + display_file_name(item, config, None).unwrap().contents, ); } @@ -1865,10 +1968,15 @@ fn classify_file(path: &PathData) -> Option { /// * `config.indicator_style` to append specific characters to `name` using [`classify_file`]. /// * `config.format` to display symlink targets if `Format::Long`. This function is also /// responsible for coloring symlink target names if `config.color` is specified. +/// * `config.context` to prepend security context to `name` if compiled with `feat_selinux`. /// /// Note that non-unicode sequences in symlink targets are dealt with using /// [`std::path::Path::to_string_lossy`]. -fn display_file_name(path: &PathData, config: &Config) -> Option { +fn display_file_name( + path: &PathData, + config: &Config, + prefix_context: Option, +) -> Option { // This is our return value. We start by `&path.display_name` and modify it along the way. let mut name = escape_name(&path.display_name, &config.quoting_style); @@ -1960,6 +2068,20 @@ fn display_file_name(path: &PathData, config: &Config) -> Option { } } + // Prepend the security context to the `name` and adjust `width` in order + // to get correct alignment from later calls to`display_grid()`. + if config.context { + if let Some(pad_count) = prefix_context { + let security_context = if !matches!(config.format, Format::Commas) { + pad_left(&path.security_context, pad_count) + } else { + path.security_context.to_owned() + }; + name = format!("{} {}", security_context, name); + width += security_context.len() + 1; + } + } + Some(Cell { contents: name, width, From e018a45b54b96e1e3f8dd323b411e0bc6ae84673 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Fri, 10 Sep 2021 21:35:00 +0200 Subject: [PATCH 004/131] test_ls: add/adjust tests for SELinux context --- src/uu/ls/src/ls.rs | 12 ++-- tests/by-util/test_ls.rs | 127 +++++++++++++++++++++++++++------------ 2 files changed, 94 insertions(+), 45 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 7b7fd873e..5c26c8e26 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -639,10 +639,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .map(|v| v.map(Path::new).collect()) .unwrap_or_else(|| vec![Path::new(".")]); - if config.context && !config.selinux_supported { - show_warning!("--context (-Z) works only on an SELinux-enabled kernel",); - } - list(locs, config) } @@ -1260,7 +1256,7 @@ impl PathData { None => OnceCell::new(), }; - let security_context: String = if config.context { + let security_context = if config.context { if config.selinux_supported { #[cfg(feature = "selinux")] { @@ -1692,9 +1688,9 @@ fn display_item_long( out, "{}{} {}", display_permissions(md, true), - if item.security_context != "?" { - // GNU `ls` uses a "." character to indicate a file with a security context, but not - // other alternate access method. + if item.security_context.len() > 1 { + // GNU `ls` uses a "." character to indicate a file with a security context, + // but not other alternate access method. "." } else { "" diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 3d6092416..f14fc424a 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -347,6 +347,7 @@ fn test_ls_long_format() { // A line of the output should be: // One of the characters -bcCdDlMnpPsStTx? // rwx, with - for missing permissions, thrice. + // Zero or one "." for indicating a file with security context // A number, preceded by column whitespace, and followed by a single space. // A username, currently [^ ], followed by column whitespace, twice (or thrice for Hurd). // A number, followed by a single space. @@ -356,13 +357,13 @@ fn test_ls_long_format() { // and followed by a single space. // Whatever comes after is irrelevant to this specific test. scene.ucmd().arg(arg).arg("test-long-dir").succeeds().stdout_matches(&Regex::new( - r"\n[-bcCdDlMnpPsStTx?]([r-][w-][xt-]){3} +\d+ [^ ]+ +[^ ]+( +[^ ]+)? +\d+ [A-Z][a-z]{2} {0,2}\d{0,2} {0,2}[0-9:]+ " + r"\n[-bcCdDlMnpPsStTx?]([r-][w-][xt-]){3}\.? +\d+ [^ ]+ +[^ ]+( +[^ ]+)? +\d+ [A-Z][a-z]{2} {0,2}\d{0,2} {0,2}[0-9:]+ " ).unwrap()); } // This checks for the line with the .. entry. The uname and group should be digits. scene.ucmd().arg("-lan").arg("test-long-dir").succeeds().stdout_matches(&Regex::new( - r"\nd([r-][w-][xt-]){3} +\d+ \d+ +\d+( +\d+)? +\d+ [A-Z][a-z]{2} {0,2}\d{0,2} {0,2}[0-9:]+ \.\." + r"\nd([r-][w-][xt-]){3}\.? +\d+ \d+ +\d+( +\d+)? +\d+ [A-Z][a-z]{2} {0,2}\d{0,2} {0,2}[0-9:]+ \.\." ).unwrap()); } @@ -370,6 +371,7 @@ fn test_ls_long_format() { /// This test is mainly about coloring, but, the recursion, symlink `->` processing, /// and `.` and `..` being present in `-a` all need to work for the test to pass. /// This test does not really test anything provided by `-l` but the file names and symlinks. +#[cfg(all(feature = "ln", feature = "mkdir", feature = "touch"))] #[test] fn test_ls_long_symlink_color() { // If you break this test after breaking mkdir, touch, or ln, do not be alarmed! @@ -639,55 +641,57 @@ fn test_ls_long_formats() { let at = &scene.fixtures; at.touch(&at.plus_as_string("test-long-formats")); + // Zero or one "." for indicating a file with security context + // Regex for three names, so all of author, group and owner - let re_three = Regex::new(r"[xrw-]{9} \d ([-0-9_a-z]+ ){3}0").unwrap(); + let re_three = Regex::new(r"[xrw-]{9}\.? \d ([-0-9_a-z]+ ){3}0").unwrap(); #[cfg(unix)] - let re_three_num = Regex::new(r"[xrw-]{9} \d (\d+ ){3}0").unwrap(); + let re_three_num = Regex::new(r"[xrw-]{9}\.? \d (\d+ ){3}0").unwrap(); // Regex for two names, either: // - group and owner // - author and owner // - author and group - let re_two = Regex::new(r"[xrw-]{9} \d ([-0-9_a-z]+ ){2}0").unwrap(); + let re_two = Regex::new(r"[xrw-]{9}\.? \d ([-0-9_a-z]+ ){2}0").unwrap(); #[cfg(unix)] - let re_two_num = Regex::new(r"[xrw-]{9} \d (\d+ ){2}0").unwrap(); + let re_two_num = Regex::new(r"[xrw-]{9}\.? \d (\d+ ){2}0").unwrap(); // Regex for one name: author, group or owner - let re_one = Regex::new(r"[xrw-]{9} \d [-0-9_a-z]+ 0").unwrap(); + let re_one = Regex::new(r"[xrw-]{9}\.? \d [-0-9_a-z]+ 0").unwrap(); #[cfg(unix)] - let re_one_num = Regex::new(r"[xrw-]{9} \d \d+ 0").unwrap(); + let re_one_num = Regex::new(r"[xrw-]{9}\.? \d \d+ 0").unwrap(); // Regex for no names - let re_zero = Regex::new(r"[xrw-]{9} \d 0").unwrap(); + let re_zero = Regex::new(r"[xrw-]{9}\.? \d 0").unwrap(); - let result = scene + scene .ucmd() .arg("-l") .arg("--author") .arg("test-long-formats") - .succeeds(); - assert!(re_three.is_match(result.stdout_str())); + .succeeds() + .stdout_matches(&re_three); - let result = scene + scene .ucmd() .arg("-l1") .arg("--author") .arg("test-long-formats") - .succeeds(); - assert!(re_three.is_match(result.stdout_str())); + .succeeds() + .stdout_matches(&re_three); #[cfg(unix)] { - let result = scene + scene .ucmd() .arg("-n") .arg("--author") .arg("test-long-formats") - .succeeds(); - assert!(re_three_num.is_match(result.stdout_str())); + .succeeds() + .stdout_matches(&re_three_num); } for arg in &[ @@ -697,22 +701,22 @@ fn test_ls_long_formats() { "-lG --author", // only author and owner "-l --no-group --author", // only author and owner ] { - let result = scene + scene .ucmd() .args(&arg.split(' ').collect::>()) .arg("test-long-formats") - .succeeds(); - assert!(re_two.is_match(result.stdout_str())); + .succeeds() + .stdout_matches(&re_two); #[cfg(unix)] { - let result = scene + scene .ucmd() .arg("-n") .args(&arg.split(' ').collect::>()) .arg("test-long-formats") - .succeeds(); - assert!(re_two_num.is_match(result.stdout_str())); + .succeeds() + .stdout_matches(&re_two_num); } } @@ -726,22 +730,22 @@ fn test_ls_long_formats() { "-l --no-group", // only owner "-gG --author", // only author ] { - let result = scene + scene .ucmd() .args(&arg.split(' ').collect::>()) .arg("test-long-formats") - .succeeds(); - assert!(re_one.is_match(result.stdout_str())); + .succeeds() + .stdout_matches(&re_one); #[cfg(unix)] { - let result = scene + scene .ucmd() .arg("-n") .args(&arg.split(' ').collect::>()) .arg("test-long-formats") - .succeeds(); - assert!(re_one_num.is_match(result.stdout_str())); + .succeeds() + .stdout_matches(&re_one_num); } } @@ -758,22 +762,22 @@ fn test_ls_long_formats() { "-og1", "-og1l", ] { - let result = scene + scene .ucmd() .args(&arg.split(' ').collect::>()) .arg("test-long-formats") - .succeeds(); - assert!(re_zero.is_match(result.stdout_str())); + .succeeds() + .stdout_matches(&re_zero); #[cfg(unix)] { - let result = scene + scene .ucmd() .arg("-n") .args(&arg.split(' ').collect::>()) .arg("test-long-formats") - .succeeds(); - assert!(re_zero.is_match(result.stdout_str())); + .succeeds() + .stdout_matches(&re_zero); } } } @@ -1251,7 +1255,7 @@ fn test_ls_inode() { at.touch(file); let re_short = Regex::new(r" *(\d+) test_inode").unwrap(); - let re_long = Regex::new(r" *(\d+) [xrw-]{10} \d .+ test_inode").unwrap(); + let re_long = Regex::new(r" *(\d+) [xrw-]{10}\.? \d .+ test_inode").unwrap(); let result = scene.ucmd().arg("test_inode").arg("-i").succeeds(); assert!(re_short.is_match(result.stdout_str())); @@ -2275,3 +2279,52 @@ fn test_ls_dangling_symlinks() { .succeeds() // this should fail, though at the moment, ls lacks a way to propagate errors encountered during display .stdout_contains(if cfg!(windows) { "dangle" } else { "? dangle" }); } + +#[test] +#[cfg(feature = "feat_selinux")] +fn test_ls_context() { + use selinux::{self, KernelSupport}; + if selinux::kernel_support() == KernelSupport::Unsupported { + println!("test skipped: Kernel has no support for SElinux context",); + return; + } + let ts = TestScenario::new(util_name!()); + for c_flag in &["-Z", "--context"] { + ts.ucmd() + .args(&[c_flag, &"/"]) + .succeeds() + .stdout_only(unwrap_or_return!(expected_result(&ts, &[c_flag, &"/"])).stdout_str()); + } +} + +#[test] +#[cfg(feature = "feat_selinux")] +fn test_ls_context_format() { + use selinux::{self, KernelSupport}; + if selinux::kernel_support() == KernelSupport::Unsupported { + println!("test skipped: Kernel has no support for SElinux context",); + return; + } + let ts = TestScenario::new(util_name!()); + // NOTE: + // --format=long/verbose matches the output of GNU's ls for --context + // except for the size count which may differ to the size count reported by GNU's ls. + for word in &[ + "across", + "commas", + "horizontal", + // "long", + "single-column", + // "verbose", + "vertical", + ] { + let format = format!("--format={}", word); + ts.ucmd() + .args(&[&"-Z", &format.as_str(), &"/"]) + .succeeds() + .stdout_only( + unwrap_or_return!(expected_result(&ts, &[&"-Z", &format.as_str(), &"/"])) + .stdout_str(), + ); + } +} From 7e577476d81f36cac9f3dd64df924b3761e53b49 Mon Sep 17 00:00:00 2001 From: Smicry Date: Sun, 12 Sep 2021 23:20:05 +0800 Subject: [PATCH 005/131] add kill -l final new line #2644 --- src/uu/kill/src/kill.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index 494dc0602..b1cbba17a 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -160,18 +160,13 @@ fn print_signal(signal_name_or_value: &str) -> UResult<()> { } fn print_signals() { - let mut pos = 0; for (idx, signal) in ALL_SIGNALS.iter().enumerate() { - pos += signal.len(); - print!("{}", signal); - if idx > 0 && pos > 73 { - println!(); - pos = 0; - } else { - pos += 1; + if idx > 0 { print!(" "); } + print!("{}", signal); } + println!(); } fn list(arg: Option) -> UResult<()> { From 7d445612a29a8a2afba61c4024923be13253aca8 Mon Sep 17 00:00:00 2001 From: Smicry Date: Mon, 13 Sep 2021 00:15:53 +0800 Subject: [PATCH 006/131] add kill list final new line test --- tests/by-util/test_kill.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/by-util/test_kill.rs b/tests/by-util/test_kill.rs index fe5d4557a..9afac2862 100644 --- a/tests/by-util/test_kill.rs +++ b/tests/by-util/test_kill.rs @@ -56,6 +56,14 @@ fn test_kill_list_all_signals() { .stdout_contains("HUP"); } +#[test] +fn test_kill_list_final_new_line() { + new_ucmd!() + .arg("-l") + .succeeds() + .stdout_matches(&Regex::new("\\n$").unwrap()); +} + #[test] fn test_kill_list_all_signals_as_table() { // Check for a few signals. Do not try to be comprehensive. From db21fef95de9931a2d0c69cf0ccf3b18966f013d Mon Sep 17 00:00:00 2001 From: Smicry Date: Mon, 13 Sep 2021 09:27:14 +0800 Subject: [PATCH 007/131] fix kill list final new line test --- tests/by-util/test_kill.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_kill.rs b/tests/by-util/test_kill.rs index 9afac2862..a16760689 100644 --- a/tests/by-util/test_kill.rs +++ b/tests/by-util/test_kill.rs @@ -61,7 +61,7 @@ fn test_kill_list_final_new_line() { new_ucmd!() .arg("-l") .succeeds() - .stdout_matches(&Regex::new("\\n$").unwrap()); + .stdout_matches(&Regex::new("[\\n\\r]$").unwrap()); } #[test] From cd2153eac624232ff240b323eb4c04c110308e53 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Mon, 13 Sep 2021 13:24:44 +0200 Subject: [PATCH 008/131] ls: (--context/-Z) add support for invalid utf8 If security context byte slice contains invalid utf8: * issue warning * show replacement characters for invalid utf8 sequences --- src/uu/ls/src/ls.rs | 37 ++++++++++++++++++++++++++----------- tests/by-util/test_ls.rs | 18 +++++++++++++++++- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 5c26c8e26..be1964e43 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -40,6 +40,7 @@ use std::{ time::Duration, }; use term_grid::{Cell, Direction, Filling, Grid, GridOptions}; +use uucore::display::Quotable; use uucore::error::{set_exit_code, FromIo, UError, UResult}; use unicode_width::UnicodeWidthStr; @@ -1256,27 +1257,41 @@ impl PathData { None => OnceCell::new(), }; + let substitute_string = "?".to_string(); let security_context = if config.context { if config.selinux_supported { #[cfg(feature = "selinux")] { - if let Ok(Some(context)) = - selinux::SecurityContext::of_path(&p_buf, must_dereference, false) - { - String::from_utf8_lossy(context.as_bytes()) - .trim_end_matches(char::from(0)) - .to_string() - } else { - // TODO: print warning with error to stderr - "?".to_string() + match selinux::SecurityContext::of_path(&p_buf, must_dereference, false) { + Err(_r) => { + // TODO: show the actual reason why it failed + show_warning!("failed to get security context of: {}", p_buf.quote()); + substitute_string + } + Ok(None) => substitute_string, + Ok(Some(context)) => { + let mut context = context.as_bytes(); + if context.ends_with(&[0]) { + // TODO: replace with `strip_prefix()` when MSRV >= 1.51 + context = &context[..context.len() - 1] + }; + String::from_utf8(context.to_vec()).unwrap_or_else(|e| { + show_warning!( + "getting security context of: {}: {}", + p_buf.quote(), + e.to_string() + ); + String::from_utf8_lossy(context).into_owned() + }) + } } } #[cfg(not(feature = "selinux"))] { - "?".to_string() + substitute_string } } else { - "?".to_string() + substitute_string } } else { String::new() diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index f14fc424a..58119aac3 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -2282,7 +2282,23 @@ fn test_ls_dangling_symlinks() { #[test] #[cfg(feature = "feat_selinux")] -fn test_ls_context() { +fn test_ls_context1() { + use selinux::{self, KernelSupport}; + if selinux::kernel_support() == KernelSupport::Unsupported { + println!("test skipped: Kernel has no support for SElinux context",); + return; + } + + let file = "test_ls_context_file"; + let expected = format!("unconfined_u:object_r:user_tmp_t:s0 {}\n", file); + let (at, mut ucmd) = at_and_ucmd!(); + at.touch(file); + ucmd.args(&["-Z", file]).succeeds().stdout_is(expected); +} + +#[test] +#[cfg(feature = "feat_selinux")] +fn test_ls_context2() { use selinux::{self, KernelSupport}; if selinux::kernel_support() == KernelSupport::Unsupported { println!("test skipped: Kernel has no support for SElinux context",); From 89428a77f31c26024980be35ed89e4b7de7983d7 Mon Sep 17 00:00:00 2001 From: Smicry Date: Tue, 14 Sep 2021 23:56:08 +0800 Subject: [PATCH 009/131] fix kill list final new line test --- tests/by-util/test_kill.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/by-util/test_kill.rs b/tests/by-util/test_kill.rs index a16760689..91e524733 100644 --- a/tests/by-util/test_kill.rs +++ b/tests/by-util/test_kill.rs @@ -58,10 +58,8 @@ fn test_kill_list_all_signals() { #[test] fn test_kill_list_final_new_line() { - new_ucmd!() - .arg("-l") - .succeeds() - .stdout_matches(&Regex::new("[\\n\\r]$").unwrap()); + let re = Regex::new("\\n$").unwrap(); + assert!(re.is_match(new_ucmd!().arg("-l").succeeds().stdout_str())); } #[test] From 94fbe1edef0c4ac9719340b10d0ce5c5e135aa7d Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Sun, 19 Sep 2021 11:25:00 +0200 Subject: [PATCH 010/131] `chroot`: quick fix for #2687 * fixes #2687 * change error message to better mimic GNU --- src/uu/chroot/src/chroot.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/uu/chroot/src/chroot.rs b/src/uu/chroot/src/chroot.rs index 40799d009..c78172025 100644 --- a/src/uu/chroot/src/chroot.rs +++ b/src/uu/chroot/src/chroot.rs @@ -67,7 +67,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { // TODO: refactor the args and command matching // See: https://github.com/uutils/coreutils/pull/2365#discussion_r647849967 let command: Vec<&str> = match commands.len() { - 1 => { + 0 => { let shell: &str = match user_shell { Err(_) => default_shell, Ok(ref s) => s.as_ref(), @@ -79,10 +79,22 @@ pub fn uumain(args: impl uucore::Args) -> i32 { set_context(newroot, &matches); + assert!(!command.is_empty()); let pstatus = Command::new(command[0]) .args(&command[1..]) .status() - .unwrap_or_else(|e| crash!(1, "Cannot exec: {}", e)); + .unwrap_or_else(|e| { + // TODO: Exit status: + // 125 if chroot itself fails + // 126 if command is found but cannot be invoked + // 127 if command cannot be found + crash!( + 1, + "failed to run command {}: {}", + command[0].to_string().quote(), + e + ) + }); if pstatus.success() { 0 From 8db8d8ac4ec6eea0109fa5c584991e0823025144 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Sun, 19 Sep 2021 22:21:29 +0200 Subject: [PATCH 011/131] chroot: add 'test_default_shell` --- tests/by-util/test_chroot.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/by-util/test_chroot.rs b/tests/by-util/test_chroot.rs index 65d821d01..361133ccb 100644 --- a/tests/by-util/test_chroot.rs +++ b/tests/by-util/test_chroot.rs @@ -89,3 +89,24 @@ fn test_preference_of_userspec() { println!("result.stdout = {}", result.stdout_str()); println!("result.stderr = {}", result.stderr_str()); } + +#[test] +fn test_default_shell() { + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + + let dir = "CHROOT_DIR"; + at.mkdir(dir); + + let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string()); + let expected = format!( + "chroot: failed to run command '{}': No such file or directory", + shell + ); + + if let Ok(result) = run_ucmd_as_root(&ts, &[dir]) { + result.stderr_contains(expected); + } else { + print!("TEST SKIPPED"); + } +} From 8c0b7d1314f15bb2caec618073e7a07b0b229505 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Wed, 22 Sep 2021 11:59:43 +0200 Subject: [PATCH 012/131] chroot: move logic so it can be triggered by tests * move the command building logic before the `chroot` syscall so it will be reachable by tests that don't have root permissions. --- src/uu/chroot/src/chroot.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/uu/chroot/src/chroot.rs b/src/uu/chroot/src/chroot.rs index c78172025..55097c1bb 100644 --- a/src/uu/chroot/src/chroot.rs +++ b/src/uu/chroot/src/chroot.rs @@ -77,11 +77,15 @@ pub fn uumain(args: impl uucore::Args) -> i32 { _ => commands, }; + assert!(!command.is_empty()); + let chroot_command = command[0]; + let chroot_args = &command[1..]; + + // NOTE: Tests can only trigger code beyond this point if they're invoked with root permissions set_context(newroot, &matches); - assert!(!command.is_empty()); - let pstatus = Command::new(command[0]) - .args(&command[1..]) + let pstatus = Command::new(chroot_command) + .args(chroot_args) .status() .unwrap_or_else(|e| { // TODO: Exit status: From 8f229aad87187b4ffe11cfbdbb747029fff6c210 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Wed, 22 Sep 2021 12:24:27 +0200 Subject: [PATCH 013/131] ls: move SELinux String building logic to its own function --- src/uu/ls/src/ls.rs | 84 ++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 911323715..ba666d740 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1259,45 +1259,7 @@ impl PathData { None => OnceCell::new(), }; - let substitute_string = "?".to_string(); - let security_context = if config.context { - if config.selinux_supported { - #[cfg(feature = "selinux")] - { - match selinux::SecurityContext::of_path(&p_buf, must_dereference, false) { - Err(_r) => { - // TODO: show the actual reason why it failed - show_warning!("failed to get security context of: {}", p_buf.quote()); - substitute_string - } - Ok(None) => substitute_string, - Ok(Some(context)) => { - let mut context = context.as_bytes(); - if context.ends_with(&[0]) { - // TODO: replace with `strip_prefix()` when MSRV >= 1.51 - context = &context[..context.len() - 1] - }; - String::from_utf8(context.to_vec()).unwrap_or_else(|e| { - show_warning!( - "getting security context of: {}: {}", - p_buf.quote(), - e.to_string() - ); - String::from_utf8_lossy(context).into_owned() - }) - } - } - } - #[cfg(not(feature = "selinux"))] - { - substitute_string - } - } else { - substitute_string - } - } else { - String::new() - }; + let security_context = get_security_context(config, &p_buf, must_dereference); Self { md: OnceCell::new(), @@ -2124,3 +2086,47 @@ fn display_symlink_count(_metadata: &Metadata) -> String { fn display_symlink_count(metadata: &Metadata) -> String { metadata.nlink().to_string() } + +// This returns the SELinux security context as UTF8 `String`. +// In the long term this should be changed to `OsStr`, see discussions at #2621/#2656 +fn get_security_context(config: &Config, p_buf: &PathBuf, must_dereference: bool) -> String { + let substitute_string = "?".to_string(); + if config.context { + if config.selinux_supported { + #[cfg(feature = "selinux")] + { + match selinux::SecurityContext::of_path(p_buf, must_dereference, false) { + Err(_r) => { + // TODO: show the actual reason why it failed + show_warning!("failed to get security context of: {}", p_buf.quote()); + substitute_string + } + Ok(None) => substitute_string, + Ok(Some(context)) => { + let mut context = context.as_bytes(); + if context.ends_with(&[0]) { + // TODO: replace with `strip_prefix()` when MSRV >= 1.51 + context = &context[..context.len() - 1] + }; + String::from_utf8(context.to_vec()).unwrap_or_else(|e| { + show_warning!( + "getting security context of: {}: {}", + p_buf.quote(), + e.to_string() + ); + String::from_utf8_lossy(context).into_owned() + }) + } + } + } + #[cfg(not(feature = "selinux"))] + { + substitute_string + } + } else { + substitute_string + } + } else { + String::new() + } +} From 8cd8c25b0dd59aef697dbf772944fc6d57967cda Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Wed, 22 Sep 2021 13:49:08 +0200 Subject: [PATCH 014/131] ls: silence clippy warnings if feat_selinx is not set --- src/uu/ls/src/ls.rs | 69 +++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index ba666d740..51dc0d247 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1259,7 +1259,11 @@ impl PathData { None => OnceCell::new(), }; - let security_context = get_security_context(config, &p_buf, must_dereference); + let security_context = if config.context { + get_security_context(config, &p_buf, must_dereference) + } else { + String::new() + }; Self { md: OnceCell::new(), @@ -2089,44 +2093,41 @@ fn display_symlink_count(metadata: &Metadata) -> String { // This returns the SELinux security context as UTF8 `String`. // In the long term this should be changed to `OsStr`, see discussions at #2621/#2656 -fn get_security_context(config: &Config, p_buf: &PathBuf, must_dereference: bool) -> String { +#[allow(unused_variables)] +fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String { let substitute_string = "?".to_string(); - if config.context { - if config.selinux_supported { - #[cfg(feature = "selinux")] - { - match selinux::SecurityContext::of_path(p_buf, must_dereference, false) { - Err(_r) => { - // TODO: show the actual reason why it failed - show_warning!("failed to get security context of: {}", p_buf.quote()); - substitute_string - } - Ok(None) => substitute_string, - Ok(Some(context)) => { - let mut context = context.as_bytes(); - if context.ends_with(&[0]) { - // TODO: replace with `strip_prefix()` when MSRV >= 1.51 - context = &context[..context.len() - 1] - }; - String::from_utf8(context.to_vec()).unwrap_or_else(|e| { - show_warning!( - "getting security context of: {}: {}", - p_buf.quote(), - e.to_string() - ); - String::from_utf8_lossy(context).into_owned() - }) - } + if config.selinux_supported { + #[cfg(feature = "selinux")] + { + match selinux::SecurityContext::of_path(p_buf, must_dereference, false) { + Err(_r) => { + // TODO: show the actual reason why it failed + show_warning!("failed to get security context of: {}", p_buf.quote()); + substitute_string + } + Ok(None) => substitute_string, + Ok(Some(context)) => { + let mut context = context.as_bytes(); + if context.ends_with(&[0]) { + // TODO: replace with `strip_prefix()` when MSRV >= 1.51 + context = &context[..context.len() - 1] + }; + String::from_utf8(context.to_vec()).unwrap_or_else(|e| { + show_warning!( + "getting security context of: {}: {}", + p_buf.quote(), + e.to_string() + ); + String::from_utf8_lossy(context).into_owned() + }) } } - #[cfg(not(feature = "selinux"))] - { - substitute_string - } - } else { + } + #[cfg(not(feature = "selinux"))] + { substitute_string } } else { - String::new() + substitute_string } } From 30c8a4378893d52161b3f9fbea244fdcd54df52a Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Wed, 22 Sep 2021 12:03:22 +0200 Subject: [PATCH 015/131] test_chroot: add comments --- tests/by-util/test_chroot.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/by-util/test_chroot.rs b/tests/by-util/test_chroot.rs index 361133ccb..3e5c22679 100644 --- a/tests/by-util/test_chroot.rs +++ b/tests/by-util/test_chroot.rs @@ -15,6 +15,7 @@ fn test_missing_operand() { #[test] fn test_enter_chroot_fails() { + // NOTE: since #2689 this test also ensures that we don't regress #2687 let (at, mut ucmd) = at_and_ucmd!(); at.mkdir("jail"); @@ -92,6 +93,7 @@ fn test_preference_of_userspec() { #[test] fn test_default_shell() { + // NOTE: This test intends to trigger code which can only be reached with root permissions. let ts = TestScenario::new(util_name!()); let at = &ts.fixtures; @@ -99,14 +101,15 @@ fn test_default_shell() { at.mkdir(dir); let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string()); - let expected = format!( + let _expected = format!( "chroot: failed to run command '{}': No such file or directory", shell ); - if let Ok(result) = run_ucmd_as_root(&ts, &[dir]) { - result.stderr_contains(expected); - } else { - print!("TEST SKIPPED"); - } + // TODO: [2021-09; jhscheer] uncomment if/when #2692 gets merged + // if let Ok(result) = run_ucmd_as_root(&ts, &[dir]) { + // result.stderr_contains(expected); + // } else { + // print!("TEST SKIPPED"); + // } } From 3882df5cdcde6e96f559176f9936f49e81a966b2 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 25 Sep 2021 00:22:01 -0300 Subject: [PATCH 016/131] expr: use UResult --- src/uu/expr/src/expr.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/uu/expr/src/expr.rs b/src/uu/expr/src/expr.rs index 2d82300ff..eaf329bc5 100644 --- a/src/uu/expr/src/expr.rs +++ b/src/uu/expr/src/expr.rs @@ -9,6 +9,7 @@ extern crate uucore; use clap::{crate_version, App, Arg}; +use uucore::error::{UResult, USimpleError}; use uucore::InvalidEncodingHandling; mod syntax_tree; @@ -23,7 +24,8 @@ pub fn uu_app() -> App<'static, 'static> { .arg(Arg::with_name(HELP).long(HELP)) } -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = args .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); @@ -32,13 +34,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 { // The following usage should work without escaping hyphens: `expr -15 = 1 + 2 \* \( 3 - -4 \)` if maybe_handle_help_or_version(&args) { - 0 + Ok(()) } else { let token_strings = args[1..].to_vec(); match process_expr(&token_strings) { Ok(expr_result) => print_expr_ok(&expr_result), - Err(expr_error) => print_expr_error(&expr_error), + Err(expr_error) => Err(USimpleError::new(2, &expr_error)), } } } @@ -49,19 +51,15 @@ fn process_expr(token_strings: &[String]) -> Result { evaluate_ast(maybe_ast) } -fn print_expr_ok(expr_result: &str) -> i32 { +fn print_expr_ok(expr_result: &str) -> UResult<()> { println!("{}", expr_result); if expr_result == "0" || expr_result.is_empty() { - 1 + Err(1.into()) } else { - 0 + Ok(()) } } -fn print_expr_error(expr_error: &str) -> ! { - crash!(2, "{}", expr_error) -} - fn evaluate_ast(maybe_ast: Result, String>) -> Result { maybe_ast.and_then(|ast| ast.evaluate()) } From 4ff5fea502a87c7334d5d954d47c1a1def4e48af Mon Sep 17 00:00:00 2001 From: vulppine Date: Fri, 1 Oct 2021 14:58:26 -0700 Subject: [PATCH 017/131] cp: uumain returns UResult, UError for Error --- src/uu/cp/src/cp.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index cd33f9fa6..97a196dc1 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -49,6 +49,7 @@ use std::path::{Path, PathBuf, StripPrefixError}; use std::str::FromStr; use std::string::ToString; use uucore::backup_control::{self, BackupMode}; +use uucore::error::{set_exit_code, UError, UResult}; use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use walkdir::WalkDir; @@ -105,6 +106,12 @@ quick_error! { } } +impl UError for Error { + fn code(&self) -> i32 { + 1 + } +} + /// Continue next iteration of loop if result of expression is error macro_rules! or_continue( ($expr:expr) => (match $expr { @@ -220,7 +227,6 @@ pub struct Options { static ABOUT: &str = "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY."; static LONG_HELP: &str = ""; -static EXIT_OK: i32 = 0; static EXIT_ERR: i32 = 1; fn usage() -> String { @@ -446,7 +452,7 @@ pub fn uu_app() -> App<'static, 'static> { .multiple(true)) } -pub fn uumain(args: impl uucore::Args) -> i32 { +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let usage = usage(); let matches = uu_app() .after_help(&*format!( @@ -457,11 +463,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .usage(&usage[..]) .get_matches_from(args); - let options = crash_if_err!(EXIT_ERR, Options::from_matches(&matches)); + let options = Options::from_matches(&matches)?; if options.overwrite == OverwriteMode::NoClobber && options.backup != BackupMode::NoBackup { show_usage_error!("options --backup and --no-clobber are mutually exclusive"); - return 1; + set_exit_code(EXIT_ERR); + return Ok(()); } let paths: Vec = matches @@ -469,7 +476,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .map(|v| v.map(ToString::to_string).collect()) .unwrap_or_default(); - let (sources, target) = crash_if_err!(EXIT_ERR, parse_path_args(&paths, &options)); + let (sources, target) = parse_path_args(&paths, &options)?; if let Err(error) = copy(&sources, &target, &options) { match error { @@ -479,10 +486,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 { // Else we caught a fatal bubbled-up error, log it to stderr _ => show_error!("{}", error), }; - return EXIT_ERR; + set_exit_code(EXIT_ERR); } - EXIT_OK + Ok(()) } impl ClobberMode { @@ -1124,7 +1131,7 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu let xattrs = xattr::list(source)?; for attr in xattrs { if let Some(attr_value) = xattr::get(source, attr.clone())? { - crash_if_err!(EXIT_ERR, xattr::set(dest, attr, &attr_value[..])); + xattr::set(dest, attr, &attr_value[..])?; } } } From c64f09dc59897583d5cf7fac856b2daa69cc6089 Mon Sep 17 00:00:00 2001 From: vulppine Date: Fri, 1 Oct 2021 15:18:05 -0700 Subject: [PATCH 018/131] cp: Adds a needed macro, changes a return --- src/uu/cp/src/cp.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 97a196dc1..b50bca905 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -49,7 +49,7 @@ use std::path::{Path, PathBuf, StripPrefixError}; use std::str::FromStr; use std::string::ToString; use uucore::backup_control::{self, BackupMode}; -use uucore::error::{set_exit_code, UError, UResult}; +use uucore::error::{set_exit_code, ExitCode, UError, UResult}; use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use walkdir::WalkDir; @@ -452,6 +452,7 @@ pub fn uu_app() -> App<'static, 'static> { .multiple(true)) } +#[uucore_procs::gen_uumain] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let usage = usage(); let matches = uu_app() @@ -467,8 +468,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { if options.overwrite == OverwriteMode::NoClobber && options.backup != BackupMode::NoBackup { show_usage_error!("options --backup and --no-clobber are mutually exclusive"); - set_exit_code(EXIT_ERR); - return Ok(()); + return Err(ExitCode(EXIT_ERR).into()); } let paths: Vec = matches From e9371dc57d6f323f9d8953126af9fa23d81054b5 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Fri, 1 Oct 2021 23:25:56 +0200 Subject: [PATCH 019/131] common/util: fix parsing of coreutil version For the CICD on macOS, this fixes: ``` ---- common::util::tests::test_check_coreutil_version stdout ---- ---- common::util::tests::test_expected_result stdout ---- thread 'common::util::tests::test_expected_result' panicked at 'byte index 4 is out of bounds of `9.0`', tests/common/util.rs:1172:41 ``` --- tests/common/util.rs | 46 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/tests/common/util.rs b/tests/common/util.rs index f3cdec010..8e9078e9c 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -1169,7 +1169,7 @@ pub fn check_coreutil_version( if s.contains(&format!("(GNU coreutils) {}", version_expected)) { Ok(format!("{}: {}", UUTILS_INFO, s.to_string())) } else if s.contains("(GNU coreutils)") { - let version_found = s.split_whitespace().last().unwrap()[..4].parse::().unwrap_or_default(); + let version_found = parse_coreutil_version(s); let version_expected = version_expected.parse::().unwrap_or_default(); if version_found > version_expected { Ok(format!("{}: version for the reference coreutil '{}' is higher than expected; expected: {}, found: {}", UUTILS_INFO, util_name, version_expected, version_found)) @@ -1182,6 +1182,20 @@ pub fn check_coreutil_version( ) } +// simple heuristic to parse the coreutils SemVer string, e.g. "id (GNU coreutils) 8.32.263-0475" +fn parse_coreutil_version(version_string: &str) -> f32 { + version_string + .split_whitespace() + .last() + .unwrap() + .split('.') + .take(2) + .collect::>() + .join(".") + .parse::() + .unwrap_or_default() +} + /// This runs the GNU coreutils `util_name` binary in `$PATH` in order to /// dynamically gather reference values on the system. /// If the `util_name` in `$PATH` doesn't include a coreutils version string, @@ -1474,6 +1488,36 @@ mod tests { res.normalized_newlines_stdout_is("A\r\nB\nC\n"); } + #[test] + #[cfg(unix)] + fn test_parse_coreutil_version() { + use std::assert_eq; + assert_eq!( + parse_coreutil_version("id (GNU coreutils) 9.0.123-0123").to_string(), + "9" + ); + assert_eq!( + parse_coreutil_version("id (GNU coreutils) 8.32.263-0475").to_string(), + "8.32" + ); + assert_eq!( + parse_coreutil_version("id (GNU coreutils) 8.25.123-0123").to_string(), + "8.25" + ); + assert_eq!( + parse_coreutil_version("id (GNU coreutils) 9.0").to_string(), + "9" + ); + assert_eq!( + parse_coreutil_version("id (GNU coreutils) 8.32").to_string(), + "8.32" + ); + assert_eq!( + parse_coreutil_version("id (GNU coreutils) 8.25").to_string(), + "8.25" + ); + } + #[test] #[cfg(unix)] fn test_check_coreutil_version() { From 4319248bb650bda453a17e8f2d30d6b922ed700d Mon Sep 17 00:00:00 2001 From: vulppine Date: Fri, 1 Oct 2021 16:45:19 -0700 Subject: [PATCH 020/131] cp: Changes '1' to 'EXIT_ERR' in UError impl --- src/uu/cp/src/cp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index b50bca905..518a2262c 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -108,7 +108,7 @@ quick_error! { impl UError for Error { fn code(&self) -> i32 { - 1 + EXIT_ERR } } From d013461a6fa963c130468d4c7e26ddd437082b9c Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Fri, 1 Oct 2021 22:27:01 -0400 Subject: [PATCH 021/131] ls: replace redundant closure with function itself --- src/uu/ls/src/ls.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 51dc0d247..3fa3b4f8e 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1967,11 +1967,7 @@ fn display_file_name( #[cfg(unix)] { if config.format != Format::Long && config.inode { - name = path - .md() - .map_or_else(|| "?".to_string(), |md| get_inode(md)) - + " " - + &name; + name = path.md().map_or_else(|| "?".to_string(), get_inode) + " " + &name; } } From 71b7d6b57da8853059d8a78e115ac3dbce27280d Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Fri, 1 Oct 2021 22:27:19 -0400 Subject: [PATCH 022/131] more: remove redundant mut from stdout accesses --- src/uu/more/src/more.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index 3a601c1e8..d424d5a77 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -210,7 +210,7 @@ fn reset_term(stdout: &mut std::io::Stdout) { #[inline(always)] fn reset_term(_: &mut usize) {} -fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bool) { +fn more(buff: &str, stdout: &mut Stdout, next_file: Option<&str>, silent: bool) { let (cols, rows) = terminal::size().unwrap(); let lines = break_buff(buff, usize::from(cols)); @@ -232,7 +232,7 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bo code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, }) => { - reset_term(&mut stdout); + reset_term(stdout); std::process::exit(0); } Event::Key(KeyEvent { From 06ae968ecf732ce53954f01b85fbae5969a49d38 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Fri, 1 Oct 2021 23:01:06 -0400 Subject: [PATCH 023/131] csplit: use assert! instead of if then panic! --- src/uu/csplit/src/csplit.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/uu/csplit/src/csplit.rs b/src/uu/csplit/src/csplit.rs index dbf65b71d..0d99154df 100644 --- a/src/uu/csplit/src/csplit.rs +++ b/src/uu/csplit/src/csplit.rs @@ -320,18 +320,19 @@ impl<'a> SplitWriter<'a> { let l = line?; match n.cmp(&(&ln + 1)) { Ordering::Less => { - if input_iter.add_line_to_buffer(ln, l).is_some() { - panic!("the buffer is big enough to contain 1 line"); - } + assert!( + input_iter.add_line_to_buffer(ln, l).is_none(), + "the buffer is big enough to contain 1 line" + ); ret = Ok(()); break; } Ordering::Equal => { - if !self.options.suppress_matched - && input_iter.add_line_to_buffer(ln, l).is_some() - { - panic!("the buffer is big enough to contain 1 line"); - } + assert!( + self.options.suppress_matched + || input_iter.add_line_to_buffer(ln, l).is_none(), + "the buffer is big enough to contain 1 line" + ); ret = Ok(()); break; } @@ -378,9 +379,10 @@ impl<'a> SplitWriter<'a> { match (self.options.suppress_matched, offset) { // no offset, add the line to the next split (false, 0) => { - if input_iter.add_line_to_buffer(ln, l).is_some() { - panic!("the buffer is big enough to contain 1 line"); - } + assert!( + input_iter.add_line_to_buffer(ln, l).is_none(), + "the buffer is big enough to contain 1 line" + ); } // a positive offset, some more lines need to be added to the current split (false, _) => self.writeln(l)?, @@ -425,9 +427,10 @@ impl<'a> SplitWriter<'a> { if !self.options.suppress_matched { // add 1 to the buffer size to make place for the matched line input_iter.set_size_of_buffer(offset_usize + 1); - if input_iter.add_line_to_buffer(ln, l).is_some() { - panic!("should be big enough to hold every lines"); - } + assert!( + input_iter.add_line_to_buffer(ln, l).is_none(), + "should be big enough to hold every lines" + ); } self.finish_split(); if input_iter.buffer_len() < offset_usize { From de158c012243554b71ced15f35eca45fd1a86c80 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Fri, 1 Oct 2021 23:02:17 -0400 Subject: [PATCH 024/131] sort: replace redundant closure with function itself --- src/uu/sort/src/sort.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index fe286aa6d..bd79a6811 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -825,7 +825,7 @@ impl FieldSelector { fn parse(key: &str, global_settings: &GlobalSettings) -> UResult { let mut from_to = key.split(','); let (from, from_options) = Self::split_key_options(from_to.next().unwrap()); - let to = from_to.next().map(|to| Self::split_key_options(to)); + let to = from_to.next().map(Self::split_key_options); let options_are_empty = from_options.is_empty() && matches!(to, None | Some((_, ""))); if options_are_empty { From 6aee05a0f141b935f646ec1cd874feab93c5ebf0 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 2 Oct 2021 09:44:16 -0400 Subject: [PATCH 025/131] od: use assert! instead of if then panic! --- src/uu/od/src/output_info.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/uu/od/src/output_info.rs b/src/uu/od/src/output_info.rs index 49c2a09a2..cf050475a 100644 --- a/src/uu/od/src/output_info.rs +++ b/src/uu/od/src/output_info.rs @@ -145,13 +145,12 @@ impl OutputInfo { byte_size_block: usize, print_width_block: usize, ) -> [usize; MAX_BYTES_PER_UNIT] { - if byte_size_block > MAX_BYTES_PER_UNIT { - panic!( - "{}-bits types are unsupported. Current max={}-bits.", - 8 * byte_size_block, - 8 * MAX_BYTES_PER_UNIT - ); - } + assert!( + byte_size_block <= MAX_BYTES_PER_UNIT, + "{}-bits types are unsupported. Current max={}-bits.", + 8 * byte_size_block, + 8 * MAX_BYTES_PER_UNIT + ); let mut spacing = [0; MAX_BYTES_PER_UNIT]; let mut byte_size = sf.byte_size(); From 548a5121ae5bbab718896688dc8cebfe30f1bdc0 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 2 Oct 2021 10:15:15 -0400 Subject: [PATCH 026/131] dd: use assert! instead of if then panic! --- src/uu/dd/src/parseargs/unit_tests.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/uu/dd/src/parseargs/unit_tests.rs b/src/uu/dd/src/parseargs/unit_tests.rs index b898f1e5d..21900ee49 100644 --- a/src/uu/dd/src/parseargs/unit_tests.rs +++ b/src/uu/dd/src/parseargs/unit_tests.rs @@ -35,12 +35,11 @@ fn unimplemented_flags_should_error_non_linux() { } } - if !succeeded.is_empty() { - panic!( - "The following flags did not panic as expected: {:?}", - succeeded - ); - } + assert!( + succeeded.is_empty(), + "The following flags did not panic as expected: {:?}", + succeeded + ); } #[test] @@ -64,12 +63,11 @@ fn unimplemented_flags_should_error() { } } - if !succeeded.is_empty() { - panic!( - "The following flags did not panic as expected: {:?}", - succeeded - ); - } + assert!( + succeeded.is_empty(), + "The following flags did not panic as expected: {:?}", + succeeded + ); } #[test] From d5caa0d9d88c7d19cee46b0a4137397ffa7c5642 Mon Sep 17 00:00:00 2001 From: vulppine Date: Sat, 2 Oct 2021 08:15:25 -0700 Subject: [PATCH 027/131] seq: Adds hexadecimal integer parsing --- src/uu/seq/src/seq.rs | 76 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index aac8f2280..594796641 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -88,30 +88,76 @@ impl FromStr for Number { if s.starts_with('+') { s = &s[1..]; } - - match s.parse::() { - Ok(n) => { - // If `s` is '-0', then `parse()` returns - // `BigInt::zero()`, but we need to return - // `Number::MinusZero` instead. - if n == BigInt::zero() && s.starts_with('-') { - Ok(Number::MinusZero) - } else { - Ok(Number::BigInt(n)) - } + let is_neg = s.starts_with('-'); + let is_hex = { + // GNU 20.11.2 - Parsing of Floats + match s.find("0x") { + Some(i) => (true, i), + None => match s.find("0X") { + Some(i) => (true, i), + None => (false, 0), + }, } - Err(_) => match s.parse::() { - Ok(value) if value.is_nan() => Err(format!( + }; + + match is_hex { + (true, i) => match i <= 1 { + false => Err(format!( + "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", + s.quote(), + uucore::execution_phrase(), + )), + true => match &s.as_bytes()[i + 2] { + b'-' | b'+' => Err(format!( + "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", + s.quote(), + uucore::execution_phrase(), + )), + // TODO: hexadecimal floating point parsing (see #2660) + b'.' => Err(format!( + "NotImplemented: hexadecimal floating point numbers: {}\nTry '{} --help' for more information.", + s.quote(), + uucore::execution_phrase(), + )), + _ => { + let num = BigInt::from_str_radix(&s[i + 2..], 16) + .map_err(|_| format!( + "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", + s.quote(), + uucore::execution_phrase(), + ))?; + match (is_neg, num == BigInt::zero()) { + (true, true) => Ok(Number::MinusZero), + (true, false) => Ok(Number::BigInt(-num)), + (false, _) => Ok(Number::BigInt(num)), + } + } + }, + }, + (false, _) => match s.parse::() { + Ok(n) => { + // If `s` is '-0', then `parse()` returns + // `BigInt::zero()`, but we need to return + // `Number::MinusZero` instead. + if n == BigInt::zero() && is_neg { + Ok(Number::MinusZero) + } else { + Ok(Number::BigInt(n)) + } + } + Err(_) => match s.parse::() { + Ok(value) if value.is_nan() => Err(format!( "invalid 'not-a-number' argument: {}\nTry '{} --help' for more information.", s.quote(), uucore::execution_phrase(), )), - Ok(value) => Ok(Number::F64(value)), - Err(_) => Err(format!( + Ok(value) => Ok(Number::F64(value)), + Err(_) => Err(format!( "invalid floating point argument: {}\nTry '{} --help' for more information.", s.quote(), uucore::execution_phrase(), )), + }, }, } } From aad0682a404814a03d47efb8bcb88c7b14f8bcdb Mon Sep 17 00:00:00 2001 From: vulppine Date: Sat, 2 Oct 2021 08:46:09 -0700 Subject: [PATCH 028/131] seq: Adds testing for hexadecimal integer parsing --- tests/by-util/test_seq.rs | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 27b5f99bc..2d2ea5344 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -1,6 +1,46 @@ use crate::common::util::*; use std::io::Read; +#[test] +fn test_hex_rejects_posneg_after_identifier() { + new_ucmd!() + .args(&["0x-123ABC"]) + .fails() + .no_stdout() + .stderr_contains("invalid hexadecimal argument: '0x-123ABC'") + .stderr_contains("for more information."); + new_ucmd!() + .args(&["0x+123ABC"]) + .fails() + .no_stdout() + .stderr_contains("invalid hexadecimal argument: '0x+123ABC'") + .stderr_contains("for more information."); + new_ucmd!() + .args(&["-0x-123ABC"]) + .fails() + .no_stdout() + .stderr_contains("invalid hexadecimal argument: '-0x-123ABC'") + .stderr_contains("for more information."); + new_ucmd!() + .args(&["-0x+123ABC"]) + .fails() + .no_stdout() + .stderr_contains("invalid hexadecimal argument: '-0x+123ABC'") + .stderr_contains("for more information."); +} + +#[test] +fn test_hex_lowercase_uppercase() { + new_ucmd!() + .args(&["0xa", "0xA"]) + .succeeds() + .stdout_is("10\n"); + new_ucmd!() + .args(&["0Xa", "0XA"]) + .succeeds() + .stdout_is("10\n"); +} + #[test] fn test_rejects_nan() { let ts = TestScenario::new(util_name!()); From 9dd401c3584633376440ea61af40ec786a727077 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 2 Oct 2021 23:10:00 -0300 Subject: [PATCH 029/131] base32: base_common use UResult --- src/uu/base32/src/base_common.rs | 78 ++++++++++++++++---------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index 0fd0fa5c4..015925e12 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -11,13 +11,16 @@ use std::io::{stdout, Read, Write}; use uucore::display::Quotable; use uucore::encoding::{wrap_print, Data, Format}; +use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; use uucore::InvalidEncodingHandling; use std::fs::File; use std::io::{BufReader, Stdin}; use std::path::Path; -use clap::{App, Arg}; +use clap::{crate_version, App, Arg}; + +pub static BASE_CMD_PARSE_ERROR: i32 = 1; // Config. pub struct Config { @@ -35,15 +38,14 @@ pub mod options { } impl Config { - pub fn from(app_name: &str, options: &clap::ArgMatches) -> Result { + pub fn from(options: &clap::ArgMatches) -> UResult { let file: Option = match options.values_of(options::FILE) { Some(mut values) => { let name = values.next().unwrap(); if let Some(extra_op) = values.next() { - return Err(format!( - "extra operand {}\nTry '{} --help' for more information.", - extra_op.quote(), - app_name + return Err(UUsageError::new( + BASE_CMD_PARSE_ERROR, + format!("extra operand {}", extra_op.quote(),), )); } @@ -51,7 +53,10 @@ impl Config { None } else { if !Path::exists(Path::new(name)) { - return Err(format!("{}: No such file or directory", name.maybe_quote())); + return Err(USimpleError::new( + BASE_CMD_PARSE_ERROR, + format!("{}: No such file or directory", name.maybe_quote()), + )); } Some(name.to_owned()) } @@ -62,8 +67,12 @@ impl Config { let cols = options .value_of(options::WRAP) .map(|num| { - num.parse::() - .map_err(|_| format!("invalid wrap size: {}", num.quote())) + num.parse::().map_err(|_| { + USimpleError::new( + BASE_CMD_PARSE_ERROR, + format!("invalid wrap size: {}", num.quote()), + ) + }) }) .transpose()?; @@ -76,23 +85,17 @@ impl Config { } } -pub fn parse_base_cmd_args( - args: impl uucore::Args, - name: &str, - version: &str, - about: &str, - usage: &str, -) -> Result { - let app = base_app(name, version, about).usage(usage); +pub fn parse_base_cmd_args(args: impl uucore::Args, about: &str, usage: &str) -> UResult { + let app = base_app(about).usage(usage); let arg_list = args .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - Config::from(name, &app.get_matches_from(arg_list)) + Config::from(&app.get_matches_from(arg_list)) } -pub fn base_app<'a>(name: &str, version: &'a str, about: &'a str) -> App<'static, 'a> { - App::new(name) - .version(version) +pub fn base_app<'a>(about: &'a str) -> App<'static, 'a> { + App::new(uucore::util_name()) + .version(crate_version!()) .about(about) // Format arguments. .arg( @@ -121,14 +124,15 @@ pub fn base_app<'a>(name: &str, version: &'a str, about: &'a str) -> App<'static .arg(Arg::with_name(options::FILE).index(1).multiple(true)) } -pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> Box { +pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> UResult> { match &config.to_read { Some(name) => { - let file_buf = crash_if_err!(1, File::open(Path::new(name))); - Box::new(BufReader::new(file_buf)) // as Box + let file_buf = + File::open(Path::new(name)).map_err_context(|| name.maybe_quote().to_string())?; + Ok(Box::new(BufReader::new(file_buf))) // as Box } None => { - Box::new(stdin_ref.lock()) // as Box + Ok(Box::new(stdin_ref.lock())) // as Box } } } @@ -139,8 +143,7 @@ pub fn handle_input( line_wrap: Option, ignore_garbage: bool, decode: bool, - name: &str, -) { +) -> UResult<()> { let mut data = Data::new(input, format).ignore_garbage(ignore_garbage); if let Some(wrap) = line_wrap { data = data.line_wrap(wrap); @@ -150,28 +153,23 @@ pub fn handle_input( match data.encode() { Ok(s) => { wrap_print(&data, s); + Ok(()) } - Err(_) => { - eprintln!( - "{}: error: invalid input (length must be multiple of 4 characters)", - name - ); - exit!(1) - } + Err(_) => Err(USimpleError::new( + 1, + "error: invalid input (length must be multiple of 4 characters)", + )), } } else { match data.decode() { Ok(s) => { if stdout().write_all(&s).is_err() { // on windows console, writing invalid utf8 returns an error - eprintln!("{}: error: Cannot write non-utf8 data", name); - exit!(1) + return Err(USimpleError::new(1, "error: cannot write non-utf8 data")); } + Ok(()) } - Err(_) => { - eprintln!("{}: error: invalid input", name); - exit!(1) - } + Err(_) => Err(USimpleError::new(1, "error: invalid input")), } } } From 97df700d67acb6d6c94859a713445aaffbdbbb67 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 2 Oct 2021 23:12:09 -0300 Subject: [PATCH 030/131] base32: use UResult --- src/uu/base32/src/base32.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/uu/base32/src/base32.rs b/src/uu/base32/src/base32.rs index 667fd927e..0159ef181 100644 --- a/src/uu/base32/src/base32.rs +++ b/src/uu/base32/src/base32.rs @@ -11,7 +11,7 @@ extern crate uucore; use std::io::{stdin, Read}; use clap::App; -use uucore::encoding::Format; +use uucore::{encoding::Format, error::UResult}; pub mod base_common; @@ -24,27 +24,22 @@ static ABOUT: &str = " to attempt to recover from any other non-alphabet bytes in the encoded stream. "; -static VERSION: &str = env!("CARGO_PKG_VERSION"); - -static BASE_CMD_PARSE_ERROR: i32 = 1; fn usage() -> String { format!("{0} [OPTION]... [FILE]", uucore::execution_phrase()) } -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let format = Format::Base32; let usage = usage(); - let name = uucore::util_name(); - let config_result: Result = - base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage); - let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s)); + let config: base_common::Config = base_common::parse_base_cmd_args(args, ABOUT, &usage)?; // Create a reference to stdin so we can return a locked stdin from // parse_base_cmd_args let stdin_raw = stdin(); - let mut input: Box = base_common::get_input(&config, &stdin_raw); + let mut input: Box = base_common::get_input(&config, &stdin_raw)?; base_common::handle_input( &mut input, @@ -52,12 +47,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { config.wrap_cols, config.ignore_garbage, config.decode, - name, - ); - - 0 + ) } pub fn uu_app() -> App<'static, 'static> { - base_common::base_app(uucore::util_name(), VERSION, ABOUT) + base_common::base_app(ABOUT) } From 051284de4ce1322ce0574b4940f37199cfdf0b14 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 2 Oct 2021 23:12:42 -0300 Subject: [PATCH 031/131] tests/base32: update test --- tests/by-util/test_base32.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/by-util/test_base32.rs b/tests/by-util/test_base32.rs index 5c74c5b59..ffe2cf74c 100644 --- a/tests/by-util/test_base32.rs +++ b/tests/by-util/test_base32.rs @@ -113,12 +113,18 @@ fn test_wrap_bad_arg() { #[test] fn test_base32_extra_operand() { + let ts = TestScenario::new(util_name!()); + // Expect a failure when multiple files are specified. - new_ucmd!() + ts.ucmd() .arg("a.txt") .arg("b.txt") .fails() - .stderr_only("base32: extra operand 'b.txt'\nTry 'base32 --help' for more information."); + .stderr_only(format!( + "{0}: extra operand 'b.txt'\nTry '{1} {0} --help' for more information.", + ts.util_name, + ts.bin_path.to_string_lossy() + )); } #[test] From 452329ad199878e9f483c67621791f94cbc2eaa2 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 2 Oct 2021 23:15:40 -0300 Subject: [PATCH 032/131] base64: use UResult --- src/uu/base64/src/base64.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/uu/base64/src/base64.rs b/src/uu/base64/src/base64.rs index ded157362..ed8b1b461 100644 --- a/src/uu/base64/src/base64.rs +++ b/src/uu/base64/src/base64.rs @@ -12,7 +12,7 @@ extern crate uucore; use uu_base32::base_common; pub use uu_base32::uu_app; -use uucore::encoding::Format; +use uucore::{encoding::Format, error::UResult}; use std::io::{stdin, Read}; @@ -25,26 +25,22 @@ static ABOUT: &str = " to attempt to recover from any other non-alphabet bytes in the encoded stream. "; -static VERSION: &str = env!("CARGO_PKG_VERSION"); - -static BASE_CMD_PARSE_ERROR: i32 = 1; fn usage() -> String { format!("{0} [OPTION]... [FILE]", uucore::execution_phrase()) } -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let format = Format::Base64; let usage = usage(); - let name = uucore::util_name(); - let config_result: Result = - base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage); - let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s)); + + let config: base_common::Config = base_common::parse_base_cmd_args(args, ABOUT, &usage)?; // Create a reference to stdin so we can return a locked stdin from // parse_base_cmd_args let stdin_raw = stdin(); - let mut input: Box = base_common::get_input(&config, &stdin_raw); + let mut input: Box = base_common::get_input(&config, &stdin_raw)?; base_common::handle_input( &mut input, @@ -52,8 +48,5 @@ pub fn uumain(args: impl uucore::Args) -> i32 { config.wrap_cols, config.ignore_garbage, config.decode, - name, - ); - - 0 + ) } From f85ccf8e460dca0a11f4a7a8440aa62e02964952 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 2 Oct 2021 23:16:07 -0300 Subject: [PATCH 033/131] tests/base64: update test --- tests/by-util/test_base64.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/by-util/test_base64.rs b/tests/by-util/test_base64.rs index a860aae91..87aa0db44 100644 --- a/tests/by-util/test_base64.rs +++ b/tests/by-util/test_base64.rs @@ -95,12 +95,18 @@ fn test_wrap_bad_arg() { #[test] fn test_base64_extra_operand() { + let ts = TestScenario::new(util_name!()); + // Expect a failure when multiple files are specified. - new_ucmd!() + ts.ucmd() .arg("a.txt") .arg("b.txt") .fails() - .stderr_only("base64: extra operand 'b.txt'\nTry 'base64 --help' for more information."); + .stderr_only(format!( + "{0}: extra operand 'b.txt'\nTry '{1} {0} --help' for more information.", + ts.util_name, + ts.bin_path.to_string_lossy() + )); } #[test] From b924774c8a2e6cedffa22ab5e8f429d885362c46 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 2 Oct 2021 23:17:16 -0300 Subject: [PATCH 034/131] basenc: use UResult --- src/uu/basenc/src/basenc.rs | 40 ++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/uu/basenc/src/basenc.rs b/src/uu/basenc/src/basenc.rs index 86c251ad1..a730d3e0f 100644 --- a/src/uu/basenc/src/basenc.rs +++ b/src/uu/basenc/src/basenc.rs @@ -11,10 +11,14 @@ #[macro_use] extern crate uucore; -use clap::{crate_version, App, Arg}; -use uu_base32::base_common::{self, Config}; +use clap::{App, Arg}; +use uu_base32::base_common::{self, Config, BASE_CMD_PARSE_ERROR}; -use uucore::{encoding::Format, InvalidEncodingHandling}; +use uucore::{ + encoding::Format, + error::{UResult, UUsageError}, + InvalidEncodingHandling, +}; use std::io::{stdin, Read}; @@ -26,8 +30,6 @@ static ABOUT: &str = " from any other non-alphabet bytes in the encoded stream. "; -static BASE_CMD_PARSE_ERROR: i32 = 1; - const ENCODINGS: &[(&str, Format)] = &[ ("base64", Format::Base64), ("base64url", Format::Base64Url), @@ -47,14 +49,14 @@ fn usage() -> String { } pub fn uu_app() -> App<'static, 'static> { - let mut app = base_common::base_app(uucore::util_name(), crate_version!(), ABOUT); + let mut app = base_common::base_app(ABOUT); for encoding in ENCODINGS { app = app.arg(Arg::with_name(encoding.0).long(encoding.0)); } app } -fn parse_cmd_args(args: impl uucore::Args) -> (Config, Format) { +fn parse_cmd_args(args: impl uucore::Args) -> UResult<(Config, Format)> { let usage = usage(); let matches = uu_app().usage(&usage[..]).get_matches_from( args.collect_str(InvalidEncodingHandling::ConvertLossy) @@ -63,24 +65,19 @@ fn parse_cmd_args(args: impl uucore::Args) -> (Config, Format) { let format = ENCODINGS .iter() .find(|encoding| matches.is_present(encoding.0)) - .unwrap_or_else(|| { - show_usage_error!("missing encoding type"); - std::process::exit(1) - }) + .ok_or_else(|| UUsageError::new(BASE_CMD_PARSE_ERROR, "missing encoding type"))? .1; - ( - Config::from("basenc", &matches).unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s)), - format, - ) + let config = Config::from(&matches)?; + Ok((config, format)) } -pub fn uumain(args: impl uucore::Args) -> i32 { - let name = uucore::util_name(); - let (config, format) = parse_cmd_args(args); +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let (config, format) = parse_cmd_args(args)?; // Create a reference to stdin so we can return a locked stdin from // parse_base_cmd_args let stdin_raw = stdin(); - let mut input: Box = base_common::get_input(&config, &stdin_raw); + let mut input: Box = base_common::get_input(&config, &stdin_raw)?; base_common::handle_input( &mut input, @@ -88,8 +85,5 @@ pub fn uumain(args: impl uucore::Args) -> i32 { config.wrap_cols, config.ignore_garbage, config.decode, - name, - ); - - 0 + ) } From 4e1f945e86e99e350f3f3e12db8057d19401acc2 Mon Sep 17 00:00:00 2001 From: vulppine Date: Sun, 3 Oct 2021 09:50:49 -0700 Subject: [PATCH 035/131] seq: Adds testing for large hex numbers --- tests/by-util/test_seq.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 2d2ea5344..7136f5e76 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -2,7 +2,7 @@ use crate::common::util::*; use std::io::Read; #[test] -fn test_hex_rejects_posneg_after_identifier() { +fn test_hex_rejects_sign_after_identifier() { new_ucmd!() .args(&["0x-123ABC"]) .fails() @@ -41,6 +41,19 @@ fn test_hex_lowercase_uppercase() { .stdout_is("10\n"); } +#[test] +fn test_hex_big_number() { + new_ucmd!() + .args(&[ + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0x100000000000000000000000000000000", + ]) + .succeeds() + .stdout_is( + "340282366920938463463374607431768211455\n340282366920938463463374607431768211456\n", + ); +} + #[test] fn test_rejects_nan() { let ts = TestScenario::new(util_name!()); From 00c9710206a35eaf8c96b6361c2980c14697c784 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sun, 3 Oct 2021 23:57:33 -0300 Subject: [PATCH 036/131] uucore_procs+uu: specify uucore::show_error This allows crates to use `#[uucore_procs::gen_uumain]` without `use uucore::show_error` or #[macro_use] extern crate uucore; Removed unecessary usage --- src/uu/arch/src/arch.rs | 3 --- src/uu/base32/src/base32.rs | 3 --- src/uu/base64/src/base64.rs | 3 --- src/uu/basenc/src/basenc.rs | 3 --- src/uu/cat/src/cat.rs | 2 -- src/uu/chgrp/src/chgrp.rs | 2 -- src/uu/chown/src/chown.rs | 2 -- src/uu/df/src/df.rs | 2 -- src/uu/dirname/src/dirname.rs | 3 --- src/uu/echo/src/echo.rs | 3 --- src/uu/expr/src/expr.rs | 3 --- src/uu/false/src/false.rs | 3 --- src/uu/hostid/src/hostid.rs | 3 --- src/uu/hostname/src/hostname.rs | 3 --- src/uu/mktemp/src/mktemp.rs | 3 --- src/uu/pwd/src/pwd.rs | 3 --- src/uu/sleep/src/sleep.rs | 3 --- src/uu/true/src/true.rs | 3 --- src/uu/unlink/src/unlink.rs | 3 --- src/uu/uptime/src/uptime.rs | 2 -- src/uu/whoami/src/whoami.rs | 2 -- src/uu/yes/src/yes.rs | 2 -- src/uucore_procs/src/lib.rs | 2 +- 23 files changed, 1 insertion(+), 60 deletions(-) diff --git a/src/uu/arch/src/arch.rs b/src/uu/arch/src/arch.rs index 478fef6f1..d23a11cc8 100644 --- a/src/uu/arch/src/arch.rs +++ b/src/uu/arch/src/arch.rs @@ -6,9 +6,6 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -#[macro_use] -extern crate uucore; - use platform_info::*; use clap::{crate_version, App}; diff --git a/src/uu/base32/src/base32.rs b/src/uu/base32/src/base32.rs index 0159ef181..f4b4b49de 100644 --- a/src/uu/base32/src/base32.rs +++ b/src/uu/base32/src/base32.rs @@ -5,9 +5,6 @@ // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. -#[macro_use] -extern crate uucore; - use std::io::{stdin, Read}; use clap::App; diff --git a/src/uu/base64/src/base64.rs b/src/uu/base64/src/base64.rs index ed8b1b461..c041d6d69 100644 --- a/src/uu/base64/src/base64.rs +++ b/src/uu/base64/src/base64.rs @@ -6,9 +6,6 @@ // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. -#[macro_use] -extern crate uucore; - use uu_base32::base_common; pub use uu_base32::uu_app; diff --git a/src/uu/basenc/src/basenc.rs b/src/uu/basenc/src/basenc.rs index a730d3e0f..e4b24c351 100644 --- a/src/uu/basenc/src/basenc.rs +++ b/src/uu/basenc/src/basenc.rs @@ -8,9 +8,6 @@ //spell-checker:ignore (args) lsbf msbf -#[macro_use] -extern crate uucore; - use clap::{App, Arg}; use uu_base32::base_common::{self, Config, BASE_CMD_PARSE_ERROR}; diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index baf8af6d5..308e11bfe 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -12,8 +12,6 @@ #[cfg(unix)] extern crate unix_socket; -#[macro_use] -extern crate uucore; // last synced with: cat (GNU coreutils) 8.13 use clap::{crate_version, App, Arg}; diff --git a/src/uu/chgrp/src/chgrp.rs b/src/uu/chgrp/src/chgrp.rs index 1795ad0d5..c70e5e5c7 100644 --- a/src/uu/chgrp/src/chgrp.rs +++ b/src/uu/chgrp/src/chgrp.rs @@ -7,8 +7,6 @@ // spell-checker:ignore (ToDO) COMFOLLOW Chowner RFILE RFILE's derefer dgid nonblank nonprint nonprinting -#[macro_use] -extern crate uucore; use uucore::display::Quotable; pub use uucore::entries; use uucore::error::{FromIo, UResult, USimpleError}; diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 1cd71d3f5..f24c4ec89 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -7,8 +7,6 @@ // spell-checker:ignore (ToDO) COMFOLLOW Passwd RFILE RFILE's derefer dgid duid groupname -#[macro_use] -extern crate uucore; use uucore::display::Quotable; pub use uucore::entries::{self, Group, Locate, Passwd}; use uucore::perms::{chown_base, options, IfFrom}; diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 310d3c664..2f703542c 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -6,8 +6,6 @@ // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. -#[macro_use] -extern crate uucore; use uucore::error::UError; use uucore::error::UResult; #[cfg(unix)] diff --git a/src/uu/dirname/src/dirname.rs b/src/uu/dirname/src/dirname.rs index 601d93ac0..129c9369e 100644 --- a/src/uu/dirname/src/dirname.rs +++ b/src/uu/dirname/src/dirname.rs @@ -5,9 +5,6 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -#[macro_use] -extern crate uucore; - use clap::{crate_version, App, Arg}; use std::path::Path; use uucore::display::print_verbatim; diff --git a/src/uu/echo/src/echo.rs b/src/uu/echo/src/echo.rs index 601fd8d48..a0e6c0d9c 100644 --- a/src/uu/echo/src/echo.rs +++ b/src/uu/echo/src/echo.rs @@ -6,9 +6,6 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -#[macro_use] -extern crate uucore; - use clap::{crate_version, App, Arg}; use std::io::{self, Write}; use std::iter::Peekable; diff --git a/src/uu/expr/src/expr.rs b/src/uu/expr/src/expr.rs index eaf329bc5..6e2a8701a 100644 --- a/src/uu/expr/src/expr.rs +++ b/src/uu/expr/src/expr.rs @@ -5,9 +5,6 @@ //* For the full copyright and license information, please view the LICENSE //* file that was distributed with this source code. -#[macro_use] -extern crate uucore; - use clap::{crate_version, App, Arg}; use uucore::error::{UResult, USimpleError}; use uucore::InvalidEncodingHandling; diff --git a/src/uu/false/src/false.rs b/src/uu/false/src/false.rs index 88ec1af06..783c7fa0d 100644 --- a/src/uu/false/src/false.rs +++ b/src/uu/false/src/false.rs @@ -5,9 +5,6 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -#[macro_use] -extern crate uucore; - use clap::App; use uucore::error::UResult; diff --git a/src/uu/hostid/src/hostid.rs b/src/uu/hostid/src/hostid.rs index 4c9cafa35..309e15990 100644 --- a/src/uu/hostid/src/hostid.rs +++ b/src/uu/hostid/src/hostid.rs @@ -7,9 +7,6 @@ // spell-checker:ignore (ToDO) gethostid -#[macro_use] -extern crate uucore; - use clap::{crate_version, App}; use libc::c_long; use uucore::error::UResult; diff --git a/src/uu/hostname/src/hostname.rs b/src/uu/hostname/src/hostname.rs index 2de6627e8..9c8504027 100644 --- a/src/uu/hostname/src/hostname.rs +++ b/src/uu/hostname/src/hostname.rs @@ -7,9 +7,6 @@ // spell-checker:ignore (ToDO) MAKEWORD addrs hashset -#[macro_use] -extern crate uucore; - use std::collections::hash_set::HashSet; use std::net::ToSocketAddrs; use std::str; diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index 81a3521e9..4318f0ad6 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -8,9 +8,6 @@ // spell-checker:ignore (paths) GPGHome -#[macro_use] -extern crate uucore; - use clap::{crate_version, App, Arg}; use uucore::display::{println_verbatim, Quotable}; use uucore::error::{FromIo, UError, UResult}; diff --git a/src/uu/pwd/src/pwd.rs b/src/uu/pwd/src/pwd.rs index 1138dba8e..8beb65dbd 100644 --- a/src/uu/pwd/src/pwd.rs +++ b/src/uu/pwd/src/pwd.rs @@ -5,9 +5,6 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -#[macro_use] -extern crate uucore; - use clap::{crate_version, App, Arg}; use std::env; use std::io; diff --git a/src/uu/sleep/src/sleep.rs b/src/uu/sleep/src/sleep.rs index a70a524c4..80e8cd852 100644 --- a/src/uu/sleep/src/sleep.rs +++ b/src/uu/sleep/src/sleep.rs @@ -5,9 +5,6 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -#[macro_use] -extern crate uucore; - use std::thread; use std::time::Duration; diff --git a/src/uu/true/src/true.rs b/src/uu/true/src/true.rs index 6b4a87bf1..b5fbf7c9d 100644 --- a/src/uu/true/src/true.rs +++ b/src/uu/true/src/true.rs @@ -5,9 +5,6 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -#[macro_use] -extern crate uucore; - use clap::App; use uucore::error::UResult; diff --git a/src/uu/unlink/src/unlink.rs b/src/uu/unlink/src/unlink.rs index 1b4e4c998..58bb5442c 100644 --- a/src/uu/unlink/src/unlink.rs +++ b/src/uu/unlink/src/unlink.rs @@ -7,9 +7,6 @@ /* last synced with: unlink (GNU coreutils) 8.21 */ -#[macro_use] -extern crate uucore; - use std::fs::remove_file; use std::path::Path; diff --git a/src/uu/uptime/src/uptime.rs b/src/uu/uptime/src/uptime.rs index f649b96b6..eabcd1abf 100644 --- a/src/uu/uptime/src/uptime.rs +++ b/src/uu/uptime/src/uptime.rs @@ -11,8 +11,6 @@ use chrono::{Local, TimeZone, Utc}; use clap::{crate_version, App, Arg}; -#[macro_use] -extern crate uucore; // import crate time from utmpx pub use uucore::libc; use uucore::libc::time_t; diff --git a/src/uu/whoami/src/whoami.rs b/src/uu/whoami/src/whoami.rs index 830b86e63..0820588ee 100644 --- a/src/uu/whoami/src/whoami.rs +++ b/src/uu/whoami/src/whoami.rs @@ -9,8 +9,6 @@ #[macro_use] extern crate clap; -#[macro_use] -extern crate uucore; use clap::App; diff --git a/src/uu/yes/src/yes.rs b/src/uu/yes/src/yes.rs index 03ae4e415..bbedc0af1 100644 --- a/src/uu/yes/src/yes.rs +++ b/src/uu/yes/src/yes.rs @@ -12,8 +12,6 @@ use std::io::{self, Write}; #[macro_use] extern crate clap; -#[macro_use] -extern crate uucore; use clap::{App, Arg}; use uucore::error::{UResult, USimpleError}; diff --git a/src/uucore_procs/src/lib.rs b/src/uucore_procs/src/lib.rs index 092a4a66c..13b1dae3b 100644 --- a/src/uucore_procs/src/lib.rs +++ b/src/uucore_procs/src/lib.rs @@ -101,7 +101,7 @@ pub fn gen_uumain(_args: TokenStream, stream: TokenStream) -> TokenStream { Err(e) => { let s = format!("{}", e); if s != "" { - show_error!("{}", s); + uucore::show_error!("{}", s); } if e.usage() { eprintln!("Try '{} --help' for more information.", uucore::execution_phrase()); From e2fa6f9412a3f831c687267d9cc4f47ded80b739 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Mon, 4 Oct 2021 00:04:33 -0300 Subject: [PATCH 037/131] users: use UResult --- src/uu/users/src/users.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/uu/users/src/users.rs b/src/uu/users/src/users.rs index d374df181..d0768d8d1 100644 --- a/src/uu/users/src/users.rs +++ b/src/uu/users/src/users.rs @@ -11,6 +11,7 @@ use std::path::Path; use clap::{crate_version, App, Arg}; +use uucore::error::UResult; use uucore::utmpx::{self, Utmpx}; static ABOUT: &str = "Print the user names of users currently logged in to the current host"; @@ -29,7 +30,8 @@ If FILE is not specified, use {}. /var/log/wtmp as FILE is common.", ) } -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let usage = usage(); let after_help = get_long_usage(); @@ -59,7 +61,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { println!("{}", users.join(" ")); } - 0 + Ok(()) } pub fn uu_app() -> App<'static, 'static> { From cddd40b4e1766a32d3f46f010e64dc07c2fb1fad Mon Sep 17 00:00:00 2001 From: vulppine Date: Tue, 5 Oct 2021 18:41:28 -0700 Subject: [PATCH 038/131] seq: Updates hex parse readability, adds hex test --- src/uu/seq/src/seq.rs | 29 +++++++++-------------------- tests/by-util/test_seq.rs | 10 ++++++++++ 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 594796641..a76a23c4e 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -89,25 +89,9 @@ impl FromStr for Number { s = &s[1..]; } let is_neg = s.starts_with('-'); - let is_hex = { - // GNU 20.11.2 - Parsing of Floats - match s.find("0x") { - Some(i) => (true, i), - None => match s.find("0X") { - Some(i) => (true, i), - None => (false, 0), - }, - } - }; - match is_hex { - (true, i) => match i <= 1 { - false => Err(format!( - "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", - s.quote(), - uucore::execution_phrase(), - )), - true => match &s.as_bytes()[i + 2] { + match s.to_lowercase().find("0x") { + Some(i) if i <= 1 => match &s.as_bytes()[i + 2] { b'-' | b'+' => Err(format!( "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", s.quote(), @@ -133,8 +117,13 @@ impl FromStr for Number { } } }, - }, - (false, _) => match s.parse::() { + Some(_) => Err(format!( + "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", + s.quote(), + uucore::execution_phrase(), + )), + + None => match s.parse::() { Ok(n) => { // If `s` is '-0', then `parse()` returns // `BigInt::zero()`, but we need to return diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 7136f5e76..6ed3cb67d 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -54,6 +54,16 @@ fn test_hex_big_number() { ); } +#[test] +fn test_hex_identifier_in_wrong_place() { + new_ucmd!() + .args(&["1234ABCD0x"]) + .fails() + .no_stdout() + .stderr_contains("invalid hexadecimal argument: '1234ABCD0x'") + .stderr_contains("for more information."); +} + #[test] fn test_rejects_nan() { let ts = TestScenario::new(util_name!()); From 3300d80e3ffacf279748cc077b0bebb2688f48b6 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Sat, 25 Sep 2021 11:02:49 +0200 Subject: [PATCH 039/131] tests: silence clippy warnings for unused_imports --- tests/test_util_name.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_util_name.rs b/tests/test_util_name.rs index b0a78a2e8..76b6f4728 100644 --- a/tests/test_util_name.rs +++ b/tests/test_util_name.rs @@ -1,3 +1,4 @@ +#![allow(unused_imports)] mod common; use common::util::TestScenario; @@ -25,6 +26,7 @@ fn execution_phrase_double() { #[test] #[cfg(feature = "ls")] +#[cfg(any(unix, windows))] fn execution_phrase_single() { use std::process::Command; @@ -63,6 +65,7 @@ fn util_name_double() { #[test] #[cfg(feature = "sort")] +#[cfg(any(unix, windows))] fn util_name_single() { use std::{ io::Write, From a9bfd5df0626168dd3bcbba20799182f48e4d19d Mon Sep 17 00:00:00 2001 From: Chad Brewbaker Date: Sat, 9 Oct 2021 23:01:05 -0500 Subject: [PATCH 040/131] Fix need for ssh keys Github now mandates git ssh keys instead of username/password. I modified the download instructions to remove the need for ssh authentication. --- util/build-gnu.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/build-gnu.sh b/util/build-gnu.sh index bbb16981e..eb8293fbe 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -5,12 +5,12 @@ set -e if test ! -d ../gnu; then echo "Could not find ../gnu" - echo "git clone git@github.com:coreutils/coreutils.git gnu" + echo "git clone https://github.com:coreutils/coreutils.git gnu" exit 1 fi if test ! -d ../gnulib; then echo "Could not find ../gnulib" - echo "git clone git@github.com:coreutils/gnulib.git gnulib" + echo "git clone https://github.com/coreutils/gnulib.git gnulib" exit 1 fi From 00de952592d97c1c717dfe769ab4d57473a1743a Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 10 Oct 2021 09:57:39 +0200 Subject: [PATCH 041/131] Fix various 'if_then_panic' clippy warnings --- tests/by-util/test_dd.rs | 8 ++--- tests/by-util/test_od.rs | 21 +++++++----- tests/common/util.rs | 72 ++++++++++++++++------------------------ 3 files changed, 42 insertions(+), 59 deletions(-) diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 918be21f3..8340c7059 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -28,9 +28,7 @@ macro_rules! fixture_path { macro_rules! assert_fixture_exists { ($fname:expr) => {{ let fpath = fixture_path!($fname); - if !fpath.exists() { - panic!("Fixture missing: {:?}", fpath); - } + assert!(fpath.exists(), "Fixture missing: {:?}", fpath); }}; } @@ -38,9 +36,7 @@ macro_rules! assert_fixture_exists { macro_rules! assert_fixture_not_exists { ($fname:expr) => {{ let fpath = PathBuf::from(format!("./fixtures/dd/{}", $fname)); - if fpath.exists() { - panic!("Fixture present: {:?}", fpath); - } + assert!(!fpath.exists(), "Fixture present: {:?}", fpath); }}; } diff --git a/tests/by-util/test_od.rs b/tests/by-util/test_od.rs index 0fc1d5106..59a4bae60 100644 --- a/tests/by-util/test_od.rs +++ b/tests/by-util/test_od.rs @@ -35,9 +35,10 @@ fn test_file() { { let mut f = File::create(&file).unwrap(); // spell-checker:disable-next-line - if f.write_all(b"abcdefghijklmnopqrstuvwxyz\n").is_err() { - panic!("Test setup failed - could not write file"); - } + assert!( + !f.write_all(b"abcdefghijklmnopqrstuvwxyz\n").is_err(), + "Test setup failed - could not write file" + ); } new_ucmd!() @@ -75,9 +76,10 @@ fn test_2files() { // spell-checker:disable-next-line for &(path, data) in &[(&file1, "abcdefghijklmnop"), (&file2, "qrstuvwxyz\n")] { let mut f = File::create(&path).unwrap(); - if f.write_all(data.as_bytes()).is_err() { - panic!("Test setup failed - could not write file"); - } + assert!( + !f.write_all(data.as_bytes()).is_err(), + "Test setup failed - could not write file" + ); } new_ucmd!() @@ -126,9 +128,10 @@ fn test_from_mixed() { let (data1, data2, data3) = ("abcdefg", "hijklmnop", "qrstuvwxyz\n"); for &(path, data) in &[(&file1, data1), (&file3, data3)] { let mut f = File::create(&path).unwrap(); - if f.write_all(data.as_bytes()).is_err() { - panic!("Test setup failed - could not write file"); - } + assert!( + !f.write_all(data.as_bytes()).is_err(), + "Test setup failed - could not write file" + ); } new_ucmd!() diff --git a/tests/common/util.rs b/tests/common/util.rs index 8e9078e9c..61576a087 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -163,25 +163,23 @@ impl CmdResult { /// asserts that the command resulted in a success (zero) status code pub fn success(&self) -> &CmdResult { - if !self.success { - panic!( - "Command was expected to succeed.\nstdout = {}\n stderr = {}", - self.stdout_str(), - self.stderr_str() - ); - } + assert!( + self.success, + "Command was expected to succeed.\nstdout = {}\n stderr = {}", + self.stdout_str(), + self.stderr_str() + ); self } /// asserts that the command resulted in a failure (non-zero) status code pub fn failure(&self) -> &CmdResult { - if self.success { - panic!( - "Command was expected to fail.\nstdout = {}\n stderr = {}", - self.stdout_str(), - self.stderr_str() - ); - } + assert!( + !self.success, + "Command was expected to fail.\nstdout = {}\n stderr = {}", + self.stdout_str(), + self.stderr_str() + ); self } @@ -197,12 +195,11 @@ impl CmdResult { /// 1. you can not know exactly what stdout will be or /// 2. you know that stdout will also be empty pub fn no_stderr(&self) -> &CmdResult { - if !self.stderr.is_empty() { - panic!( - "Expected stderr to be empty, but it's:\n{}", - self.stderr_str() - ); - } + assert!( + self.stderr.is_empty(), + "Expected stderr to be empty, but it's:\n{}", + self.stderr_str() + ); self } @@ -213,12 +210,11 @@ impl CmdResult { /// 1. you can not know exactly what stderr will be or /// 2. you know that stderr will also be empty pub fn no_stdout(&self) -> &CmdResult { - if !self.stdout.is_empty() { - panic!( - "Expected stdout to be empty, but it's:\n{}", - self.stderr_str() - ); - } + assert!( + self.stdout.is_empty(), + "Expected stdout to be empty, but it's:\n{}", + self.stderr_str() + ); self } @@ -868,9 +864,7 @@ impl UCommand { /// Add a parameter to the invocation. Path arguments are treated relative /// to the test environment directory. pub fn arg>(&mut self, arg: S) -> &mut UCommand { - if self.has_run { - panic!("{}", ALREADY_RUN); - } + assert!(!self.has_run, ALREADY_RUN); self.comm_string.push(' '); self.comm_string .push_str(arg.as_ref().to_str().unwrap_or_default()); @@ -881,9 +875,7 @@ impl UCommand { /// Add multiple parameters to the invocation. Path arguments are treated relative /// to the test environment directory. pub fn args>(&mut self, args: &[S]) -> &mut UCommand { - if self.has_run { - panic!("{}", MULTIPLE_STDIN_MEANINGLESS); - } + assert!(!self.has_run, MULTIPLE_STDIN_MEANINGLESS); let strings = args .iter() .map(|s| s.as_ref().to_os_string()) @@ -901,9 +893,7 @@ impl UCommand { /// provides standard input to feed in to the command when spawned pub fn pipe_in>>(&mut self, input: T) -> &mut UCommand { - if self.bytes_into_stdin.is_some() { - panic!("{}", MULTIPLE_STDIN_MEANINGLESS); - } + assert!(!self.bytes_into_stdin.is_some(), MULTIPLE_STDIN_MEANINGLESS); self.bytes_into_stdin = Some(input.into()); self } @@ -918,9 +908,7 @@ impl UCommand { /// This is typically useful to test non-standard workflows /// like feeding something to a command that does not read it pub fn ignore_stdin_write_error(&mut self) -> &mut UCommand { - if self.bytes_into_stdin.is_none() { - panic!("{}", NO_STDIN_MEANINGLESS); - } + assert!(!self.bytes_into_stdin.is_none(), NO_STDIN_MEANINGLESS); self.ignore_stdin_write_error = true; self } @@ -930,9 +918,7 @@ impl UCommand { K: AsRef, V: AsRef, { - if self.has_run { - panic!("{}", ALREADY_RUN); - } + assert!(!self.has_run, ALREADY_RUN); self.raw.env(key, val); self } @@ -951,9 +937,7 @@ impl UCommand { /// Spawns the command, feeds the stdin if any, and returns the /// child process immediately. pub fn run_no_wait(&mut self) -> Child { - if self.has_run { - panic!("{}", ALREADY_RUN); - } + assert!(!self.has_run, ALREADY_RUN); self.has_run = true; log_info("run", &self.comm_string); let mut child = self From b4864d760eb51230b6b4517e93f76298bcaa0f58 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 10 Oct 2021 11:33:46 +0200 Subject: [PATCH 042/131] pr: fix locking of stdout --- src/uu/pr/src/pr.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index 45d9480a7..0886a9991 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -20,7 +20,7 @@ use quick_error::ResultExt; use regex::Regex; use std::convert::From; use std::fs::{metadata, File}; -use std::io::{stdin, stdout, BufRead, BufReader, Lines, Read, Stdout, Write}; +use std::io::{stdin, stdout, BufRead, BufReader, Lines, Read, Write}; #[cfg(unix)] use std::os::unix::fs::FileTypeExt; @@ -1036,15 +1036,16 @@ fn print_page(lines: &[FileLine], options: &OutputOptions, page: usize) -> Resul let header = header_content(options, page); let trailer_content = trailer_content(options); - let out = &mut stdout(); - out.lock(); + let out = stdout(); + let mut out = out.lock(); + for x in header { out.write_all(x.as_bytes())?; out.write_all(line_separator)?; } - let lines_written = write_columns(lines, options, out)?; + let lines_written = write_columns(lines, options, &mut out)?; for (index, x) in trailer_content.iter().enumerate() { out.write_all(x.as_bytes())?; @@ -1060,7 +1061,7 @@ fn print_page(lines: &[FileLine], options: &OutputOptions, page: usize) -> Resul fn write_columns( lines: &[FileLine], options: &OutputOptions, - out: &mut Stdout, + out: &mut impl Write, ) -> Result { let line_separator = options.content_line_separator.as_bytes(); From 51613c02eca5db5e935232b61b1a353e6f13966b Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 10 Oct 2021 10:10:46 +0200 Subject: [PATCH 043/131] add a word to ignore... --- tests/by-util/test_od.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/by-util/test_od.rs b/tests/by-util/test_od.rs index 59a4bae60..6c167b325 100644 --- a/tests/by-util/test_od.rs +++ b/tests/by-util/test_od.rs @@ -1,3 +1,4 @@ +// spell-checker:ignore abcdefghijklmnopqrstuvwxyz // * This file is part of the uutils coreutils package. // * // * For the full copyright and license information, please view the LICENSE From e041fda51d59a63783648815f345830f1d0163d9 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Wed, 6 Oct 2021 20:33:13 +0200 Subject: [PATCH 044/131] tac: do not re-compile regular expression for each file --- src/uu/tac/src/tac.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index 4a93a7c65..caf77c4a7 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -44,9 +44,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { raw_separator }; - let files: Vec = match matches.values_of(options::FILE) { - Some(v) => v.map(|v| v.to_owned()).collect(), - None => vec!["-".to_owned()], + let files: Vec<&str> = match matches.values_of(options::FILE) { + Some(v) => v.collect(), + None => vec!["-"], }; tac(files, before, regex, separator) @@ -102,7 +102,7 @@ pub fn uu_app() -> App<'static, 'static> { /// returns [`std::io::Error`]. fn buffer_tac_regex( data: &[u8], - pattern: regex::bytes::Regex, + pattern: ®ex::bytes::Regex, before: bool, ) -> std::io::Result<()> { let mut out = stdout(); @@ -208,10 +208,16 @@ fn buffer_tac(data: &[u8], before: bool, separator: &str) -> std::io::Result<()> Ok(()) } -fn tac(filenames: Vec, before: bool, regex: bool, separator: &str) -> i32 { +fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32 { let mut exit_code = 0; - for filename in &filenames { + let pattern = if regex { + Some(crash_if_err!(1, regex::bytes::Regex::new(separator))) + } else { + None + }; + + for &filename in &filenames { let mut file = BufReader::new(if filename == "-" { Box::new(stdin()) as Box } else { @@ -244,8 +250,7 @@ fn tac(filenames: Vec, before: bool, regex: bool, separator: &str) -> i3 exit_code = 1; continue; }; - if regex { - let pattern = crash_if_err!(1, regex::bytes::Regex::new(separator)); + if let Some(pattern) = &pattern { buffer_tac_regex(&data, pattern, before) } else { buffer_tac(&data, before, separator) From 0d583754cad9f4d6587469a428fb41a69059818c Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Wed, 6 Oct 2021 21:38:08 +0200 Subject: [PATCH 045/131] tac: lock stdout only once instead for each line --- src/uu/tac/src/tac.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index caf77c4a7..eab6a943b 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -105,7 +105,8 @@ fn buffer_tac_regex( pattern: ®ex::bytes::Regex, before: bool, ) -> std::io::Result<()> { - let mut out = stdout(); + let out = stdout(); + let mut out = out.lock(); // The index of the line separator for the current line. // @@ -171,7 +172,8 @@ fn buffer_tac_regex( /// `separator` appears at the beginning of each line, as in /// `"/abc/def"`. fn buffer_tac(data: &[u8], before: bool, separator: &str) -> std::io::Result<()> { - let mut out = stdout(); + let out = stdout(); + let mut out = out.lock(); // The number of bytes in the line separator. let slen = separator.as_bytes().len(); From 28b04fa89950ff1bac7863934050ef9c26a0022d Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Wed, 6 Oct 2021 20:50:34 +0200 Subject: [PATCH 046/131] tac: do not use a buffered read as fs::read is more efficient and io::Stdin is buffered internally --- src/uu/tac/src/tac.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index eab6a943b..92bf1ea00 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -12,8 +12,8 @@ extern crate uucore; use clap::{crate_version, App, Arg}; use memchr::memmem; -use std::io::{stdin, stdout, BufReader, Read, Write}; -use std::{fs::File, path::Path}; +use std::io::{stdin, stdout, Read, Write}; +use std::{fs::read, path::Path}; use uucore::display::Quotable; use uucore::InvalidEncodingHandling; @@ -220,8 +220,14 @@ fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32 }; for &filename in &filenames { - let mut file = BufReader::new(if filename == "-" { - Box::new(stdin()) as Box + let data = if filename == "-" { + let mut data = Vec::new(); + if let Err(e) = stdin().read_to_end(&mut data) { + show_error!("failed to read from stdin: {}", e); + exit_code = 1; + continue; + } + data } else { let path = Path::new(filename); if path.is_dir() || path.metadata().is_err() { @@ -236,22 +242,16 @@ fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32 exit_code = 1; continue; } - match File::open(path) { - Ok(f) => Box::new(f) as Box, + match read(path) { + Ok(data) => data, Err(e) => { - show_error!("failed to open {} for reading: {}", filename.quote(), e); + show_error!("failed to read {}: {}", filename.quote(), e); exit_code = 1; continue; } } - }); - - let mut data = Vec::new(); - if let Err(e) = file.read_to_end(&mut data) { - show_error!("failed to read {}: {}", filename.quote(), e); - exit_code = 1; - continue; }; + if let Some(pattern) = &pattern { buffer_tac_regex(&data, pattern, before) } else { From 4eab275235f2d89ea48bb7b2b921ea33a8db3fa7 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sat, 9 Oct 2021 12:12:57 +0200 Subject: [PATCH 047/131] tac: buffer stdout more coarsely than line-based following the GNU tac implementation. --- src/uu/tac/src/tac.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index 92bf1ea00..370e92230 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -12,7 +12,7 @@ extern crate uucore; use clap::{crate_version, App, Arg}; use memchr::memmem; -use std::io::{stdin, stdout, Read, Write}; +use std::io::{stdin, stdout, BufWriter, Read, Write}; use std::{fs::read, path::Path}; use uucore::display::Quotable; use uucore::InvalidEncodingHandling; @@ -106,7 +106,7 @@ fn buffer_tac_regex( before: bool, ) -> std::io::Result<()> { let out = stdout(); - let mut out = out.lock(); + let mut out = BufWriter::new(out.lock()); // The index of the line separator for the current line. // @@ -173,7 +173,7 @@ fn buffer_tac_regex( /// `"/abc/def"`. fn buffer_tac(data: &[u8], before: bool, separator: &str) -> std::io::Result<()> { let out = stdout(); - let mut out = out.lock(); + let mut out = BufWriter::new(out.lock()); // The number of bytes in the line separator. let slen = separator.as_bytes().len(); From c526df57b8e10b94b096476c5eaf20eae52e7061 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Wed, 6 Oct 2021 21:08:11 +0200 Subject: [PATCH 048/131] tac: opportunistically use memory maps Since tac must read its input files completely to start processing them from the end, it is particularly suited to use memory maps to benefit from the page cache maintained by the operating systems to bring the necessary data into memory as required. This does also include situations where the input is stdin, but not via a pipe but for example a file descriptor set up by the user's shell through an input redirection. --- Cargo.lock | 10 ++++++ src/uu/tac/Cargo.toml | 3 ++ src/uu/tac/src/tac.rs | 72 +++++++++++++++++++++++++++++++++---------- 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c30a182d..3633928c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1064,6 +1064,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +[[package]] +name = "memmap2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4647a11b578fead29cdbb34d4adef8dd3dc35b876c9c6d5240d83f205abfe96e" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.4" @@ -3025,6 +3034,7 @@ version = "0.0.7" dependencies = [ "clap", "memchr 2.4.0", + "memmap2", "regex", "uucore", "uucore_procs", diff --git a/src/uu/tac/Cargo.toml b/src/uu/tac/Cargo.toml index 1e436e916..00803c8d2 100644 --- a/src/uu/tac/Cargo.toml +++ b/src/uu/tac/Cargo.toml @@ -1,3 +1,5 @@ +# spell-checker:ignore memmap + [package] name = "uu_tac" version = "0.0.7" @@ -16,6 +18,7 @@ path = "src/tac.rs" [dependencies] memchr = "2" +memmap2 = "0.5" regex = "1" clap = { version = "2.33", features = ["wrap_help"] } uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index 370e92230..cdb2d74e3 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -5,15 +5,19 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -// spell-checker:ignore (ToDO) sbytes slen dlen memmem +// spell-checker:ignore (ToDO) sbytes slen dlen memmem memmap Mmap mmap SIGBUS #[macro_use] extern crate uucore; use clap::{crate_version, App, Arg}; use memchr::memmem; +use memmap2::Mmap; use std::io::{stdin, stdout, BufWriter, Read, Write}; -use std::{fs::read, path::Path}; +use std::{ + fs::{read, File}, + path::Path, +}; use uucore::display::Quotable; use uucore::InvalidEncodingHandling; @@ -220,14 +224,23 @@ fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32 }; for &filename in &filenames { - let data = if filename == "-" { - let mut data = Vec::new(); - if let Err(e) = stdin().read_to_end(&mut data) { - show_error!("failed to read from stdin: {}", e); - exit_code = 1; - continue; + let mmap; + let buf; + + let data: &[u8] = if filename == "-" { + if let Some(mmap1) = try_mmap_stdin() { + mmap = mmap1; + &mmap + } else { + let mut buf1 = Vec::new(); + if let Err(e) = stdin().read_to_end(&mut buf1) { + show_error!("failed to read from stdin: {}", e); + exit_code = 1; + continue; + } + buf = buf1; + &buf } - data } else { let path = Path::new(filename); if path.is_dir() || path.metadata().is_err() { @@ -242,22 +255,47 @@ fn tac(filenames: Vec<&str>, before: bool, regex: bool, separator: &str) -> i32 exit_code = 1; continue; } - match read(path) { - Ok(data) => data, - Err(e) => { - show_error!("failed to read {}: {}", filename.quote(), e); - exit_code = 1; - continue; + + if let Some(mmap1) = try_mmap_path(path) { + mmap = mmap1; + &mmap + } else { + match read(path) { + Ok(buf1) => { + buf = buf1; + &buf + } + Err(e) => { + show_error!("failed to read {}: {}", filename.quote(), e); + exit_code = 1; + continue; + } } } }; if let Some(pattern) = &pattern { - buffer_tac_regex(&data, pattern, before) + buffer_tac_regex(data, pattern, before) } else { - buffer_tac(&data, before, separator) + buffer_tac(data, before, separator) } .unwrap_or_else(|e| crash!(1, "failed to write to stdout: {}", e)); } exit_code } + +fn try_mmap_stdin() -> Option { + // SAFETY: If the file is truncated while we map it, SIGBUS will be raised + // and our process will be terminated, thus preventing access of invalid memory. + unsafe { Mmap::map(&stdin()).ok() } +} + +fn try_mmap_path(path: &Path) -> Option { + let file = File::open(path).ok()?; + + // SAFETY: If the file is truncated while we map it, SIGBUS will be raised + // and our process will be terminated, thus preventing access of invalid memory. + let mmap = unsafe { Mmap::map(&file).ok()? }; + + Some(mmap) +} From 86d22aaa1d4a1bd252083cfd2a144bbfa5198c8f Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 10 Oct 2021 13:54:35 +0200 Subject: [PATCH 049/131] tac: Add a simple how to for benchmarking --- src/uu/tac/BENCHMARKING.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/uu/tac/BENCHMARKING.md diff --git a/src/uu/tac/BENCHMARKING.md b/src/uu/tac/BENCHMARKING.md new file mode 100644 index 000000000..4e6d13ea2 --- /dev/null +++ b/src/uu/tac/BENCHMARKING.md @@ -0,0 +1,25 @@ +## Benchmarking `tac` + + + +`tac` is often used to process log files in reverse chronological order, i.e. from newer towards older entries. In this case, the performance target to yield results as fast as possible, i.e. without reading in the whole file that is to be reversed line-by-line. Therefore, a sensible benchmark is to read a large log file containing N lines and measure how long it takes to produce the last K lines from that file. + +Large text files can for example be found in the [Wikipedia database dumps](https://dumps.wikimedia.org/wikidatawiki/latest/), usually sized at multiple gigabytes and comprising more than 100M lines. + +After you have obtained and uncompressed such a file, you need to build `tac` in release mode + +```shell +$ cargo build --release --package uu_tac +``` + +and then you can time how it long it takes to extract the last 10M lines by running + +```shell +$ /usr/bin/time ./target/release/tac wikidatawiki-20211001-pages-logging.xml | head -n10000000 >/dev/null +``` + +For more systematic measurements that include warm-ups, repetitions and comparisons, [Hyperfine](https://github.com/sharkdp/hyperfine) can be helpful. For example, to compare this implementation to the one provided by your distribution run + +```shell +$ hyperfine "./target/release/tac wikidatawiki-20211001-pages-logging.xml | head -n10000000 >/dev/null" "/usr/bin/tac wikidatawiki-20211001-pages-logging.xml | head -n10000000 >/dev/null" +``` From 9e21d26b7ca92233a717ebc95cf7a670e393000d Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 11 Oct 2021 16:51:14 -0400 Subject: [PATCH 050/131] tests: add template string to assert! statements Add missing "{}" template strings to `assert!()` statements. --- tests/common/util.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/common/util.rs b/tests/common/util.rs index 61576a087..1f6a4f6aa 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -864,7 +864,7 @@ impl UCommand { /// Add a parameter to the invocation. Path arguments are treated relative /// to the test environment directory. pub fn arg>(&mut self, arg: S) -> &mut UCommand { - assert!(!self.has_run, ALREADY_RUN); + assert!(!self.has_run, "{}", ALREADY_RUN); self.comm_string.push(' '); self.comm_string .push_str(arg.as_ref().to_str().unwrap_or_default()); @@ -875,7 +875,7 @@ impl UCommand { /// Add multiple parameters to the invocation. Path arguments are treated relative /// to the test environment directory. pub fn args>(&mut self, args: &[S]) -> &mut UCommand { - assert!(!self.has_run, MULTIPLE_STDIN_MEANINGLESS); + assert!(!self.has_run, "{}", MULTIPLE_STDIN_MEANINGLESS); let strings = args .iter() .map(|s| s.as_ref().to_os_string()) @@ -893,7 +893,11 @@ impl UCommand { /// provides standard input to feed in to the command when spawned pub fn pipe_in>>(&mut self, input: T) -> &mut UCommand { - assert!(!self.bytes_into_stdin.is_some(), MULTIPLE_STDIN_MEANINGLESS); + assert!( + !self.bytes_into_stdin.is_some(), + "{}", + MULTIPLE_STDIN_MEANINGLESS + ); self.bytes_into_stdin = Some(input.into()); self } @@ -908,7 +912,7 @@ impl UCommand { /// This is typically useful to test non-standard workflows /// like feeding something to a command that does not read it pub fn ignore_stdin_write_error(&mut self) -> &mut UCommand { - assert!(!self.bytes_into_stdin.is_none(), NO_STDIN_MEANINGLESS); + assert!(!self.bytes_into_stdin.is_none(), "{}", NO_STDIN_MEANINGLESS); self.ignore_stdin_write_error = true; self } @@ -918,7 +922,7 @@ impl UCommand { K: AsRef, V: AsRef, { - assert!(!self.has_run, ALREADY_RUN); + assert!(!self.has_run, "{}", ALREADY_RUN); self.raw.env(key, val); self } @@ -937,7 +941,7 @@ impl UCommand { /// Spawns the command, feeds the stdin if any, and returns the /// child process immediately. pub fn run_no_wait(&mut self) -> Child { - assert!(!self.has_run, ALREADY_RUN); + assert!(!self.has_run, "{}", ALREADY_RUN); self.has_run = true; log_info("run", &self.comm_string); let mut child = self From c50b5ac11015a19fb6782b43ce5b4fe1ca93936d Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 11 Oct 2021 17:36:08 -0400 Subject: [PATCH 051/131] hashsum: fix handling of \r\n in Windows text mode Fix a bug in which "\r\n" was not being replaced with "\n" in text mode on Windows. This would happen only if one call to `write()` ended with a "\r" character and the next call to `write()` started with a "\n" character. This commit fixes the bug by buffering a "\r" character if it appears at the end of one call to `write()` and only writing if the first character in the next call to `write()` is *not* a "\n" character. Fixes issue #2681. --- src/uu/hashsum/src/digest.rs | 93 ++++++++++++++++++++++++++++++----- src/uu/hashsum/src/hashsum.rs | 8 +++ 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/src/uu/hashsum/src/digest.rs b/src/uu/hashsum/src/digest.rs index 531dc7e4f..809223fa7 100644 --- a/src/uu/hashsum/src/digest.rs +++ b/src/uu/hashsum/src/digest.rs @@ -189,13 +189,31 @@ pub struct DigestWriter<'a> { /// "\n" before passing input bytes to the [`digest`]. #[allow(dead_code)] binary: bool, - // TODO This is dead code only on non-Windows operating systems. It - // might be better to use a `#[cfg(windows)]` guard here. + + /// Whether the previous + #[allow(dead_code)] + was_last_character_carriage_return: bool, + // TODO These are dead code only on non-Windows operating systems. + // It might be better to use a `#[cfg(windows)]` guard here. } impl<'a> DigestWriter<'a> { pub fn new(digest: &'a mut Box, binary: bool) -> DigestWriter { - DigestWriter { digest, binary } + let was_last_character_carriage_return = false; + DigestWriter { + digest, + binary, + was_last_character_carriage_return, + } + } + + pub fn finalize(&mut self) -> bool { + if self.was_last_character_carriage_return { + self.digest.input(&[b'\r']); + true + } else { + false + } } } @@ -213,22 +231,40 @@ impl<'a> Write for DigestWriter<'a> { return Ok(buf.len()); } - // In Windows text mode, replace each occurrence of "\r\n" - // with "\n". + // The remaining code handles Windows text mode, where we must + // replace each occurrence of "\r\n" with "\n". // - // Find all occurrences of "\r\n", inputting the slice just - // before the "\n" in the previous instance of "\r\n" and - // the beginning of this "\r\n". - // - // FIXME This fails if one call to `write()` ends with the - // "\r" and the next call to `write()` begins with the "\n". + // First, if the last character written was "\r" and the first + // character in the current buffer to write is not "\n", then we + // need to write the "\r" that we buffered from the previous + // call to `write()`. let n = buf.len(); + if self.was_last_character_carriage_return && n > 0 && buf[0] != b'\n' { + self.digest.input(&[b'\r']); + } + + // Next, find all occurrences of "\r\n", inputting the slice + // just before the "\n" in the previous instance of "\r\n" and + // the beginning of this "\r\n". let mut i_prev = 0; for i in memmem::find_iter(buf, b"\r\n") { self.digest.input(&buf[i_prev..i]); i_prev = i + 1; } - self.digest.input(&buf[i_prev..n]); + + // Finally, check whether the last character is "\r". If so, + // buffer it until we know that the next character is not "\n", + // which can only be known on the next call to `write()`. + // + // This all assumes that `write()` will be called on adjacent + // blocks of the input. + if n > 0 && buf[n - 1] == b'\r' { + self.was_last_character_carriage_return = true; + self.digest.input(&buf[i_prev..n - 1]); + } else { + self.was_last_character_carriage_return = false; + self.digest.input(&buf[i_prev..n]); + } // Even though we dropped a "\r" for each "\r\n" we found, we // still report the number of bytes written as `n`. This is @@ -243,3 +279,36 @@ impl<'a> Write for DigestWriter<'a> { Ok(()) } } + +#[cfg(test)] +mod tests { + + /// Test for replacing a "\r\n" sequence with "\n" when the "\r" is + /// at the end of one block and the "\n" is at the beginning of the + /// next block, when reading in blocks. + #[cfg(windows)] + #[test] + fn test_crlf_across_blocks() { + use std::io::Write; + + use crate::digest::Digest; + use crate::digest::DigestWriter; + + // Writing "\r" in one call to `write()`, and then "\n" in another. + let mut digest = Box::new(md5::Context::new()) as Box; + let mut writer_crlf = DigestWriter::new(&mut digest, false); + writer_crlf.write(&[b'\r']).unwrap(); + writer_crlf.write(&[b'\n']).unwrap(); + writer_crlf.finalize(); + let result_crlf = digest.result_str(); + + // We expect "\r\n" to be replaced with "\n" in text mode on Windows. + let mut digest = Box::new(md5::Context::new()) as Box; + let mut writer_lf = DigestWriter::new(&mut digest, false); + writer_lf.write(&[b'\n']).unwrap(); + writer_lf.finalize(); + let result_lf = digest.result_str(); + + assert_eq!(result_crlf, result_lf); + } +} diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index 4186043f5..07070ed1b 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -611,8 +611,16 @@ fn digest_reader( // If `binary` is `false` and the operating system is Windows, then // `DigestWriter` replaces "\r\n" with "\n" before it writes the // bytes into `digest`. Otherwise, it just inserts the bytes as-is. + // + // In order to support replacing "\r\n", we must call `finalize()` + // in order to support the possibility that the last character read + // from the reader was "\r". (This character gets buffered by + // `DigestWriter` and only written if the following character is + // "\n". But when "\r" is the last character read, we need to force + // it to be written.) let mut digest_writer = DigestWriter::new(digest, binary); std::io::copy(reader, &mut digest_writer)?; + digest_writer.finalize(); if digest.output_bits() > 0 { Ok(digest.result_str()) From d1e02665bfcb2c530f147ad89678eb6eb8a11a32 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 11 Oct 2021 18:18:29 -0400 Subject: [PATCH 052/131] fixup! hashsum: fix handling of \r\n in Windows text mode --- src/uu/hashsum/src/digest.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uu/hashsum/src/digest.rs b/src/uu/hashsum/src/digest.rs index 809223fa7..61f425662 100644 --- a/src/uu/hashsum/src/digest.rs +++ b/src/uu/hashsum/src/digest.rs @@ -297,15 +297,15 @@ mod tests { // Writing "\r" in one call to `write()`, and then "\n" in another. let mut digest = Box::new(md5::Context::new()) as Box; let mut writer_crlf = DigestWriter::new(&mut digest, false); - writer_crlf.write(&[b'\r']).unwrap(); - writer_crlf.write(&[b'\n']).unwrap(); + writer_crlf.write_all(&[b'\r']).unwrap(); + writer_crlf.write_all(&[b'\n']).unwrap(); writer_crlf.finalize(); let result_crlf = digest.result_str(); // We expect "\r\n" to be replaced with "\n" in text mode on Windows. let mut digest = Box::new(md5::Context::new()) as Box; let mut writer_lf = DigestWriter::new(&mut digest, false); - writer_lf.write(&[b'\n']).unwrap(); + writer_lf.write_all(&[b'\n']).unwrap(); writer_lf.finalize(); let result_lf = digest.result_str(); From 429e1d0f12eadf6a709bbdbf55aa2c0ce70657df Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 11 Oct 2021 18:29:08 -0400 Subject: [PATCH 053/131] head: use default() instead of new() for options Remove the `HeadOptions::new()` function in favor of the `Default` implementation. Both were implemented, but only `Default::default()` is needed. --- src/uu/head/src/head.rs | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index ead734088..c6635ac67 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -108,6 +108,12 @@ enum Modes { Bytes(usize), } +impl Default for Modes { + fn default() -> Self { + Modes::Lines(10) + } +} + fn parse_mode(src: &str, closure: F) -> Result<(Modes, bool), String> where F: FnOnce(usize) -> Modes, @@ -144,7 +150,7 @@ fn arg_iterate<'a>( } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Default)] struct HeadOptions { pub quiet: bool, pub verbose: bool, @@ -155,22 +161,11 @@ struct HeadOptions { } impl HeadOptions { - pub fn new() -> HeadOptions { - HeadOptions { - quiet: false, - verbose: false, - zeroed: false, - all_but_last: false, - mode: Modes::Lines(10), - files: Vec::new(), - } - } - ///Construct options from matches pub fn get_from(args: impl uucore::Args) -> Result { let matches = uu_app().get_matches_from(arg_iterate(args)?); - let mut options = HeadOptions::new(); + let mut options: HeadOptions = Default::default(); options.quiet = matches.is_present(options::QUIET_NAME); options.verbose = matches.is_present(options::VERBOSE_NAME); @@ -197,12 +192,6 @@ impl HeadOptions { Ok(options) } } -// to make clippy shut up -impl Default for HeadOptions { - fn default() -> Self { - Self::new() - } -} fn read_n_bytes(input: R, n: usize) -> std::io::Result<()> where @@ -523,17 +512,13 @@ mod tests { assert!(options("-c IsThisJustFantasy").is_err()); } #[test] - #[allow(clippy::bool_comparison)] fn test_options_correct_defaults() { - let opts = HeadOptions::new(); - let opts2: HeadOptions = Default::default(); + let opts: HeadOptions = Default::default(); - assert_eq!(opts, opts2); - - assert!(opts.verbose == false); - assert!(opts.quiet == false); - assert!(opts.zeroed == false); - assert!(opts.all_but_last == false); + assert!(!opts.verbose); + assert!(!opts.quiet); + assert!(!opts.zeroed); + assert!(!opts.all_but_last); assert_eq!(opts.mode, Modes::Lines(10)); assert!(opts.files.is_empty()); } From 0b2483452a175fff50664063c37bd9dde0dec288 Mon Sep 17 00:00:00 2001 From: James Robson Date: Mon, 11 Oct 2021 21:23:52 +0100 Subject: [PATCH 054/131] Add symbolic mode support to mkdir --- src/uu/chmod/src/chmod.rs | 26 +-------- src/uu/mkdir/src/mkdir.rs | 91 ++++++++++++++++++++++++----- src/uucore/src/lib/features/mode.rs | 24 ++++++++ tests/by-util/test_chmod.rs | 2 +- tests/by-util/test_mkdir.rs | 35 +++++++++++ 5 files changed, 136 insertions(+), 42 deletions(-) diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index e25202fbe..68c55b4cb 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -57,7 +57,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { // Before we can parse 'args' with clap (and previously getopts), // a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE"). - let mode_had_minus_prefix = strip_minus_from_mode(&mut args); + let mode_had_minus_prefix = mode::strip_minus_from_mode(&mut args); let usage = usage(); let after_help = get_long_usage(); @@ -180,30 +180,6 @@ pub fn uu_app() -> App<'static, 'static> { ) } -// Iterate 'args' and delete the first occurrence -// of a prefix '-' if it's associated with MODE -// e.g. "chmod -v -xw -R FILE" -> "chmod -v xw -R FILE" -pub fn strip_minus_from_mode(args: &mut Vec) -> bool { - for arg in args { - if arg == "--" { - break; - } - if arg.starts_with('-') { - if let Some(second) = arg.chars().nth(1) { - match second { - 'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'..='7' => { - // TODO: use strip_prefix() once minimum rust version reaches 1.45.0 - *arg = arg[1..arg.len()].to_string(); - return true; - } - _ => {} - } - } - } - } - false -} - struct Chmoder { changes: bool, quiet: bool, diff --git a/src/uu/mkdir/src/mkdir.rs b/src/uu/mkdir/src/mkdir.rs index 92c068408..9ff816dcb 100644 --- a/src/uu/mkdir/src/mkdir.rs +++ b/src/uu/mkdir/src/mkdir.rs @@ -5,15 +5,22 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. +// spell-checker:ignore (ToDO) ugoa cmode + #[macro_use] extern crate uucore; use clap::OsValues; -use clap::{crate_version, App, Arg}; +use clap::{crate_version, App, Arg, ArgMatches}; use std::fs; use std::path::Path; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError}; +#[cfg(not(windows))] +use uucore::mode; +use uucore::InvalidEncodingHandling; + +static DEFAULT_PERM: u32 = 0o755; static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist"; mod options { @@ -26,29 +33,81 @@ mod options { fn usage() -> String { format!("{0} [OPTION]... [USER]", uucore::execution_phrase()) } +fn get_long_usage() -> String { + String::from("Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.") +} + +#[cfg(windows)] +fn get_mode(_matches: &ArgMatches, _mode_had_minus_prefix: bool) -> Result { + Ok(DEFAULT_PERM) +} + +#[cfg(not(windows))] +fn get_mode(matches: &ArgMatches, mode_had_minus_prefix: bool) -> Result { + let digits: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + // Translate a ~str in octal form to u16, default to 755 + // Not tested on Windows + let mut new_mode = DEFAULT_PERM; + match matches.value_of(options::MODE) { + Some(m) => { + for mode in m.split(',') { + if mode.contains(digits) { + new_mode = mode::parse_numeric(new_mode, m, true)?; + } else { + let cmode = if mode_had_minus_prefix { + // clap parsing is finished, now put prefix back + format!("-{}", mode) + } else { + mode.to_string() + }; + new_mode = mode::parse_symbolic(new_mode, &cmode, mode::get_umask(), true)?; + } + } + Ok(new_mode) + } + None => Ok(DEFAULT_PERM), + } +} + +#[cfg(windows)] +fn strip_minus_from_mode(_args: &mut Vec) -> bool { + false +} + +#[cfg(not(windows))] +fn strip_minus_from_mode(args: &mut Vec) -> bool { + mode::strip_minus_from_mode(args) +} #[uucore_procs::gen_uumain] pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let mut args = args + .collect_str(InvalidEncodingHandling::ConvertLossy) + .accept_any(); + + // Before we can parse 'args' with clap (and previously getopts), + // a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE"). + let mode_had_minus_prefix = strip_minus_from_mode(&mut args); + let usage = usage(); + let after_help = get_long_usage(); // Linux-specific options, not implemented // opts.optflag("Z", "context", "set SELinux security context" + // " of each created directory to CTX"), - let matches = uu_app().usage(&usage[..]).get_matches_from(args); + let matches = uu_app() + .usage(&usage[..]) + .after_help(&after_help[..]) + .get_matches_from(args); let dirs = matches.values_of_os(options::DIRS).unwrap_or_default(); let verbose = matches.is_present(options::VERBOSE); let recursive = matches.is_present(options::PARENTS); - // Translate a ~str in octal form to u16, default to 755 - // Not tested on Windows - let mode: u16 = match matches.value_of(options::MODE) { - Some(m) => u16::from_str_radix(m, 8) - .map_err(|_| USimpleError::new(1, format!("invalid mode {}", m.quote())))?, - None => 0o755_u16, - }; - - exec(dirs, recursive, mode, verbose) + match get_mode(&matches, mode_had_minus_prefix) { + Ok(mode) => exec(dirs, recursive, mode, verbose), + Err(f) => Err(USimpleError::new(1, f)), + } } pub fn uu_app() -> App<'static, 'static> { @@ -86,7 +145,7 @@ pub fn uu_app() -> App<'static, 'static> { /** * Create the list of new directories */ -fn exec(dirs: OsValues, recursive: bool, mode: u16, verbose: bool) -> UResult<()> { +fn exec(dirs: OsValues, recursive: bool, mode: u32, verbose: bool) -> UResult<()> { for dir in dirs { let path = Path::new(dir); show_if_err!(mkdir(path, recursive, mode, verbose)); @@ -94,7 +153,7 @@ fn exec(dirs: OsValues, recursive: bool, mode: u16, verbose: bool) -> UResult<() Ok(()) } -fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> UResult<()> { +fn mkdir(path: &Path, recursive: bool, mode: u32, verbose: bool) -> UResult<()> { let create_dir = if recursive { fs::create_dir_all } else { @@ -115,18 +174,18 @@ fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> UResult<()> } #[cfg(any(unix, target_os = "redox"))] -fn chmod(path: &Path, mode: u16) -> UResult<()> { +fn chmod(path: &Path, mode: u32) -> UResult<()> { use std::fs::{set_permissions, Permissions}; use std::os::unix::fs::PermissionsExt; - let mode = Permissions::from_mode(u32::from(mode)); + let mode = Permissions::from_mode(mode); set_permissions(path, mode) .map_err_context(|| format!("cannot set permissions {}", path.quote())) } #[cfg(windows)] -fn chmod(_path: &Path, _mode: u16) -> UResult<()> { +fn chmod(_path: &Path, _mode: u32) -> UResult<()> { // chmod on Windows only sets the readonly flag, which isn't even honored on directories Ok(()) } diff --git a/src/uucore/src/lib/features/mode.rs b/src/uucore/src/lib/features/mode.rs index 72083e799..5e299f988 100644 --- a/src/uucore/src/lib/features/mode.rs +++ b/src/uucore/src/lib/features/mode.rs @@ -155,6 +155,30 @@ pub fn get_umask() -> u32 { mask as u32 } +// Iterate 'args' and delete the first occurrence +// of a prefix '-' if it's associated with MODE +// e.g. "chmod -v -xw -R FILE" -> "chmod -v xw -R FILE" +pub fn strip_minus_from_mode(args: &mut Vec) -> bool { + for arg in args { + if arg == "--" { + break; + } + if arg.starts_with('-') { + if let Some(second) = arg.chars().nth(1) { + match second { + 'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'..='7' => { + // TODO: use strip_prefix() once minimum rust version reaches 1.45.0 + *arg = arg[1..arg.len()].to_string(); + return true; + } + _ => {} + } + } + } + } + false +} + #[cfg(test)] mod test { diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index 1b8983bc3..c8348d491 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -4,7 +4,7 @@ use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; use std::sync::Mutex; extern crate libc; -use self::chmod::strip_minus_from_mode; +use uucore::mode::strip_minus_from_mode; extern crate chmod; use self::libc::umask; diff --git a/tests/by-util/test_mkdir.rs b/tests/by-util/test_mkdir.rs index 54a6fe3c8..f3451fb5e 100644 --- a/tests/by-util/test_mkdir.rs +++ b/tests/by-util/test_mkdir.rs @@ -1,4 +1,6 @@ use crate::common::util::*; +#[cfg(not(windows))] +use std::os::unix::fs::PermissionsExt; static TEST_DIR1: &str = "mkdir_test1"; static TEST_DIR2: &str = "mkdir_test2"; @@ -65,3 +67,36 @@ fn test_mkdir_dup_file() { // mkdir should fail for a file even if -p is specified. scene.ucmd().arg("-p").arg(TEST_FILE7).fails(); } + +#[test] +#[cfg(not(windows))] +fn test_symbolic_mode() { + let (at, mut ucmd) = at_and_ucmd!(); + + ucmd.arg("-m").arg("a=rwx").arg(TEST_DIR1).succeeds(); + let perms = at.metadata(TEST_DIR1).permissions().mode(); + assert_eq!(perms, 0o40777) +} + +#[test] +#[cfg(not(windows))] +fn test_symbolic_alteration() { + let (at, mut ucmd) = at_and_ucmd!(); + + ucmd.arg("-m").arg("-w").arg(TEST_DIR1).succeeds(); + let perms = at.metadata(TEST_DIR1).permissions().mode(); + assert_eq!(perms, 0o40555) +} + +#[test] +#[cfg(not(windows))] +fn test_multi_symbolic() { + let (at, mut ucmd) = at_and_ucmd!(); + + ucmd.arg("-m") + .arg("u=rwx,g=rx,o=") + .arg(TEST_DIR1) + .succeeds(); + let perms = at.metadata(TEST_DIR1).permissions().mode(); + assert_eq!(perms, 0o40750) +} From cb34b660cbba77c94c35e23b38909246f3558591 Mon Sep 17 00:00:00 2001 From: Smicry Date: Tue, 19 Oct 2021 00:15:17 +0800 Subject: [PATCH 055/131] add tail usage --- src/uu/head/src/head.rs | 1 - src/uu/tail/src/tail.rs | 13 +++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index c6635ac67..42b7c0fda 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -19,7 +19,6 @@ const BUF_SIZE: usize = 65536; const ABOUT: &str = "\ Print the first 10 lines of each FILE to standard output.\n\ With more than one FILE, precede each with a header giving the file name.\n\ - \n\ With no FILE, or when FILE is -, read standard input.\n\ \n\ Mandatory arguments to long flags are mandatory for short flags too.\ diff --git a/src/uu/tail/src/tail.rs b/src/uu/tail/src/tail.rs index 89fbe4d36..eaf7bf8bf 100644 --- a/src/uu/tail/src/tail.rs +++ b/src/uu/tail/src/tail.rs @@ -35,6 +35,15 @@ use crate::platform::stdin_is_pipe_or_fifo; #[cfg(unix)] use std::os::unix::fs::MetadataExt; +const ABOUT: &str = "\ + Print the last 10 lines of each FILE to standard output.\n\ + With more than one FILE, precede each with a header giving the file name.\n\ + With no FILE, or when FILE is -, read standard input.\n\ + \n\ + Mandatory arguments to long flags are mandatory for short flags too.\ + "; +const USAGE: &str = "tail [FLAG]... [FILE]..."; + pub mod options { pub mod verbosity { pub static QUIET: &str = "quiet"; @@ -218,8 +227,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { pub fn uu_app() -> App<'static, 'static> { App::new(uucore::util_name()) .version(crate_version!()) - .about("output the last part of files") - // TODO: add usage + .about(ABOUT) + .usage(USAGE) .arg( Arg::with_name(options::BYTES) .short("c") From 7c5395a27a0d9fafef98bad2ae4ad3e0876bab72 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Mon, 18 Oct 2021 23:47:09 +0200 Subject: [PATCH 056/131] build.rs: silence clippy warnings --- build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.rs b/build.rs index 4fbb27cce..261eb5d9a 100644 --- a/build.rs +++ b/build.rs @@ -46,6 +46,8 @@ pub fn main() { "type UtilityMap = HashMap<&'static str, (fn(T) -> i32, fn() -> App<'static, 'static>)>;\n\ \n\ fn util_map() -> UtilityMap {\n\ + \t#[allow(unused_mut)]\n\ + \t#[allow(clippy::let_and_return)]\n\ \tlet mut map = UtilityMap::new();\n\ " .as_bytes(), From bfa8a2a068f58eb63228ea9a6d8392d70144b721 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Tue, 19 Oct 2021 01:12:12 +0200 Subject: [PATCH 057/131] tests/util: add more wrappers for common file handling tasks truncate, rename, remove, copy, rmdir, etc. --- tests/common/util.rs | 57 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/tests/common/util.rs b/tests/common/util.rs index 61576a087..d60fb3952 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -510,43 +510,86 @@ impl AtPath { } pub fn write(&self, name: &str, contents: &str) { - log_info("open(write)", self.plus_as_string(name)); + log_info("write(default)", self.plus_as_string(name)); std::fs::write(self.plus(name), contents) .unwrap_or_else(|e| panic!("Couldn't write {}: {}", name, e)); } pub fn write_bytes(&self, name: &str, contents: &[u8]) { - log_info("open(write)", self.plus_as_string(name)); + log_info("write(default)", self.plus_as_string(name)); std::fs::write(self.plus(name), contents) .unwrap_or_else(|e| panic!("Couldn't write {}: {}", name, e)); } pub fn append(&self, name: &str, contents: &str) { - log_info("open(append)", self.plus_as_string(name)); + log_info("write(append)", self.plus_as_string(name)); let mut f = OpenOptions::new() .write(true) .append(true) + .create(true) .open(self.plus(name)) .unwrap(); f.write_all(contents.as_bytes()) - .unwrap_or_else(|e| panic!("Couldn't write {}: {}", name, e)); + .unwrap_or_else(|e| panic!("Couldn't write(append) {}: {}", name, e)); } pub fn append_bytes(&self, name: &str, contents: &[u8]) { - log_info("open(append)", self.plus_as_string(name)); + log_info("write(append)", self.plus_as_string(name)); let mut f = OpenOptions::new() .write(true) .append(true) + .create(true) .open(self.plus(name)) .unwrap(); f.write_all(contents) - .unwrap_or_else(|e| panic!("Couldn't append to {}: {}", name, e)); + .unwrap_or_else(|e| panic!("Couldn't write(append) to {}: {}", name, e)); + } + + pub fn truncate(&self, name: &str, contents: &str) { + log_info("write(truncate)", self.plus_as_string(name)); + let mut f = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(self.plus(name)) + .unwrap(); + f.write_all(contents.as_bytes()) + .unwrap_or_else(|e| panic!("Couldn't write(truncate) {}: {}", name, e)); + } + + pub fn rename(&self, source: &str, target: &str) { + let source = self.plus(source); + let target = self.plus(target); + log_info("rename", format!("{:?} {:?}", source, target)); + std::fs::rename(&source, &target) + .unwrap_or_else(|e| panic!("Couldn't rename {:?} -> {:?}: {}", source, target, e)); + } + + pub fn remove(&self, source: &str) { + let source = self.plus(source); + log_info("remove", format!("{:?}", source)); + std::fs::remove_file(&source) + .unwrap_or_else(|e| panic!("Couldn't remove {:?}: {}", source, e)); + } + + pub fn copy(&self, source: &str, target: &str) { + let source = self.plus(source); + let target = self.plus(target); + log_info("copy", format!("{:?} {:?}", source, target)); + std::fs::copy(&source, &target) + .unwrap_or_else(|e| panic!("Couldn't copy {:?} -> {:?}: {}", source, target, e)); + } + + pub fn rmdir(&self, dir: &str) { + log_info("rmdir", self.plus_as_string(dir)); + fs::remove_dir(&self.plus(dir)).unwrap(); } pub fn mkdir(&self, dir: &str) { log_info("mkdir", self.plus_as_string(dir)); fs::create_dir(&self.plus(dir)).unwrap(); } + pub fn mkdir_all(&self, dir: &str) { log_info("mkdir_all", self.plus_as_string(dir)); fs::create_dir_all(self.plus(dir)).unwrap(); @@ -1020,6 +1063,8 @@ impl UCommand { } } +/// Wrapper for `child.stdout.read_exact()`. +/// Careful, this blocks indefinitely if `size` bytes is never reached. pub fn read_size(child: &mut Child, size: usize) -> String { let mut output = Vec::new(); output.resize(size, 0); From 2cbc2aa59bd7d154125ab20b1f168ea6f8617cc8 Mon Sep 17 00:00:00 2001 From: Smicry Date: Wed, 20 Oct 2021 23:49:04 +0800 Subject: [PATCH 058/131] head: use UResult in util --- src/uu/head/src/head.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index 42b7c0fda..93404876d 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -10,10 +10,9 @@ use std::convert::TryFrom; use std::ffi::OsString; use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; use uucore::display::Quotable; -use uucore::{crash, show_error_custom_description}; +use uucore::error::{UResult, USimpleError}; +use uucore::show_error_custom_description; -const EXIT_FAILURE: i32 = 1; -const EXIT_SUCCESS: i32 = 0; const BUF_SIZE: usize = 65536; const ABOUT: &str = "\ @@ -372,7 +371,7 @@ fn head_file(input: &mut std::fs::File, options: &HeadOptions) -> std::io::Resul } } -fn uu_head(options: &HeadOptions) -> Result<(), u32> { +fn uu_head(options: &HeadOptions) -> UResult<()> { let mut error_count = 0; let mut first = true; for file in &options.files { @@ -445,23 +444,21 @@ fn uu_head(options: &HeadOptions) -> Result<(), u32> { first = false; } if error_count > 0 { - Err(error_count) + Err(USimpleError::new(1, format!(""))) } else { Ok(()) } } -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = match HeadOptions::get_from(args) { Ok(o) => o, Err(s) => { - crash!(EXIT_FAILURE, "{}", s); + return Err(USimpleError::new(1, format!("{}", s))); } }; - match uu_head(&args) { - Ok(_) => EXIT_SUCCESS, - Err(_) => EXIT_FAILURE, - } + uu_head(&args) } #[cfg(test)] From 396fa7a9b4037166dae1e63ca6007f8184694ecb Mon Sep 17 00:00:00 2001 From: Smicry Date: Thu, 21 Oct 2021 00:13:28 +0800 Subject: [PATCH 059/131] fix lint error --- src/uu/head/src/head.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index 93404876d..67f73a397 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -444,7 +444,7 @@ fn uu_head(options: &HeadOptions) -> UResult<()> { first = false; } if error_count > 0 { - Err(USimpleError::new(1, format!(""))) + Err(USimpleError::new(1, "")) } else { Ok(()) } @@ -455,7 +455,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = match HeadOptions::get_from(args) { Ok(o) => o, Err(s) => { - return Err(USimpleError::new(1, format!("{}", s))); + return Err(USimpleError::new(1, s.to_string())); } }; uu_head(&args) From b89b11f5daf1549a3a025b0e9a0c88f23ae51d1f Mon Sep 17 00:00:00 2001 From: Smicry Date: Thu, 21 Oct 2021 00:22:36 +0800 Subject: [PATCH 060/131] fix lint error --- src/uu/head/src/head.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index 67f73a397..c33ec693b 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -455,7 +455,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = match HeadOptions::get_from(args) { Ok(o) => o, Err(s) => { - return Err(USimpleError::new(1, s.to_string())); + return Err(USimpleError::new(1, s)); } }; uu_head(&args) From 858b0a9e9f6e00b288d6a77675e45b1d1ec3465e Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sun, 12 Sep 2021 22:33:20 -0400 Subject: [PATCH 061/131] head: use std::io::copy() with TakeLines reader Replace the custom `split::walk_lines()` function with a call to `std::io::copy()`, using a new `TakeLines` reader as the source and `stdout` as the destination. The `TakeLines` reader is an adaptor that scans the bytes being read for line ending characters and stops the reading after a given number of lines has been read (similar to the `std::io::Take` adaptor). This change * makes the `read_n_lines()` function more concise, * allows it to mirror the implementation of `read_n_bytes()`, * increases the speed of `head -n NUM`. --- Cargo.lock | 1 + src/uu/head/BENCHMARKING.md | 41 ++++++++++++++++++ src/uu/head/Cargo.toml | 1 + src/uu/head/src/head.rs | 39 ++++++++--------- src/uu/head/src/split.rs | 60 -------------------------- src/uu/head/src/take.rs | 86 +++++++++++++++++++++++++++++++++++++ 6 files changed, 146 insertions(+), 82 deletions(-) create mode 100644 src/uu/head/BENCHMARKING.md delete mode 100644 src/uu/head/src/split.rs diff --git a/Cargo.lock b/Cargo.lock index 3633928c6..0cffb8d73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2507,6 +2507,7 @@ name = "uu_head" version = "0.0.7" dependencies = [ "clap", + "memchr 2.4.0", "uucore", "uucore_procs", ] diff --git a/src/uu/head/BENCHMARKING.md b/src/uu/head/BENCHMARKING.md new file mode 100644 index 000000000..49574eb79 --- /dev/null +++ b/src/uu/head/BENCHMARKING.md @@ -0,0 +1,41 @@ +# Benchmarking to measure performance + +To compare the performance of the `uutils` version of `head` with the +GNU version of `head`, you can use a benchmarking tool like +[hyperfine][0]. On Ubuntu 18.04 or later, you can install `hyperfine` by +running + + sudo apt-get install hyperfine + +Next, build the `head` binary under the release profile: + + cargo build --release -p uu_head + +Now, get a text file to test `head` on. I used the *Complete Works of +William Shakespeare*, which is in the public domain in the United States +and most other parts of the world. + + wget -O shakespeare.txt https://www.gutenberg.org/files/100/100-0.txt + +This particular file has about 170,000 lines, each of which is no longer +than 96 characters: + + $ wc -lL shakespeare.txt + 170592 96 shakespeare.txt + +You could use files of different shapes and sizes to test the +performance of `head` in different situations. For a larger file, you +could download a [database dump of Wikidata][1] or some related files +that the Wikimedia project provides. For example, [this file][2] +contains about 130 million lines. + +Finally, you can compare the performance of the two versions of `head` +by running, for example, + + hyperfine \ + "head -n 100000 shakespeare.txt" \ + "target/release/head -n 100000 shakespeare.txt" + +[0]: https://github.com/sharkdp/hyperfine +[1]: https://www.wikidata.org/wiki/Wikidata:Database_download +[2]: https://dumps.wikimedia.org/wikidatawiki/20211001/wikidatawiki-20211001-pages-logging.xml.gz diff --git a/src/uu/head/Cargo.toml b/src/uu/head/Cargo.toml index 4fa4c0c81..3a615812b 100644 --- a/src/uu/head/Cargo.toml +++ b/src/uu/head/Cargo.toml @@ -16,6 +16,7 @@ path = "src/head.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } +memchr = "2" uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["ringbuffer"] } uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index 42b7c0fda..7199cf936 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -3,12 +3,12 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -// spell-checker:ignore (vars) zlines +// spell-checker:ignore (vars) zlines BUFWRITER use clap::{crate_version, App, Arg}; use std::convert::TryFrom; use std::ffi::OsString; -use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; +use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write}; use uucore::display::Quotable; use uucore::{crash, show_error_custom_description}; @@ -16,6 +16,9 @@ const EXIT_FAILURE: i32 = 1; const EXIT_SUCCESS: i32 = 0; const BUF_SIZE: usize = 65536; +/// The capacity in bytes for buffered writers. +const BUFWRITER_CAPACITY: usize = 16_384; // 16 kilobytes + const ABOUT: &str = "\ Print the first 10 lines of each FILE to standard output.\n\ With more than one FILE, precede each with a header giving the file name.\n\ @@ -35,10 +38,10 @@ mod options { } mod lines; mod parse; -mod split; mod take; use lines::zlines; use take::take_all_but; +use take::take_lines; pub fn uu_app() -> App<'static, 'static> { App::new(uucore::util_name()) @@ -209,26 +212,18 @@ where } fn read_n_lines(input: &mut impl std::io::BufRead, n: usize, zero: bool) -> std::io::Result<()> { - if n == 0 { - return Ok(()); - } + // Read the first `n` lines from the `input` reader. + let separator = if zero { b'\0' } else { b'\n' }; + let mut reader = take_lines(input, n, separator); + + // Write those bytes to `stdout`. let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - let mut lines = 0usize; - split::walk_lines(input, zero, |e| match e { - split::Event::Data(dat) => { - stdout.write_all(dat)?; - Ok(true) - } - split::Event::Line => { - lines += 1; - if lines == n { - Ok(false) - } else { - Ok(true) - } - } - }) + let stdout = stdout.lock(); + let mut writer = BufWriter::with_capacity(BUFWRITER_CAPACITY, stdout); + + io::copy(&mut reader, &mut writer)?; + + Ok(()) } fn read_but_last_n_bytes(input: &mut impl std::io::BufRead, n: usize) -> std::io::Result<()> { diff --git a/src/uu/head/src/split.rs b/src/uu/head/src/split.rs deleted file mode 100644 index 9e9a0c685..000000000 --- a/src/uu/head/src/split.rs +++ /dev/null @@ -1,60 +0,0 @@ -#[derive(Debug)] -pub enum Event<'a> { - Data(&'a [u8]), - Line, -} -/// Loops over the lines read from a BufRead. -/// # Arguments -/// * `input` the ReadBuf to read from -/// * `zero` whether to use 0u8 as a line delimiter -/// * `on_event` a closure receiving some bytes read in a slice, or -/// event signalling a line was just read. -/// this is guaranteed to be signalled *directly* after the -/// slice containing the (CR on win)LF / 0 is passed -/// -/// Return whether to continue -pub fn walk_lines( - input: &mut impl std::io::BufRead, - zero: bool, - mut on_event: F, -) -> std::io::Result<()> -where - F: FnMut(Event) -> std::io::Result, -{ - let mut buffer = [0u8; super::BUF_SIZE]; - loop { - let read = loop { - match input.read(&mut buffer) { - Ok(n) => break n, - Err(e) => match e.kind() { - std::io::ErrorKind::Interrupted => {} - _ => return Err(e), - }, - } - }; - if read == 0 { - return Ok(()); - } - let mut base = 0usize; - for (i, byte) in buffer[..read].iter().enumerate() { - match byte { - b'\n' if !zero => { - on_event(Event::Data(&buffer[base..=i]))?; - base = i + 1; - if !on_event(Event::Line)? { - return Ok(()); - } - } - 0u8 if zero => { - on_event(Event::Data(&buffer[base..=i]))?; - base = i + 1; - if !on_event(Event::Line)? { - return Ok(()); - } - } - _ => {} - } - } - on_event(Event::Data(&buffer[base..read]))?; - } -} diff --git a/src/uu/head/src/take.rs b/src/uu/head/src/take.rs index 94fa012be..5f4c29b65 100644 --- a/src/uu/head/src/take.rs +++ b/src/uu/head/src/take.rs @@ -1,4 +1,8 @@ //! Take all but the last elements of an iterator. +use std::io::Read; + +use memchr::memchr_iter; + use uucore::ringbuffer::RingBuffer; /// Create an iterator over all but the last `n` elements of `iter`. @@ -58,10 +62,63 @@ where } } +/// Like `std::io::Take`, but for lines instead of bytes. +/// +/// This struct is generally created by calling [`take_lines`] on a +/// reader. Please see the documentation of [`take`] for more +/// details. +pub struct TakeLines { + inner: T, + limit: usize, + separator: u8, +} + +impl Read for TakeLines { + /// Read bytes from a buffer up to the requested number of lines. + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + if self.limit == 0 { + return Ok(0); + } + match self.inner.read(buf) { + Ok(0) => Ok(0), + Ok(n) => { + for i in memchr_iter(self.separator, &buf[..n]) { + self.limit -= 1; + if self.limit == 0 { + return Ok(i + 1); + } + } + Ok(n) + } + Err(e) => Err(e), + } + } +} + +/// Create an adaptor that will read at most `limit` lines from a given reader. +/// +/// This function returns a new instance of `Read` that will read at +/// most `limit` lines, after which it will always return EOF +/// (`Ok(0)`). +/// +/// The `separator` defines the character to interpret as the line +/// ending. For the usual notion of "line", set this to `b'\n'`. +pub fn take_lines(reader: R, limit: usize, separator: u8) -> TakeLines { + TakeLines { + inner: reader, + limit, + separator, + } +} + #[cfg(test)] mod tests { + use std::io::BufRead; + use std::io::BufReader; + use crate::take::take_all_but; + use crate::take::take_lines; #[test] fn test_fewer_elements() { @@ -90,4 +147,33 @@ mod tests { assert_eq!(Some(&2), iter.next()); assert_eq!(None, iter.next()); } + + #[test] + fn test_zero_lines() { + let input_reader = std::io::Cursor::new("a\nb\nc\n"); + let output_reader = BufReader::new(take_lines(input_reader, 0, b'\n')); + let mut iter = output_reader.lines().map(|l| l.unwrap()); + assert_eq!(None, iter.next()); + } + + #[test] + fn test_fewer_lines() { + let input_reader = std::io::Cursor::new("a\nb\nc\n"); + let output_reader = BufReader::new(take_lines(input_reader, 2, b'\n')); + let mut iter = output_reader.lines().map(|l| l.unwrap()); + assert_eq!(Some(String::from("a")), iter.next()); + assert_eq!(Some(String::from("b")), iter.next()); + assert_eq!(None, iter.next()); + } + + #[test] + fn test_more_lines() { + let input_reader = std::io::Cursor::new("a\nb\nc\n"); + let output_reader = BufReader::new(take_lines(input_reader, 4, b'\n')); + let mut iter = output_reader.lines().map(|l| l.unwrap()); + assert_eq!(Some(String::from("a")), iter.next()); + assert_eq!(Some(String::from("b")), iter.next()); + assert_eq!(Some(String::from("c")), iter.next()); + assert_eq!(None, iter.next()); + } } From 4c8b74797fa947c755a123a3fe6c153a86ca5d95 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 23 Oct 2021 18:34:23 +0200 Subject: [PATCH 062/131] Silent question_mark clippy warnings --- src/uu/base32/src/base_common.rs | 2 ++ src/uu/cksum/src/cksum.rs | 2 ++ src/uu/sum/src/sum.rs | 2 ++ src/uu/wc/src/count_fast.rs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index 015925e12..b07203507 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -163,6 +163,8 @@ pub fn handle_input( } else { match data.decode() { Ok(s) => { + // Silent the warning as we want to the error message + #[allow(clippy::question_mark)] if stdout().write_all(&s).is_err() { // on windows console, writing invalid utf8 returns an error return Err(USimpleError::new(1, "error: cannot write non-utf8 data")); diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index e682aa70c..55c4f980f 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -148,6 +148,8 @@ fn cksum(fname: &str) -> io::Result<(u32, usize)> { "Is a directory", )); }; + // Silent the warning as we want to the error message + #[allow(clippy::question_mark)] if path.metadata().is_err() { return Err(std::io::Error::new( io::ErrorKind::NotFound, diff --git a/src/uu/sum/src/sum.rs b/src/uu/sum/src/sum.rs index 15c20f09d..c58a1dcdc 100644 --- a/src/uu/sum/src/sum.rs +++ b/src/uu/sum/src/sum.rs @@ -76,6 +76,8 @@ fn open(name: &str) -> Result> { "Is a directory", )); }; + // Silent the warning as we want to the error message + #[allow(clippy::question_mark)] if path.metadata().is_err() { return Err(std::io::Error::new( std::io::ErrorKind::NotFound, diff --git a/src/uu/wc/src/count_fast.rs b/src/uu/wc/src/count_fast.rs index 9351b6871..e4f6be6c1 100644 --- a/src/uu/wc/src/count_fast.rs +++ b/src/uu/wc/src/count_fast.rs @@ -50,6 +50,8 @@ fn count_bytes_using_splice(fd: &impl AsRawFd) -> Result { Ok(0) => break, Ok(res) => { byte_count += res; + // Silent the warning as we want to the error message + #[allow(clippy::question_mark)] if splice_exact(&pipe_rd, &null_file, res).is_err() { return Err(byte_count); } From 3ffc4c07eb0dc86b98117f6eef73c55bb5a74b84 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 23 Oct 2021 19:19:52 +0200 Subject: [PATCH 063/131] fix a map-clone warning --- src/uu/cat/src/cat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index 308e11bfe..af84890db 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -588,7 +588,7 @@ fn write_tab_to_end(mut in_buf: &[u8], writer: &mut W) -> usize { fn write_nonprint_to_end(in_buf: &[u8], writer: &mut W, tab: &[u8]) -> usize { let mut count = 0; - for byte in in_buf.iter().map(|c| *c) { + for byte in in_buf.iter().copied() { if byte == b'\n' { break; } From 59e9870c5613d04282ae6eec3348231d5a06cbd2 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 23 Oct 2021 19:21:37 +0200 Subject: [PATCH 064/131] Prepare version 0.0.8 --- Cargo.lock | 208 ++++++++++++------------- Cargo.toml | 206 ++++++++++++------------ src/uu/arch/Cargo.toml | 6 +- src/uu/base32/Cargo.toml | 6 +- src/uu/base64/Cargo.toml | 6 +- src/uu/basename/Cargo.toml | 6 +- src/uu/basenc/Cargo.toml | 6 +- src/uu/cat/Cargo.toml | 6 +- src/uu/chcon/Cargo.toml | 2 +- src/uu/chgrp/Cargo.toml | 6 +- src/uu/chmod/Cargo.toml | 6 +- src/uu/chown/Cargo.toml | 6 +- src/uu/chroot/Cargo.toml | 6 +- src/uu/cksum/Cargo.toml | 6 +- src/uu/comm/Cargo.toml | 6 +- src/uu/cp/Cargo.toml | 6 +- src/uu/csplit/Cargo.toml | 6 +- src/uu/cut/Cargo.toml | 6 +- src/uu/date/Cargo.toml | 6 +- src/uu/dd/Cargo.toml | 2 +- src/uu/df/Cargo.toml | 6 +- src/uu/dircolors/Cargo.toml | 6 +- src/uu/dirname/Cargo.toml | 6 +- src/uu/du/Cargo.toml | 6 +- src/uu/echo/Cargo.toml | 6 +- src/uu/env/Cargo.toml | 6 +- src/uu/expand/Cargo.toml | 6 +- src/uu/expr/Cargo.toml | 6 +- src/uu/factor/Cargo.toml | 4 +- src/uu/false/Cargo.toml | 6 +- src/uu/fmt/Cargo.toml | 6 +- src/uu/fold/Cargo.toml | 6 +- src/uu/groups/Cargo.toml | 6 +- src/uu/hashsum/Cargo.toml | 6 +- src/uu/head/Cargo.toml | 6 +- src/uu/hostid/Cargo.toml | 6 +- src/uu/hostname/Cargo.toml | 6 +- src/uu/id/Cargo.toml | 6 +- src/uu/install/Cargo.toml | 6 +- src/uu/join/Cargo.toml | 6 +- src/uu/kill/Cargo.toml | 6 +- src/uu/link/Cargo.toml | 6 +- src/uu/ln/Cargo.toml | 6 +- src/uu/logname/Cargo.toml | 6 +- src/uu/ls/Cargo.toml | 4 +- src/uu/mkdir/Cargo.toml | 6 +- src/uu/mkfifo/Cargo.toml | 6 +- src/uu/mknod/Cargo.toml | 6 +- src/uu/mktemp/Cargo.toml | 6 +- src/uu/more/Cargo.toml | 4 +- src/uu/mv/Cargo.toml | 6 +- src/uu/nice/Cargo.toml | 6 +- src/uu/nl/Cargo.toml | 6 +- src/uu/nohup/Cargo.toml | 6 +- src/uu/nproc/Cargo.toml | 6 +- src/uu/numfmt/Cargo.toml | 6 +- src/uu/od/Cargo.toml | 6 +- src/uu/paste/Cargo.toml | 6 +- src/uu/pathchk/Cargo.toml | 6 +- src/uu/pinky/Cargo.toml | 6 +- src/uu/pr/Cargo.toml | 4 +- src/uu/printenv/Cargo.toml | 6 +- src/uu/printf/Cargo.toml | 6 +- src/uu/ptx/Cargo.toml | 6 +- src/uu/pwd/Cargo.toml | 6 +- src/uu/readlink/Cargo.toml | 6 +- src/uu/realpath/Cargo.toml | 6 +- src/uu/relpath/Cargo.toml | 6 +- src/uu/rm/Cargo.toml | 6 +- src/uu/rmdir/Cargo.toml | 6 +- src/uu/runcon/Cargo.toml | 2 +- src/uu/seq/Cargo.toml | 6 +- src/uu/shred/Cargo.toml | 6 +- src/uu/shuf/Cargo.toml | 6 +- src/uu/sleep/Cargo.toml | 6 +- src/uu/sort/Cargo.toml | 6 +- src/uu/split/Cargo.toml | 6 +- src/uu/stat/Cargo.toml | 6 +- src/uu/stdbuf/Cargo.toml | 8 +- src/uu/stdbuf/src/libstdbuf/Cargo.toml | 6 +- src/uu/sum/Cargo.toml | 6 +- src/uu/sync/Cargo.toml | 6 +- src/uu/tac/Cargo.toml | 6 +- src/uu/tail/Cargo.toml | 6 +- src/uu/tee/Cargo.toml | 6 +- src/uu/test/Cargo.toml | 6 +- src/uu/timeout/Cargo.toml | 6 +- src/uu/touch/Cargo.toml | 6 +- src/uu/tr/Cargo.toml | 6 +- src/uu/true/Cargo.toml | 6 +- src/uu/truncate/Cargo.toml | 6 +- src/uu/tsort/Cargo.toml | 6 +- src/uu/tty/Cargo.toml | 6 +- src/uu/uname/Cargo.toml | 6 +- src/uu/unexpand/Cargo.toml | 6 +- src/uu/uniq/Cargo.toml | 6 +- src/uu/unlink/Cargo.toml | 6 +- src/uu/uptime/Cargo.toml | 6 +- src/uu/users/Cargo.toml | 6 +- src/uu/wc/Cargo.toml | 6 +- src/uu/who/Cargo.toml | 6 +- src/uu/whoami/Cargo.toml | 6 +- src/uu/yes/Cargo.toml | 6 +- src/uucore/Cargo.toml | 2 +- src/uucore_procs/Cargo.toml | 2 +- util/update-version.sh | 12 +- 106 files changed, 509 insertions(+), 509 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0cffb8d73..4a40fd883 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,7 +299,7 @@ dependencies = [ [[package]] name = "coreutils" -version = "0.0.7" +version = "0.0.8" dependencies = [ "atty", "chrono", @@ -2147,7 +2147,7 @@ checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b" [[package]] name = "uu_arch" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "platform-info", @@ -2157,7 +2157,7 @@ dependencies = [ [[package]] name = "uu_base32" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2166,7 +2166,7 @@ dependencies = [ [[package]] name = "uu_base64" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uu_base32", @@ -2176,7 +2176,7 @@ dependencies = [ [[package]] name = "uu_basename" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2185,7 +2185,7 @@ dependencies = [ [[package]] name = "uu_basenc" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uu_base32", @@ -2195,7 +2195,7 @@ dependencies = [ [[package]] name = "uu_cat" -version = "0.0.7" +version = "0.0.8" dependencies = [ "atty", "clap", @@ -2209,7 +2209,7 @@ dependencies = [ [[package]] name = "uu_chcon" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "fts-sys", @@ -2222,7 +2222,7 @@ dependencies = [ [[package]] name = "uu_chgrp" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2231,7 +2231,7 @@ dependencies = [ [[package]] name = "uu_chmod" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2242,7 +2242,7 @@ dependencies = [ [[package]] name = "uu_chown" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2251,7 +2251,7 @@ dependencies = [ [[package]] name = "uu_chroot" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2260,7 +2260,7 @@ dependencies = [ [[package]] name = "uu_cksum" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2270,7 +2270,7 @@ dependencies = [ [[package]] name = "uu_comm" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2280,7 +2280,7 @@ dependencies = [ [[package]] name = "uu_cp" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "exacl", @@ -2298,7 +2298,7 @@ dependencies = [ [[package]] name = "uu_csplit" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "regex", @@ -2309,7 +2309,7 @@ dependencies = [ [[package]] name = "uu_cut" -version = "0.0.7" +version = "0.0.8" dependencies = [ "atty", "bstr", @@ -2321,7 +2321,7 @@ dependencies = [ [[package]] name = "uu_date" -version = "0.0.7" +version = "0.0.8" dependencies = [ "chrono", "clap", @@ -2333,7 +2333,7 @@ dependencies = [ [[package]] name = "uu_dd" -version = "0.0.7" +version = "0.0.8" dependencies = [ "byte-unit", "clap", @@ -2347,7 +2347,7 @@ dependencies = [ [[package]] name = "uu_df" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "number_prefix", @@ -2357,7 +2357,7 @@ dependencies = [ [[package]] name = "uu_dircolors" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "glob", @@ -2367,7 +2367,7 @@ dependencies = [ [[package]] name = "uu_dirname" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2377,7 +2377,7 @@ dependencies = [ [[package]] name = "uu_du" -version = "0.0.7" +version = "0.0.8" dependencies = [ "chrono", "clap", @@ -2388,7 +2388,7 @@ dependencies = [ [[package]] name = "uu_echo" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2397,7 +2397,7 @@ dependencies = [ [[package]] name = "uu_env" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2408,7 +2408,7 @@ dependencies = [ [[package]] name = "uu_expand" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "unicode-width", @@ -2418,7 +2418,7 @@ dependencies = [ [[package]] name = "uu_expr" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2431,7 +2431,7 @@ dependencies = [ [[package]] name = "uu_factor" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "coz", @@ -2446,7 +2446,7 @@ dependencies = [ [[package]] name = "uu_false" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2455,7 +2455,7 @@ dependencies = [ [[package]] name = "uu_fmt" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2466,7 +2466,7 @@ dependencies = [ [[package]] name = "uu_fold" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2475,7 +2475,7 @@ dependencies = [ [[package]] name = "uu_groups" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2484,7 +2484,7 @@ dependencies = [ [[package]] name = "uu_hashsum" -version = "0.0.7" +version = "0.0.8" dependencies = [ "blake2b_simd", "clap", @@ -2504,7 +2504,7 @@ dependencies = [ [[package]] name = "uu_head" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "memchr 2.4.0", @@ -2514,7 +2514,7 @@ dependencies = [ [[package]] name = "uu_hostid" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2524,7 +2524,7 @@ dependencies = [ [[package]] name = "uu_hostname" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "hostname", @@ -2536,7 +2536,7 @@ dependencies = [ [[package]] name = "uu_id" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "selinux", @@ -2546,7 +2546,7 @@ dependencies = [ [[package]] name = "uu_install" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "file_diff", @@ -2559,7 +2559,7 @@ dependencies = [ [[package]] name = "uu_join" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2568,7 +2568,7 @@ dependencies = [ [[package]] name = "uu_kill" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2578,7 +2578,7 @@ dependencies = [ [[package]] name = "uu_link" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2588,7 +2588,7 @@ dependencies = [ [[package]] name = "uu_ln" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2598,7 +2598,7 @@ dependencies = [ [[package]] name = "uu_logname" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2608,7 +2608,7 @@ dependencies = [ [[package]] name = "uu_ls" -version = "0.0.7" +version = "0.0.8" dependencies = [ "atty", "chrono", @@ -2628,7 +2628,7 @@ dependencies = [ [[package]] name = "uu_mkdir" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2638,7 +2638,7 @@ dependencies = [ [[package]] name = "uu_mkfifo" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2648,7 +2648,7 @@ dependencies = [ [[package]] name = "uu_mknod" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2658,7 +2658,7 @@ dependencies = [ [[package]] name = "uu_mktemp" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "rand 0.5.6", @@ -2669,7 +2669,7 @@ dependencies = [ [[package]] name = "uu_more" -version = "0.0.7" +version = "0.0.8" dependencies = [ "atty", "clap", @@ -2685,7 +2685,7 @@ dependencies = [ [[package]] name = "uu_mv" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "fs_extra", @@ -2695,7 +2695,7 @@ dependencies = [ [[package]] name = "uu_nice" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2706,7 +2706,7 @@ dependencies = [ [[package]] name = "uu_nl" -version = "0.0.7" +version = "0.0.8" dependencies = [ "aho-corasick", "clap", @@ -2720,7 +2720,7 @@ dependencies = [ [[package]] name = "uu_nohup" -version = "0.0.7" +version = "0.0.8" dependencies = [ "atty", "clap", @@ -2731,7 +2731,7 @@ dependencies = [ [[package]] name = "uu_nproc" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2742,7 +2742,7 @@ dependencies = [ [[package]] name = "uu_numfmt" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2751,7 +2751,7 @@ dependencies = [ [[package]] name = "uu_od" -version = "0.0.7" +version = "0.0.8" dependencies = [ "byteorder", "clap", @@ -2763,7 +2763,7 @@ dependencies = [ [[package]] name = "uu_paste" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2772,7 +2772,7 @@ dependencies = [ [[package]] name = "uu_pathchk" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2782,7 +2782,7 @@ dependencies = [ [[package]] name = "uu_pinky" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2791,7 +2791,7 @@ dependencies = [ [[package]] name = "uu_pr" -version = "0.0.7" +version = "0.0.8" dependencies = [ "chrono", "clap", @@ -2805,7 +2805,7 @@ dependencies = [ [[package]] name = "uu_printenv" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2814,7 +2814,7 @@ dependencies = [ [[package]] name = "uu_printf" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "itertools 0.8.2", @@ -2824,7 +2824,7 @@ dependencies = [ [[package]] name = "uu_ptx" -version = "0.0.7" +version = "0.0.8" dependencies = [ "aho-corasick", "clap", @@ -2838,7 +2838,7 @@ dependencies = [ [[package]] name = "uu_pwd" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2847,7 +2847,7 @@ dependencies = [ [[package]] name = "uu_readlink" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2857,7 +2857,7 @@ dependencies = [ [[package]] name = "uu_realpath" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2866,7 +2866,7 @@ dependencies = [ [[package]] name = "uu_relpath" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2875,7 +2875,7 @@ dependencies = [ [[package]] name = "uu_rm" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "remove_dir_all", @@ -2887,7 +2887,7 @@ dependencies = [ [[package]] name = "uu_rmdir" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2897,7 +2897,7 @@ dependencies = [ [[package]] name = "uu_runcon" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "fts-sys", @@ -2910,7 +2910,7 @@ dependencies = [ [[package]] name = "uu_seq" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "num-bigint", @@ -2921,7 +2921,7 @@ dependencies = [ [[package]] name = "uu_shred" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -2932,7 +2932,7 @@ dependencies = [ [[package]] name = "uu_shuf" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "rand 0.5.6", @@ -2942,7 +2942,7 @@ dependencies = [ [[package]] name = "uu_sleep" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2951,7 +2951,7 @@ dependencies = [ [[package]] name = "uu_sort" -version = "0.0.7" +version = "0.0.8" dependencies = [ "binary-heap-plus", "clap", @@ -2971,7 +2971,7 @@ dependencies = [ [[package]] name = "uu_split" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2980,7 +2980,7 @@ dependencies = [ [[package]] name = "uu_stat" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -2989,7 +2989,7 @@ dependencies = [ [[package]] name = "uu_stdbuf" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "tempfile", @@ -3000,7 +3000,7 @@ dependencies = [ [[package]] name = "uu_stdbuf_libstdbuf" -version = "0.0.7" +version = "0.0.8" dependencies = [ "cpp", "cpp_build", @@ -3011,7 +3011,7 @@ dependencies = [ [[package]] name = "uu_sum" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -3020,7 +3020,7 @@ dependencies = [ [[package]] name = "uu_sync" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -3031,7 +3031,7 @@ dependencies = [ [[package]] name = "uu_tac" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "memchr 2.4.0", @@ -3043,7 +3043,7 @@ dependencies = [ [[package]] name = "uu_tail" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -3056,7 +3056,7 @@ dependencies = [ [[package]] name = "uu_tee" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -3067,7 +3067,7 @@ dependencies = [ [[package]] name = "uu_test" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -3078,7 +3078,7 @@ dependencies = [ [[package]] name = "uu_timeout" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -3089,7 +3089,7 @@ dependencies = [ [[package]] name = "uu_touch" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "filetime", @@ -3100,7 +3100,7 @@ dependencies = [ [[package]] name = "uu_tr" -version = "0.0.7" +version = "0.0.8" dependencies = [ "bit-set", "clap", @@ -3111,7 +3111,7 @@ dependencies = [ [[package]] name = "uu_true" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -3120,7 +3120,7 @@ dependencies = [ [[package]] name = "uu_truncate" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -3129,7 +3129,7 @@ dependencies = [ [[package]] name = "uu_tsort" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -3138,7 +3138,7 @@ dependencies = [ [[package]] name = "uu_tty" -version = "0.0.7" +version = "0.0.8" dependencies = [ "atty", "clap", @@ -3149,7 +3149,7 @@ dependencies = [ [[package]] name = "uu_uname" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "platform-info", @@ -3159,7 +3159,7 @@ dependencies = [ [[package]] name = "uu_unexpand" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "unicode-width", @@ -3169,7 +3169,7 @@ dependencies = [ [[package]] name = "uu_uniq" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "strum", @@ -3180,7 +3180,7 @@ dependencies = [ [[package]] name = "uu_unlink" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -3189,7 +3189,7 @@ dependencies = [ [[package]] name = "uu_uptime" -version = "0.0.7" +version = "0.0.8" dependencies = [ "chrono", "clap", @@ -3199,7 +3199,7 @@ dependencies = [ [[package]] name = "uu_users" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -3208,7 +3208,7 @@ dependencies = [ [[package]] name = "uu_wc" -version = "0.0.7" +version = "0.0.8" dependencies = [ "bytecount", "clap", @@ -3222,7 +3222,7 @@ dependencies = [ [[package]] name = "uu_who" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "uucore", @@ -3231,7 +3231,7 @@ dependencies = [ [[package]] name = "uu_whoami" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "libc", @@ -3242,7 +3242,7 @@ dependencies = [ [[package]] name = "uu_yes" -version = "0.0.7" +version = "0.0.8" dependencies = [ "clap", "nix 0.20.0", @@ -3252,7 +3252,7 @@ dependencies = [ [[package]] name = "uucore" -version = "0.0.9" +version = "0.0.10" dependencies = [ "clap", "data-encoding", @@ -3275,7 +3275,7 @@ dependencies = [ [[package]] name = "uucore_procs" -version = "0.0.6" +version = "0.0.7" dependencies = [ "proc-macro2", "quote 1.0.9", diff --git a/Cargo.toml b/Cargo.toml index 701dd8b4a..9ecea79c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "coreutils" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust" @@ -247,110 +247,110 @@ test = [ "uu_test" ] clap = { version = "2.33", features = ["wrap_help"] } lazy_static = { version="1.3" } textwrap = { version="0.14", features=["terminal_size"] } -uucore = { version=">=0.0.9", package="uucore", path="src/uucore" } +uucore = { version=">=0.0.10", package="uucore", path="src/uucore" } selinux = { version="0.2.3", optional = true } # * uutils -uu_test = { optional=true, version="0.0.7", package="uu_test", path="src/uu/test" } +uu_test = { optional=true, version="0.0.8", package="uu_test", path="src/uu/test" } # -arch = { optional=true, version="0.0.7", package="uu_arch", path="src/uu/arch" } -base32 = { optional=true, version="0.0.7", package="uu_base32", path="src/uu/base32" } -base64 = { optional=true, version="0.0.7", package="uu_base64", path="src/uu/base64" } -basename = { optional=true, version="0.0.7", package="uu_basename", path="src/uu/basename" } -basenc = { optional=true, version="0.0.7", package="uu_basenc", path="src/uu/basenc" } -cat = { optional=true, version="0.0.7", package="uu_cat", path="src/uu/cat" } -chcon = { optional=true, version="0.0.7", package="uu_chcon", path="src/uu/chcon" } -chgrp = { optional=true, version="0.0.7", package="uu_chgrp", path="src/uu/chgrp" } -chmod = { optional=true, version="0.0.7", package="uu_chmod", path="src/uu/chmod" } -chown = { optional=true, version="0.0.7", package="uu_chown", path="src/uu/chown" } -chroot = { optional=true, version="0.0.7", package="uu_chroot", path="src/uu/chroot" } -cksum = { optional=true, version="0.0.7", package="uu_cksum", path="src/uu/cksum" } -comm = { optional=true, version="0.0.7", package="uu_comm", path="src/uu/comm" } -cp = { optional=true, version="0.0.7", package="uu_cp", path="src/uu/cp" } -csplit = { optional=true, version="0.0.7", package="uu_csplit", path="src/uu/csplit" } -cut = { optional=true, version="0.0.7", package="uu_cut", path="src/uu/cut" } -date = { optional=true, version="0.0.7", package="uu_date", path="src/uu/date" } -dd = { optional=true, version="0.0.7", package="uu_dd", path="src/uu/dd" } -df = { optional=true, version="0.0.7", package="uu_df", path="src/uu/df" } -dircolors= { optional=true, version="0.0.7", package="uu_dircolors", path="src/uu/dircolors" } -dirname = { optional=true, version="0.0.7", package="uu_dirname", path="src/uu/dirname" } -du = { optional=true, version="0.0.7", package="uu_du", path="src/uu/du" } -echo = { optional=true, version="0.0.7", package="uu_echo", path="src/uu/echo" } -env = { optional=true, version="0.0.7", package="uu_env", path="src/uu/env" } -expand = { optional=true, version="0.0.7", package="uu_expand", path="src/uu/expand" } -expr = { optional=true, version="0.0.7", package="uu_expr", path="src/uu/expr" } -factor = { optional=true, version="0.0.7", package="uu_factor", path="src/uu/factor" } -false = { optional=true, version="0.0.7", package="uu_false", path="src/uu/false" } -fmt = { optional=true, version="0.0.7", package="uu_fmt", path="src/uu/fmt" } -fold = { optional=true, version="0.0.7", package="uu_fold", path="src/uu/fold" } -groups = { optional=true, version="0.0.7", package="uu_groups", path="src/uu/groups" } -hashsum = { optional=true, version="0.0.7", package="uu_hashsum", path="src/uu/hashsum" } -head = { optional=true, version="0.0.7", package="uu_head", path="src/uu/head" } -hostid = { optional=true, version="0.0.7", package="uu_hostid", path="src/uu/hostid" } -hostname = { optional=true, version="0.0.7", package="uu_hostname", path="src/uu/hostname" } -id = { optional=true, version="0.0.7", package="uu_id", path="src/uu/id" } -install = { optional=true, version="0.0.7", package="uu_install", path="src/uu/install" } -join = { optional=true, version="0.0.7", package="uu_join", path="src/uu/join" } -kill = { optional=true, version="0.0.7", package="uu_kill", path="src/uu/kill" } -link = { optional=true, version="0.0.7", package="uu_link", path="src/uu/link" } -ln = { optional=true, version="0.0.7", package="uu_ln", path="src/uu/ln" } -ls = { optional=true, version="0.0.7", package="uu_ls", path="src/uu/ls" } -logname = { optional=true, version="0.0.7", package="uu_logname", path="src/uu/logname" } -mkdir = { optional=true, version="0.0.7", package="uu_mkdir", path="src/uu/mkdir" } -mkfifo = { optional=true, version="0.0.7", package="uu_mkfifo", path="src/uu/mkfifo" } -mknod = { optional=true, version="0.0.7", package="uu_mknod", path="src/uu/mknod" } -mktemp = { optional=true, version="0.0.7", package="uu_mktemp", path="src/uu/mktemp" } -more = { optional=true, version="0.0.7", package="uu_more", path="src/uu/more" } -mv = { optional=true, version="0.0.7", package="uu_mv", path="src/uu/mv" } -nice = { optional=true, version="0.0.7", package="uu_nice", path="src/uu/nice" } -nl = { optional=true, version="0.0.7", package="uu_nl", path="src/uu/nl" } -nohup = { optional=true, version="0.0.7", package="uu_nohup", path="src/uu/nohup" } -nproc = { optional=true, version="0.0.7", package="uu_nproc", path="src/uu/nproc" } -numfmt = { optional=true, version="0.0.7", package="uu_numfmt", path="src/uu/numfmt" } -od = { optional=true, version="0.0.7", package="uu_od", path="src/uu/od" } -paste = { optional=true, version="0.0.7", package="uu_paste", path="src/uu/paste" } -pathchk = { optional=true, version="0.0.7", package="uu_pathchk", path="src/uu/pathchk" } -pinky = { optional=true, version="0.0.7", package="uu_pinky", path="src/uu/pinky" } -pr = { optional=true, version="0.0.7", package="uu_pr", path="src/uu/pr" } -printenv = { optional=true, version="0.0.7", package="uu_printenv", path="src/uu/printenv" } -printf = { optional=true, version="0.0.7", package="uu_printf", path="src/uu/printf" } -ptx = { optional=true, version="0.0.7", package="uu_ptx", path="src/uu/ptx" } -pwd = { optional=true, version="0.0.7", package="uu_pwd", path="src/uu/pwd" } -readlink = { optional=true, version="0.0.7", package="uu_readlink", path="src/uu/readlink" } -realpath = { optional=true, version="0.0.7", package="uu_realpath", path="src/uu/realpath" } -relpath = { optional=true, version="0.0.7", package="uu_relpath", path="src/uu/relpath" } -rm = { optional=true, version="0.0.7", package="uu_rm", path="src/uu/rm" } -rmdir = { optional=true, version="0.0.7", package="uu_rmdir", path="src/uu/rmdir" } -runcon = { optional=true, version="0.0.7", package="uu_runcon", path="src/uu/runcon" } -seq = { optional=true, version="0.0.7", package="uu_seq", path="src/uu/seq" } -shred = { optional=true, version="0.0.7", package="uu_shred", path="src/uu/shred" } -shuf = { optional=true, version="0.0.7", package="uu_shuf", path="src/uu/shuf" } -sleep = { optional=true, version="0.0.7", package="uu_sleep", path="src/uu/sleep" } -sort = { optional=true, version="0.0.7", package="uu_sort", path="src/uu/sort" } -split = { optional=true, version="0.0.7", package="uu_split", path="src/uu/split" } -stat = { optional=true, version="0.0.7", package="uu_stat", path="src/uu/stat" } -stdbuf = { optional=true, version="0.0.7", package="uu_stdbuf", path="src/uu/stdbuf" } -sum = { optional=true, version="0.0.7", package="uu_sum", path="src/uu/sum" } -sync = { optional=true, version="0.0.7", package="uu_sync", path="src/uu/sync" } -tac = { optional=true, version="0.0.7", package="uu_tac", path="src/uu/tac" } -tail = { optional=true, version="0.0.7", package="uu_tail", path="src/uu/tail" } -tee = { optional=true, version="0.0.7", package="uu_tee", path="src/uu/tee" } -timeout = { optional=true, version="0.0.7", package="uu_timeout", path="src/uu/timeout" } -touch = { optional=true, version="0.0.7", package="uu_touch", path="src/uu/touch" } -tr = { optional=true, version="0.0.7", package="uu_tr", path="src/uu/tr" } -true = { optional=true, version="0.0.7", package="uu_true", path="src/uu/true" } -truncate = { optional=true, version="0.0.7", package="uu_truncate", path="src/uu/truncate" } -tsort = { optional=true, version="0.0.7", package="uu_tsort", path="src/uu/tsort" } -tty = { optional=true, version="0.0.7", package="uu_tty", path="src/uu/tty" } -uname = { optional=true, version="0.0.7", package="uu_uname", path="src/uu/uname" } -unexpand = { optional=true, version="0.0.7", package="uu_unexpand", path="src/uu/unexpand" } -uniq = { optional=true, version="0.0.7", package="uu_uniq", path="src/uu/uniq" } -unlink = { optional=true, version="0.0.7", package="uu_unlink", path="src/uu/unlink" } -uptime = { optional=true, version="0.0.7", package="uu_uptime", path="src/uu/uptime" } -users = { optional=true, version="0.0.7", package="uu_users", path="src/uu/users" } -wc = { optional=true, version="0.0.7", package="uu_wc", path="src/uu/wc" } -who = { optional=true, version="0.0.7", package="uu_who", path="src/uu/who" } -whoami = { optional=true, version="0.0.7", package="uu_whoami", path="src/uu/whoami" } -yes = { optional=true, version="0.0.7", package="uu_yes", path="src/uu/yes" } +arch = { optional=true, version="0.0.8", package="uu_arch", path="src/uu/arch" } +base32 = { optional=true, version="0.0.8", package="uu_base32", path="src/uu/base32" } +base64 = { optional=true, version="0.0.8", package="uu_base64", path="src/uu/base64" } +basename = { optional=true, version="0.0.8", package="uu_basename", path="src/uu/basename" } +basenc = { optional=true, version="0.0.8", package="uu_basenc", path="src/uu/basenc" } +cat = { optional=true, version="0.0.8", package="uu_cat", path="src/uu/cat" } +chcon = { optional=true, version="0.0.8", package="uu_chcon", path="src/uu/chcon" } +chgrp = { optional=true, version="0.0.8", package="uu_chgrp", path="src/uu/chgrp" } +chmod = { optional=true, version="0.0.8", package="uu_chmod", path="src/uu/chmod" } +chown = { optional=true, version="0.0.8", package="uu_chown", path="src/uu/chown" } +chroot = { optional=true, version="0.0.8", package="uu_chroot", path="src/uu/chroot" } +cksum = { optional=true, version="0.0.8", package="uu_cksum", path="src/uu/cksum" } +comm = { optional=true, version="0.0.8", package="uu_comm", path="src/uu/comm" } +cp = { optional=true, version="0.0.8", package="uu_cp", path="src/uu/cp" } +csplit = { optional=true, version="0.0.8", package="uu_csplit", path="src/uu/csplit" } +cut = { optional=true, version="0.0.8", package="uu_cut", path="src/uu/cut" } +date = { optional=true, version="0.0.8", package="uu_date", path="src/uu/date" } +dd = { optional=true, version="0.0.8", package="uu_dd", path="src/uu/dd" } +df = { optional=true, version="0.0.8", package="uu_df", path="src/uu/df" } +dircolors= { optional=true, version="0.0.8", package="uu_dircolors", path="src/uu/dircolors" } +dirname = { optional=true, version="0.0.8", package="uu_dirname", path="src/uu/dirname" } +du = { optional=true, version="0.0.8", package="uu_du", path="src/uu/du" } +echo = { optional=true, version="0.0.8", package="uu_echo", path="src/uu/echo" } +env = { optional=true, version="0.0.8", package="uu_env", path="src/uu/env" } +expand = { optional=true, version="0.0.8", package="uu_expand", path="src/uu/expand" } +expr = { optional=true, version="0.0.8", package="uu_expr", path="src/uu/expr" } +factor = { optional=true, version="0.0.8", package="uu_factor", path="src/uu/factor" } +false = { optional=true, version="0.0.8", package="uu_false", path="src/uu/false" } +fmt = { optional=true, version="0.0.8", package="uu_fmt", path="src/uu/fmt" } +fold = { optional=true, version="0.0.8", package="uu_fold", path="src/uu/fold" } +groups = { optional=true, version="0.0.8", package="uu_groups", path="src/uu/groups" } +hashsum = { optional=true, version="0.0.8", package="uu_hashsum", path="src/uu/hashsum" } +head = { optional=true, version="0.0.8", package="uu_head", path="src/uu/head" } +hostid = { optional=true, version="0.0.8", package="uu_hostid", path="src/uu/hostid" } +hostname = { optional=true, version="0.0.8", package="uu_hostname", path="src/uu/hostname" } +id = { optional=true, version="0.0.8", package="uu_id", path="src/uu/id" } +install = { optional=true, version="0.0.8", package="uu_install", path="src/uu/install" } +join = { optional=true, version="0.0.8", package="uu_join", path="src/uu/join" } +kill = { optional=true, version="0.0.8", package="uu_kill", path="src/uu/kill" } +link = { optional=true, version="0.0.8", package="uu_link", path="src/uu/link" } +ln = { optional=true, version="0.0.8", package="uu_ln", path="src/uu/ln" } +ls = { optional=true, version="0.0.8", package="uu_ls", path="src/uu/ls" } +logname = { optional=true, version="0.0.8", package="uu_logname", path="src/uu/logname" } +mkdir = { optional=true, version="0.0.8", package="uu_mkdir", path="src/uu/mkdir" } +mkfifo = { optional=true, version="0.0.8", package="uu_mkfifo", path="src/uu/mkfifo" } +mknod = { optional=true, version="0.0.8", package="uu_mknod", path="src/uu/mknod" } +mktemp = { optional=true, version="0.0.8", package="uu_mktemp", path="src/uu/mktemp" } +more = { optional=true, version="0.0.8", package="uu_more", path="src/uu/more" } +mv = { optional=true, version="0.0.8", package="uu_mv", path="src/uu/mv" } +nice = { optional=true, version="0.0.8", package="uu_nice", path="src/uu/nice" } +nl = { optional=true, version="0.0.8", package="uu_nl", path="src/uu/nl" } +nohup = { optional=true, version="0.0.8", package="uu_nohup", path="src/uu/nohup" } +nproc = { optional=true, version="0.0.8", package="uu_nproc", path="src/uu/nproc" } +numfmt = { optional=true, version="0.0.8", package="uu_numfmt", path="src/uu/numfmt" } +od = { optional=true, version="0.0.8", package="uu_od", path="src/uu/od" } +paste = { optional=true, version="0.0.8", package="uu_paste", path="src/uu/paste" } +pathchk = { optional=true, version="0.0.8", package="uu_pathchk", path="src/uu/pathchk" } +pinky = { optional=true, version="0.0.8", package="uu_pinky", path="src/uu/pinky" } +pr = { optional=true, version="0.0.8", package="uu_pr", path="src/uu/pr" } +printenv = { optional=true, version="0.0.8", package="uu_printenv", path="src/uu/printenv" } +printf = { optional=true, version="0.0.8", package="uu_printf", path="src/uu/printf" } +ptx = { optional=true, version="0.0.8", package="uu_ptx", path="src/uu/ptx" } +pwd = { optional=true, version="0.0.8", package="uu_pwd", path="src/uu/pwd" } +readlink = { optional=true, version="0.0.8", package="uu_readlink", path="src/uu/readlink" } +realpath = { optional=true, version="0.0.8", package="uu_realpath", path="src/uu/realpath" } +relpath = { optional=true, version="0.0.8", package="uu_relpath", path="src/uu/relpath" } +rm = { optional=true, version="0.0.8", package="uu_rm", path="src/uu/rm" } +rmdir = { optional=true, version="0.0.8", package="uu_rmdir", path="src/uu/rmdir" } +runcon = { optional=true, version="0.0.8", package="uu_runcon", path="src/uu/runcon" } +seq = { optional=true, version="0.0.8", package="uu_seq", path="src/uu/seq" } +shred = { optional=true, version="0.0.8", package="uu_shred", path="src/uu/shred" } +shuf = { optional=true, version="0.0.8", package="uu_shuf", path="src/uu/shuf" } +sleep = { optional=true, version="0.0.8", package="uu_sleep", path="src/uu/sleep" } +sort = { optional=true, version="0.0.8", package="uu_sort", path="src/uu/sort" } +split = { optional=true, version="0.0.8", package="uu_split", path="src/uu/split" } +stat = { optional=true, version="0.0.8", package="uu_stat", path="src/uu/stat" } +stdbuf = { optional=true, version="0.0.8", package="uu_stdbuf", path="src/uu/stdbuf" } +sum = { optional=true, version="0.0.8", package="uu_sum", path="src/uu/sum" } +sync = { optional=true, version="0.0.8", package="uu_sync", path="src/uu/sync" } +tac = { optional=true, version="0.0.8", package="uu_tac", path="src/uu/tac" } +tail = { optional=true, version="0.0.8", package="uu_tail", path="src/uu/tail" } +tee = { optional=true, version="0.0.8", package="uu_tee", path="src/uu/tee" } +timeout = { optional=true, version="0.0.8", package="uu_timeout", path="src/uu/timeout" } +touch = { optional=true, version="0.0.8", package="uu_touch", path="src/uu/touch" } +tr = { optional=true, version="0.0.8", package="uu_tr", path="src/uu/tr" } +true = { optional=true, version="0.0.8", package="uu_true", path="src/uu/true" } +truncate = { optional=true, version="0.0.8", package="uu_truncate", path="src/uu/truncate" } +tsort = { optional=true, version="0.0.8", package="uu_tsort", path="src/uu/tsort" } +tty = { optional=true, version="0.0.8", package="uu_tty", path="src/uu/tty" } +uname = { optional=true, version="0.0.8", package="uu_uname", path="src/uu/uname" } +unexpand = { optional=true, version="0.0.8", package="uu_unexpand", path="src/uu/unexpand" } +uniq = { optional=true, version="0.0.8", package="uu_uniq", path="src/uu/uniq" } +unlink = { optional=true, version="0.0.8", package="uu_unlink", path="src/uu/unlink" } +uptime = { optional=true, version="0.0.8", package="uu_uptime", path="src/uu/uptime" } +users = { optional=true, version="0.0.8", package="uu_users", path="src/uu/users" } +wc = { optional=true, version="0.0.8", package="uu_wc", path="src/uu/wc" } +who = { optional=true, version="0.0.8", package="uu_who", path="src/uu/who" } +whoami = { optional=true, version="0.0.8", package="uu_whoami", path="src/uu/whoami" } +yes = { optional=true, version="0.0.8", package="uu_yes", path="src/uu/yes" } # this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)" # factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" } @@ -373,7 +373,7 @@ sha1 = { version="0.6", features=["std"] } tempfile = "3.2.0" time = "0.1" unindent = "0.1" -uucore = { version=">=0.0.9", package="uucore", path="src/uucore", features=["entries", "process"] } +uucore = { version=">=0.0.10", package="uucore", path="src/uucore", features=["entries", "process"] } walkdir = "2.2" atty = "0.2" diff --git a/src/uu/arch/Cargo.toml b/src/uu/arch/Cargo.toml index aa51183d7..c7fc4f9f9 100644 --- a/src/uu/arch/Cargo.toml +++ b/src/uu/arch/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_arch" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "arch ~ (uutils) display machine architecture" @@ -17,8 +17,8 @@ path = "src/arch.rs" [dependencies] platform-info = "0.1" clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "arch" diff --git a/src/uu/base32/Cargo.toml b/src/uu/base32/Cargo.toml index bc896bdb2..d5fc40024 100644 --- a/src/uu/base32/Cargo.toml +++ b/src/uu/base32/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_base32" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "base32 ~ (uutils) decode/encode input (base32-encoding)" @@ -16,8 +16,8 @@ path = "src/base32.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features = ["encoding"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "base32" diff --git a/src/uu/base64/Cargo.toml b/src/uu/base64/Cargo.toml index 011964dc1..0121b78f6 100644 --- a/src/uu/base64/Cargo.toml +++ b/src/uu/base64/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_base64" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "base64 ~ (uutils) decode/encode input (base64-encoding)" @@ -16,8 +16,8 @@ path = "src/base64.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features = ["encoding"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } uu_base32 = { version=">=0.0.6", package="uu_base32", path="../base32"} [[bin]] diff --git a/src/uu/basename/Cargo.toml b/src/uu/basename/Cargo.toml index b5b0a462c..b75105790 100644 --- a/src/uu/basename/Cargo.toml +++ b/src/uu/basename/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_basename" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "basename ~ (uutils) display PATHNAME with leading directory components removed" @@ -16,8 +16,8 @@ path = "src/basename.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "basename" diff --git a/src/uu/basenc/Cargo.toml b/src/uu/basenc/Cargo.toml index e8350d83d..609a2d453 100644 --- a/src/uu/basenc/Cargo.toml +++ b/src/uu/basenc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_basenc" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "basenc ~ (uutils) decode/encode input" @@ -16,8 +16,8 @@ path = "src/basenc.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features = ["encoding"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } uu_base32 = { version=">=0.0.6", package="uu_base32", path="../base32"} [[bin]] diff --git a/src/uu/cat/Cargo.toml b/src/uu/cat/Cargo.toml index d4f137d7e..b6b0165ef 100644 --- a/src/uu/cat/Cargo.toml +++ b/src/uu/cat/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_cat" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "cat ~ (uutils) concatenate and display input" @@ -18,8 +18,8 @@ path = "src/cat.rs" clap = { version = "2.33", features = ["wrap_help"] } thiserror = "1.0" atty = "0.2" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs", "pipes"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "pipes"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [target.'cfg(unix)'.dependencies] unix_socket = "0.5.0" diff --git a/src/uu/chcon/Cargo.toml b/src/uu/chcon/Cargo.toml index 56fbef9ba..a359f142f 100644 --- a/src/uu/chcon/Cargo.toml +++ b/src/uu/chcon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_chcon" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "chcon ~ (uutils) change file security context" diff --git a/src/uu/chgrp/Cargo.toml b/src/uu/chgrp/Cargo.toml index 0d1b7e5aa..acc03472c 100644 --- a/src/uu/chgrp/Cargo.toml +++ b/src/uu/chgrp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_chgrp" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "chgrp ~ (uutils) change the group ownership of FILE" @@ -16,8 +16,8 @@ path = "src/chgrp.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "chgrp" diff --git a/src/uu/chmod/Cargo.toml b/src/uu/chmod/Cargo.toml index df150e0ed..64961e6dd 100644 --- a/src/uu/chmod/Cargo.toml +++ b/src/uu/chmod/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_chmod" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "chmod ~ (uutils) change mode of FILE" @@ -17,8 +17,8 @@ path = "src/chmod.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs", "mode"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "mode"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } walkdir = "2.2" [[bin]] diff --git a/src/uu/chown/Cargo.toml b/src/uu/chown/Cargo.toml index e6dc7d4fe..20381c660 100644 --- a/src/uu/chown/Cargo.toml +++ b/src/uu/chown/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_chown" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "chown ~ (uutils) change the ownership of FILE" @@ -16,8 +16,8 @@ path = "src/chown.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "chown" diff --git a/src/uu/chroot/Cargo.toml b/src/uu/chroot/Cargo.toml index a808a717b..362e43b59 100644 --- a/src/uu/chroot/Cargo.toml +++ b/src/uu/chroot/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_chroot" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "chroot ~ (uutils) run COMMAND under a new root directory" @@ -16,8 +16,8 @@ path = "src/chroot.rs" [dependencies] clap= "2.33" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "chroot" diff --git a/src/uu/cksum/Cargo.toml b/src/uu/cksum/Cargo.toml index 287a2285f..b7f81d74b 100644 --- a/src/uu/cksum/Cargo.toml +++ b/src/uu/cksum/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_cksum" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "cksum ~ (uutils) display CRC and size of input" @@ -17,8 +17,8 @@ path = "src/cksum.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "cksum" diff --git a/src/uu/comm/Cargo.toml b/src/uu/comm/Cargo.toml index e44c3511c..abcbff57b 100644 --- a/src/uu/comm/Cargo.toml +++ b/src/uu/comm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_comm" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "comm ~ (uutils) compare sorted inputs" @@ -17,8 +17,8 @@ path = "src/comm.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "comm" diff --git a/src/uu/cp/Cargo.toml b/src/uu/cp/Cargo.toml index 62aef932b..02febab74 100644 --- a/src/uu/cp/Cargo.toml +++ b/src/uu/cp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_cp" -version = "0.0.7" +version = "0.0.8" authors = [ "Jordy Dickinson ", "Joshua S. Miller ", @@ -24,8 +24,8 @@ filetime = "0.2" libc = "0.2.85" quick-error = "1.2.3" selinux = { version="0.2.3", optional=true } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs", "perms", "mode"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "perms", "mode"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } walkdir = "2.2" [target.'cfg(target_os = "linux")'.dependencies] diff --git a/src/uu/csplit/Cargo.toml b/src/uu/csplit/Cargo.toml index 40d4eebfa..b76e942ee 100644 --- a/src/uu/csplit/Cargo.toml +++ b/src/uu/csplit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_csplit" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "csplit ~ (uutils) Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output" @@ -18,8 +18,8 @@ path = "src/csplit.rs" clap = { version = "2.33", features = ["wrap_help"] } thiserror = "1.0" regex = "1.0.0" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "csplit" diff --git a/src/uu/cut/Cargo.toml b/src/uu/cut/Cargo.toml index c49450251..991e3c449 100644 --- a/src/uu/cut/Cargo.toml +++ b/src/uu/cut/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_cut" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "cut ~ (uutils) display byte/field columns of input lines" @@ -16,8 +16,8 @@ path = "src/cut.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } memchr = "2" bstr = "0.2" atty = "0.2" diff --git a/src/uu/date/Cargo.toml b/src/uu/date/Cargo.toml index d2af8c4f1..b1077fbe1 100644 --- a/src/uu/date/Cargo.toml +++ b/src/uu/date/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_date" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "date ~ (uutils) display or set the current time" @@ -17,8 +17,8 @@ path = "src/date.rs" [dependencies] chrono = "0.4.4" clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index 007ebb8ff..e26c141cb 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_dd" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "dd ~ (uutils) copy and convert files" diff --git a/src/uu/df/Cargo.toml b/src/uu/df/Cargo.toml index 6979a79b4..9486640d0 100644 --- a/src/uu/df/Cargo.toml +++ b/src/uu/df/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_df" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "df ~ (uutils) display file system information" @@ -17,8 +17,8 @@ path = "src/df.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } number_prefix = "0.4" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["libc", "fsext"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["libc", "fsext"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "df" diff --git a/src/uu/dircolors/Cargo.toml b/src/uu/dircolors/Cargo.toml index ad95564f3..0bf53d91a 100644 --- a/src/uu/dircolors/Cargo.toml +++ b/src/uu/dircolors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_dircolors" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "dircolors ~ (uutils) display commands to set LS_COLORS" @@ -17,8 +17,8 @@ path = "src/dircolors.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } glob = "0.3.0" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "dircolors" diff --git a/src/uu/dirname/Cargo.toml b/src/uu/dirname/Cargo.toml index bd9e449d7..7946459f3 100644 --- a/src/uu/dirname/Cargo.toml +++ b/src/uu/dirname/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_dirname" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "dirname ~ (uutils) display parent directory of PATHNAME" @@ -17,8 +17,8 @@ path = "src/dirname.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "dirname" diff --git a/src/uu/du/Cargo.toml b/src/uu/du/Cargo.toml index dfc50f7a6..c9da0462c 100644 --- a/src/uu/du/Cargo.toml +++ b/src/uu/du/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_du" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "du ~ (uutils) display disk usage" @@ -17,8 +17,8 @@ path = "src/du.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } chrono = "0.4" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version="0.3", features=[] } diff --git a/src/uu/echo/Cargo.toml b/src/uu/echo/Cargo.toml index 60a547e21..05dd1eba1 100644 --- a/src/uu/echo/Cargo.toml +++ b/src/uu/echo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_echo" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "echo ~ (uutils) display TEXT" @@ -16,8 +16,8 @@ path = "src/echo.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "echo" diff --git a/src/uu/env/Cargo.toml b/src/uu/env/Cargo.toml index c368cfbac..374a4eda9 100644 --- a/src/uu/env/Cargo.toml +++ b/src/uu/env/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_env" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "env ~ (uutils) set each NAME to VALUE in the environment and run COMMAND" @@ -18,8 +18,8 @@ path = "src/env.rs" clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" rust-ini = "0.17.0" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "env" diff --git a/src/uu/expand/Cargo.toml b/src/uu/expand/Cargo.toml index e9b2cc747..efd338dab 100644 --- a/src/uu/expand/Cargo.toml +++ b/src/uu/expand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_expand" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "expand ~ (uutils) convert input tabs to spaces" @@ -17,8 +17,8 @@ path = "src/expand.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } unicode-width = "0.1.5" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "expand" diff --git a/src/uu/expr/Cargo.toml b/src/uu/expr/Cargo.toml index 035f00721..90a53f2ce 100644 --- a/src/uu/expr/Cargo.toml +++ b/src/uu/expr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_expr" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "expr ~ (uutils) display the value of EXPRESSION" @@ -20,8 +20,8 @@ libc = "0.2.42" num-bigint = "0.4.0" num-traits = "0.2.14" onig = "~4.3.2" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "expr" diff --git a/src/uu/factor/Cargo.toml b/src/uu/factor/Cargo.toml index 9d62e5f2b..2d2fa236b 100644 --- a/src/uu/factor/Cargo.toml +++ b/src/uu/factor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_factor" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "factor ~ (uutils) display the prime factors of each NUMBER" @@ -20,7 +20,7 @@ num-traits = "0.2.13" # Needs at least version 0.2.13 for "OverflowingAdd" rand = { version = "0.7", features = ["small_rng"] } smallvec = { version = "0.6.14, < 1.0" } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" } -uucore_procs = { version=">=0.0.6", package = "uucore_procs", path = "../../uucore_procs" } +uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" } clap = { version = "2.33", features = ["wrap_help"] } [dev-dependencies] diff --git a/src/uu/false/Cargo.toml b/src/uu/false/Cargo.toml index d6b4b7d6d..2a725e2b0 100644 --- a/src/uu/false/Cargo.toml +++ b/src/uu/false/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_false" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "false ~ (uutils) do nothing and fail" @@ -16,8 +16,8 @@ path = "src/false.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "false" diff --git a/src/uu/fmt/Cargo.toml b/src/uu/fmt/Cargo.toml index 75b81c354..a4700cb5e 100644 --- a/src/uu/fmt/Cargo.toml +++ b/src/uu/fmt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_fmt" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "fmt ~ (uutils) reformat each paragraph of input" @@ -18,8 +18,8 @@ path = "src/fmt.rs" clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" unicode-width = "0.1.5" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "fmt" diff --git a/src/uu/fold/Cargo.toml b/src/uu/fold/Cargo.toml index 7ec886264..ce2aeead4 100644 --- a/src/uu/fold/Cargo.toml +++ b/src/uu/fold/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_fold" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "fold ~ (uutils) wrap each line of input" @@ -16,8 +16,8 @@ path = "src/fold.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "fold" diff --git a/src/uu/groups/Cargo.toml b/src/uu/groups/Cargo.toml index c32153f17..6ac210692 100644 --- a/src/uu/groups/Cargo.toml +++ b/src/uu/groups/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_groups" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "groups ~ (uutils) display group memberships for USERNAME" @@ -15,8 +15,8 @@ edition = "2018" path = "src/groups.rs" [dependencies] -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "process"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "process"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } clap = { version = "2.33", features = ["wrap_help"] } [[bin]] diff --git a/src/uu/hashsum/Cargo.toml b/src/uu/hashsum/Cargo.toml index 7cb88dede..67b0f9e02 100644 --- a/src/uu/hashsum/Cargo.toml +++ b/src/uu/hashsum/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_hashsum" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "hashsum ~ (uutils) display or check input digests" @@ -27,8 +27,8 @@ sha1 = "0.6.0" sha2 = "0.6.0" sha3 = "0.6.0" blake2b_simd = "0.5.11" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "hashsum" diff --git a/src/uu/head/Cargo.toml b/src/uu/head/Cargo.toml index 3a615812b..aa32a899a 100644 --- a/src/uu/head/Cargo.toml +++ b/src/uu/head/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_head" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "head ~ (uutils) display the first lines of input" @@ -17,8 +17,8 @@ path = "src/head.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } memchr = "2" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["ringbuffer"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["ringbuffer"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "head" diff --git a/src/uu/hostid/Cargo.toml b/src/uu/hostid/Cargo.toml index 45ced472f..c56649742 100644 --- a/src/uu/hostid/Cargo.toml +++ b/src/uu/hostid/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_hostid" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "hostid ~ (uutils) display the numeric identifier of the current host" @@ -17,8 +17,8 @@ path = "src/hostid.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "hostid" diff --git a/src/uu/hostname/Cargo.toml b/src/uu/hostname/Cargo.toml index afd402f24..daaeb48f7 100644 --- a/src/uu/hostname/Cargo.toml +++ b/src/uu/hostname/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_hostname" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "hostname ~ (uutils) display or set the host name of the current host" @@ -18,8 +18,8 @@ path = "src/hostname.rs" clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" hostname = { version = "0.3", features = ["set"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["wide"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["wide"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } winapi = { version="0.3", features=["sysinfoapi", "winsock2"] } [[bin]] diff --git a/src/uu/id/Cargo.toml b/src/uu/id/Cargo.toml index b58c7fd78..0039cfc8e 100644 --- a/src/uu/id/Cargo.toml +++ b/src/uu/id/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_id" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "id ~ (uutils) display user and group information for USER" @@ -16,8 +16,8 @@ path = "src/id.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "process"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "process"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } selinux = { version="0.2.1", optional = true } [[bin]] diff --git a/src/uu/install/Cargo.toml b/src/uu/install/Cargo.toml index d930b2fbd..1b5af3dba 100644 --- a/src/uu/install/Cargo.toml +++ b/src/uu/install/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_install" -version = "0.0.7" +version = "0.0.8" authors = [ "Ben Eills ", "uutils developers", @@ -22,8 +22,8 @@ clap = { version = "2.33", features = ["wrap_help"] } filetime = "0.2" file_diff = "1.0.0" libc = ">= 0.2" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["mode", "perms", "entries"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["mode", "perms", "entries"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [dev-dependencies] time = "0.1.40" diff --git a/src/uu/join/Cargo.toml b/src/uu/join/Cargo.toml index 7e5ced498..1c140f544 100644 --- a/src/uu/join/Cargo.toml +++ b/src/uu/join/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_join" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "join ~ (uutils) merge lines from inputs with matching join fields" @@ -16,8 +16,8 @@ path = "src/join.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "join" diff --git a/src/uu/kill/Cargo.toml b/src/uu/kill/Cargo.toml index 82a8a4fd0..452b0f407 100644 --- a/src/uu/kill/Cargo.toml +++ b/src/uu/kill/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_kill" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "kill ~ (uutils) send a signal to a process" @@ -17,8 +17,8 @@ path = "src/kill.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["signals"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["signals"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "kill" diff --git a/src/uu/link/Cargo.toml b/src/uu/link/Cargo.toml index d37ac6761..275d3b5cc 100644 --- a/src/uu/link/Cargo.toml +++ b/src/uu/link/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_link" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "link ~ (uutils) create a hard (file system) link to FILE" @@ -16,8 +16,8 @@ path = "src/link.rs" [dependencies] libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } clap = { version = "2.33", features = ["wrap_help"] } [[bin]] diff --git a/src/uu/ln/Cargo.toml b/src/uu/ln/Cargo.toml index 476e13771..ba2c8de96 100644 --- a/src/uu/ln/Cargo.toml +++ b/src/uu/ln/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_ln" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "ln ~ (uutils) create a (file system) link to TARGET" @@ -17,8 +17,8 @@ path = "src/ln.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "ln" diff --git a/src/uu/logname/Cargo.toml b/src/uu/logname/Cargo.toml index 3c5f9afab..ed19ff887 100644 --- a/src/uu/logname/Cargo.toml +++ b/src/uu/logname/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_logname" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "logname ~ (uutils) display the login name of the current user" @@ -17,8 +17,8 @@ path = "src/logname.rs" [dependencies] libc = "0.2.42" clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "logname" diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index 629a7a2d8..b918f6501 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_ls" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "ls ~ (uutils) display directory contents" @@ -24,7 +24,7 @@ termsize = "0.1.6" globset = "0.4.6" lscolors = { version = "0.7.1", features = ["ansi_term"] } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] } -uucore_procs = { version=">=0.0.6", package = "uucore_procs", path = "../../uucore_procs" } +uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" } once_cell = "1.7.2" atty = "0.2" selinux = { version="0.2.1", optional = true } diff --git a/src/uu/mkdir/Cargo.toml b/src/uu/mkdir/Cargo.toml index 011d70af0..c0e6586ab 100644 --- a/src/uu/mkdir/Cargo.toml +++ b/src/uu/mkdir/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_mkdir" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "mkdir ~ (uutils) create DIRECTORY" @@ -17,8 +17,8 @@ path = "src/mkdir.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs", "mode"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "mode"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "mkdir" diff --git a/src/uu/mkfifo/Cargo.toml b/src/uu/mkfifo/Cargo.toml index 01d6bad64..acf38f6bc 100644 --- a/src/uu/mkfifo/Cargo.toml +++ b/src/uu/mkfifo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_mkfifo" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "mkfifo ~ (uutils) create FIFOs (named pipes)" @@ -17,8 +17,8 @@ path = "src/mkfifo.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "mkfifo" diff --git a/src/uu/mknod/Cargo.toml b/src/uu/mknod/Cargo.toml index e6073d50c..046e4b8d0 100644 --- a/src/uu/mknod/Cargo.toml +++ b/src/uu/mknod/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_mknod" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "mknod ~ (uutils) create special file NAME of TYPE" @@ -18,8 +18,8 @@ path = "src/mknod.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "^0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["mode"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["mode"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "mknod" diff --git a/src/uu/mktemp/Cargo.toml b/src/uu/mktemp/Cargo.toml index 4c9a8d29c..de896dba7 100644 --- a/src/uu/mktemp/Cargo.toml +++ b/src/uu/mktemp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_mktemp" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "mktemp ~ (uutils) create and display a temporary file or directory from TEMPLATE" @@ -18,8 +18,8 @@ path = "src/mktemp.rs" clap = { version = "2.33", features = ["wrap_help"] } rand = "0.5" tempfile = "3.1" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "mktemp" diff --git a/src/uu/more/Cargo.toml b/src/uu/more/Cargo.toml index cd292eea9..0111376f9 100644 --- a/src/uu/more/Cargo.toml +++ b/src/uu/more/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_more" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "more ~ (uutils) input perusal filter" @@ -17,7 +17,7 @@ path = "src/more.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } uucore = { version = ">=0.0.7", package = "uucore", path = "../../uucore" } -uucore_procs = { version=">=0.0.6", package = "uucore_procs", path = "../../uucore_procs" } +uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" } crossterm = ">=0.19" atty = "0.2" unicode-width = "0.1.7" diff --git a/src/uu/mv/Cargo.toml b/src/uu/mv/Cargo.toml index 82b1da6e1..6dd129af9 100644 --- a/src/uu/mv/Cargo.toml +++ b/src/uu/mv/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_mv" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "mv ~ (uutils) move (rename) SOURCE to DESTINATION" @@ -17,8 +17,8 @@ path = "src/mv.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } fs_extra = "1.1.0" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "mv" diff --git a/src/uu/nice/Cargo.toml b/src/uu/nice/Cargo.toml index 5f0c2c07a..ac719a768 100644 --- a/src/uu/nice/Cargo.toml +++ b/src/uu/nice/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_nice" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "nice ~ (uutils) run PROGRAM with modified scheduling priority" @@ -18,8 +18,8 @@ path = "src/nice.rs" clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" nix = "0.20" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "nice" diff --git a/src/uu/nl/Cargo.toml b/src/uu/nl/Cargo.toml index ca0d7827d..781f6502d 100644 --- a/src/uu/nl/Cargo.toml +++ b/src/uu/nl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_nl" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "nl ~ (uutils) display input with added line numbers" @@ -21,8 +21,8 @@ libc = "0.2.42" memchr = "2.2.0" regex = "1.0.1" regex-syntax = "0.6.7" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "nl" diff --git a/src/uu/nohup/Cargo.toml b/src/uu/nohup/Cargo.toml index d7de60b0b..c2f3c329e 100644 --- a/src/uu/nohup/Cargo.toml +++ b/src/uu/nohup/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_nohup" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "nohup ~ (uutils) run COMMAND, ignoring hangup signals" @@ -18,8 +18,8 @@ path = "src/nohup.rs" clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" atty = "0.2" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "nohup" diff --git a/src/uu/nproc/Cargo.toml b/src/uu/nproc/Cargo.toml index e02bec7e1..3c44062bd 100644 --- a/src/uu/nproc/Cargo.toml +++ b/src/uu/nproc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_nproc" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "nproc ~ (uutils) display the number of processing units available" @@ -18,8 +18,8 @@ path = "src/nproc.rs" libc = "0.2.42" num_cpus = "1.10" clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "nproc" diff --git a/src/uu/numfmt/Cargo.toml b/src/uu/numfmt/Cargo.toml index 6239da7f9..60bfb3b14 100644 --- a/src/uu/numfmt/Cargo.toml +++ b/src/uu/numfmt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_numfmt" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "numfmt ~ (uutils) reformat NUMBER" @@ -16,8 +16,8 @@ path = "src/numfmt.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "numfmt" diff --git a/src/uu/od/Cargo.toml b/src/uu/od/Cargo.toml index ee785e773..a6b5bc433 100644 --- a/src/uu/od/Cargo.toml +++ b/src/uu/od/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_od" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "od ~ (uutils) display formatted representation of input" @@ -19,8 +19,8 @@ byteorder = "1.3.2" clap = { version = "2.33", features = ["wrap_help"] } half = "1.6" libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "od" diff --git a/src/uu/paste/Cargo.toml b/src/uu/paste/Cargo.toml index c4873b1d0..aeba6f68e 100644 --- a/src/uu/paste/Cargo.toml +++ b/src/uu/paste/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_paste" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "paste ~ (uutils) merge lines from inputs" @@ -16,8 +16,8 @@ path = "src/paste.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "paste" diff --git a/src/uu/pathchk/Cargo.toml b/src/uu/pathchk/Cargo.toml index d7e76728a..79e0c251b 100644 --- a/src/uu/pathchk/Cargo.toml +++ b/src/uu/pathchk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_pathchk" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "pathchk ~ (uutils) diagnose invalid or non-portable PATHNAME" @@ -17,8 +17,8 @@ path = "src/pathchk.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "pathchk" diff --git a/src/uu/pinky/Cargo.toml b/src/uu/pinky/Cargo.toml index a84aa7473..297d40642 100644 --- a/src/uu/pinky/Cargo.toml +++ b/src/uu/pinky/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_pinky" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "pinky ~ (uutils) display user information" @@ -15,8 +15,8 @@ edition = "2018" path = "src/pinky.rs" [dependencies] -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["utmpx", "entries"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["utmpx", "entries"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } clap = { version = "2.33", features = ["wrap_help"] } [[bin]] diff --git a/src/uu/pr/Cargo.toml b/src/uu/pr/Cargo.toml index 09993c3b9..499ee7726 100644 --- a/src/uu/pr/Cargo.toml +++ b/src/uu/pr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_pr" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "pr ~ (uutils) convert text files for printing" @@ -17,7 +17,7 @@ path = "src/pr.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["entries"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } getopts = "0.2.21" chrono = "0.4.19" quick-error = "2.0.1" diff --git a/src/uu/printenv/Cargo.toml b/src/uu/printenv/Cargo.toml index 466f69af0..eaf482b21 100644 --- a/src/uu/printenv/Cargo.toml +++ b/src/uu/printenv/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_printenv" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "printenv ~ (uutils) display value of environment VAR" @@ -16,8 +16,8 @@ path = "src/printenv.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "printenv" diff --git a/src/uu/printf/Cargo.toml b/src/uu/printf/Cargo.toml index f4034083a..50c454db5 100644 --- a/src/uu/printf/Cargo.toml +++ b/src/uu/printf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_printf" -version = "0.0.7" +version = "0.0.8" authors = [ "Nathan Ross", "uutils developers", @@ -20,8 +20,8 @@ path = "src/printf.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } itertools = "0.8.0" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "printf" diff --git a/src/uu/ptx/Cargo.toml b/src/uu/ptx/Cargo.toml index fea4e5c1f..06eb15ee8 100644 --- a/src/uu/ptx/Cargo.toml +++ b/src/uu/ptx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_ptx" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "ptx ~ (uutils) display a permuted index of input" @@ -21,8 +21,8 @@ libc = "0.2.42" memchr = "2.2.0" regex = "1.0.1" regex-syntax = "0.6.7" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "ptx" diff --git a/src/uu/pwd/Cargo.toml b/src/uu/pwd/Cargo.toml index 37cbc7fe6..a168fb5aa 100644 --- a/src/uu/pwd/Cargo.toml +++ b/src/uu/pwd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_pwd" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "pwd ~ (uutils) display current working directory" @@ -16,8 +16,8 @@ path = "src/pwd.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "pwd" diff --git a/src/uu/readlink/Cargo.toml b/src/uu/readlink/Cargo.toml index 8552f611d..d126a3835 100644 --- a/src/uu/readlink/Cargo.toml +++ b/src/uu/readlink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_readlink" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "readlink ~ (uutils) display resolved path of PATHNAME" @@ -17,8 +17,8 @@ path = "src/readlink.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "readlink" diff --git a/src/uu/realpath/Cargo.toml b/src/uu/realpath/Cargo.toml index 3916c4ce6..4be39f978 100644 --- a/src/uu/realpath/Cargo.toml +++ b/src/uu/realpath/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_realpath" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "realpath ~ (uutils) display resolved absolute path of PATHNAME" @@ -16,8 +16,8 @@ path = "src/realpath.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "realpath" diff --git a/src/uu/relpath/Cargo.toml b/src/uu/relpath/Cargo.toml index bcb048af9..f1ddf87dc 100644 --- a/src/uu/relpath/Cargo.toml +++ b/src/uu/relpath/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_relpath" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "relpath ~ (uutils) display relative path of PATHNAME_TO from PATHNAME_FROM" @@ -16,8 +16,8 @@ path = "src/relpath.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "relpath" diff --git a/src/uu/rm/Cargo.toml b/src/uu/rm/Cargo.toml index 6099d137a..757fa8b22 100644 --- a/src/uu/rm/Cargo.toml +++ b/src/uu/rm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_rm" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "rm ~ (uutils) remove PATHNAME" @@ -18,8 +18,8 @@ path = "src/rm.rs" clap = { version = "2.33", features = ["wrap_help"] } walkdir = "2.2" remove_dir_all = "0.5.1" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [target.'cfg(windows)'.dependencies] winapi = { version="0.3", features=[] } diff --git a/src/uu/rmdir/Cargo.toml b/src/uu/rmdir/Cargo.toml index cdb08f908..bc05773a8 100644 --- a/src/uu/rmdir/Cargo.toml +++ b/src/uu/rmdir/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_rmdir" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "rmdir ~ (uutils) remove empty DIRECTORY" @@ -16,8 +16,8 @@ path = "src/rmdir.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } libc = "0.2.42" [[bin]] diff --git a/src/uu/runcon/Cargo.toml b/src/uu/runcon/Cargo.toml index 4e4c0bed6..ff06e72a1 100644 --- a/src/uu/runcon/Cargo.toml +++ b/src/uu/runcon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_runcon" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "runcon ~ (uutils) run command with specified security context" diff --git a/src/uu/seq/Cargo.toml b/src/uu/seq/Cargo.toml index 4618115cb..f5a23310f 100644 --- a/src/uu/seq/Cargo.toml +++ b/src/uu/seq/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_seq" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "seq ~ (uutils) display a sequence of numbers" @@ -18,8 +18,8 @@ path = "src/seq.rs" clap = { version = "2.33", features = ["wrap_help"] } num-bigint = "0.4.0" num-traits = "0.2.14" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "seq" diff --git a/src/uu/shred/Cargo.toml b/src/uu/shred/Cargo.toml index d87732d84..00356d701 100644 --- a/src/uu/shred/Cargo.toml +++ b/src/uu/shred/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_shred" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "shred ~ (uutils) hide former FILE contents with repeated overwrites" @@ -18,8 +18,8 @@ path = "src/shred.rs" clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" rand = "0.7" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "shred" diff --git a/src/uu/shuf/Cargo.toml b/src/uu/shuf/Cargo.toml index bb3ccc710..35da41145 100644 --- a/src/uu/shuf/Cargo.toml +++ b/src/uu/shuf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_shuf" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "shuf ~ (uutils) display random permutations of input lines" @@ -17,8 +17,8 @@ path = "src/shuf.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } rand = "0.5" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "shuf" diff --git a/src/uu/sleep/Cargo.toml b/src/uu/sleep/Cargo.toml index 8414f444c..af6f22b9f 100644 --- a/src/uu/sleep/Cargo.toml +++ b/src/uu/sleep/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_sleep" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "sleep ~ (uutils) pause for DURATION" @@ -16,8 +16,8 @@ path = "src/sleep.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "sleep" diff --git a/src/uu/sort/Cargo.toml b/src/uu/sort/Cargo.toml index 7faf9906e..b3d4fe0ea 100644 --- a/src/uu/sort/Cargo.toml +++ b/src/uu/sort/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_sort" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "sort ~ (uutils) sort input lines" @@ -27,8 +27,8 @@ rand = "0.7" rayon = "1.5" tempfile = "3" unicode-width = "0.1.8" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "sort" diff --git a/src/uu/split/Cargo.toml b/src/uu/split/Cargo.toml index d2168bf49..44d0350b2 100644 --- a/src/uu/split/Cargo.toml +++ b/src/uu/split/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_split" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "split ~ (uutils) split input into output files" @@ -16,8 +16,8 @@ path = "src/split.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "split" diff --git a/src/uu/stat/Cargo.toml b/src/uu/stat/Cargo.toml index dec209ea0..a1f168b14 100644 --- a/src/uu/stat/Cargo.toml +++ b/src/uu/stat/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_stat" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "stat ~ (uutils) display FILE status" @@ -16,8 +16,8 @@ path = "src/stat.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "libc", "fs", "fsext"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "libc", "fs", "fsext"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "stat" diff --git a/src/uu/stdbuf/Cargo.toml b/src/uu/stdbuf/Cargo.toml index e9e162803..45863cd0c 100644 --- a/src/uu/stdbuf/Cargo.toml +++ b/src/uu/stdbuf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_stdbuf" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "stdbuf ~ (uutils) run COMMAND with modified standard stream buffering" @@ -17,11 +17,11 @@ path = "src/stdbuf.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } tempfile = "3.1" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [build-dependencies] -libstdbuf = { version="0.0.7", package="uu_stdbuf_libstdbuf", path="src/libstdbuf" } +libstdbuf = { version="0.0.8", package="uu_stdbuf_libstdbuf", path="src/libstdbuf" } [[bin]] name = "stdbuf" diff --git a/src/uu/stdbuf/src/libstdbuf/Cargo.toml b/src/uu/stdbuf/src/libstdbuf/Cargo.toml index 9f86fcd17..069a2ae11 100644 --- a/src/uu/stdbuf/src/libstdbuf/Cargo.toml +++ b/src/uu/stdbuf/src/libstdbuf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_stdbuf_libstdbuf" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "stdbuf/libstdbuf ~ (uutils); dynamic library required for stdbuf" @@ -19,8 +19,8 @@ crate-type = ["cdylib", "rlib"] # XXX: note: the rlib is just to prevent Cargo f [dependencies] cpp = "0.5" libc = "0.2" -uucore = { version=">=0.0.9", package="uucore", path="../../../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../../../uucore_procs" } [build-dependencies] cpp_build = "0.4" diff --git a/src/uu/sum/Cargo.toml b/src/uu/sum/Cargo.toml index 41f2d0a38..273f9dff9 100644 --- a/src/uu/sum/Cargo.toml +++ b/src/uu/sum/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_sum" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "sum ~ (uutils) display checksum and block counts for input" @@ -16,8 +16,8 @@ path = "src/sum.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "sum" diff --git a/src/uu/sync/Cargo.toml b/src/uu/sync/Cargo.toml index a745ac6f8..4bf2c60b3 100644 --- a/src/uu/sync/Cargo.toml +++ b/src/uu/sync/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_sync" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "sync ~ (uutils) synchronize cache writes to storage" @@ -17,8 +17,8 @@ path = "src/sync.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["wide"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["wide"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } winapi = { version = "0.3", features = ["errhandlingapi", "fileapi", "handleapi", "std", "winbase", "winerror"] } [[bin]] diff --git a/src/uu/tac/Cargo.toml b/src/uu/tac/Cargo.toml index 00803c8d2..6840d6bc3 100644 --- a/src/uu/tac/Cargo.toml +++ b/src/uu/tac/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "uu_tac" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "tac ~ (uutils) concatenate and display input lines in reverse order" @@ -21,8 +21,8 @@ memchr = "2" memmap2 = "0.5" regex = "1" clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "tac" diff --git a/src/uu/tail/Cargo.toml b/src/uu/tail/Cargo.toml index 6fd05b1a9..f288846ea 100644 --- a/src/uu/tail/Cargo.toml +++ b/src/uu/tail/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_tail" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "tail ~ (uutils) display the last lines of input" @@ -17,8 +17,8 @@ path = "src/tail.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["ringbuffer"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["ringbuffer"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [target.'cfg(windows)'.dependencies] winapi = { version="0.3", features=["fileapi", "handleapi", "processthreadsapi", "synchapi", "winbase"] } diff --git a/src/uu/tee/Cargo.toml b/src/uu/tee/Cargo.toml index 900ef3564..00a250565 100644 --- a/src/uu/tee/Cargo.toml +++ b/src/uu/tee/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_tee" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "tee ~ (uutils) display input and copy to FILE" @@ -18,8 +18,8 @@ path = "src/tee.rs" clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" retain_mut = "0.1.2" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["libc"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["libc"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "tee" diff --git a/src/uu/test/Cargo.toml b/src/uu/test/Cargo.toml index 3fe531d1d..633e0f323 100644 --- a/src/uu/test/Cargo.toml +++ b/src/uu/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_test" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "test ~ (uutils) evaluate comparison and file type expressions" @@ -17,8 +17,8 @@ path = "src/test.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [target.'cfg(target_os = "redox")'.dependencies] redox_syscall = "0.2" diff --git a/src/uu/timeout/Cargo.toml b/src/uu/timeout/Cargo.toml index 3e17669e0..13afe8435 100644 --- a/src/uu/timeout/Cargo.toml +++ b/src/uu/timeout/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_timeout" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "timeout ~ (uutils) run COMMAND with a DURATION time limit" @@ -18,8 +18,8 @@ path = "src/timeout.rs" clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" nix = "0.20.0" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["process", "signals"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["process", "signals"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] diff --git a/src/uu/touch/Cargo.toml b/src/uu/touch/Cargo.toml index 5bb5822ec..b21e2dfa3 100644 --- a/src/uu/touch/Cargo.toml +++ b/src/uu/touch/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_touch" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "touch ~ (uutils) change FILE timestamps" @@ -18,8 +18,8 @@ path = "src/touch.rs" filetime = "0.2.1" clap = { version = "2.33", features = ["wrap_help"] } time = "0.1.40" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["libc"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["libc"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "touch" diff --git a/src/uu/tr/Cargo.toml b/src/uu/tr/Cargo.toml index 450c562e0..8792938a9 100644 --- a/src/uu/tr/Cargo.toml +++ b/src/uu/tr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_tr" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "tr ~ (uutils) translate characters within input and display" @@ -18,8 +18,8 @@ path = "src/tr.rs" bit-set = "0.5.0" fnv = "1.0.5" clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "tr" diff --git a/src/uu/true/Cargo.toml b/src/uu/true/Cargo.toml index 36ea0f991..369bbb51f 100644 --- a/src/uu/true/Cargo.toml +++ b/src/uu/true/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_true" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "true ~ (uutils) do nothing and succeed" @@ -16,8 +16,8 @@ path = "src/true.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "true" diff --git a/src/uu/truncate/Cargo.toml b/src/uu/truncate/Cargo.toml index e779e32ba..811aa6ef6 100644 --- a/src/uu/truncate/Cargo.toml +++ b/src/uu/truncate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_truncate" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "truncate ~ (uutils) truncate (or extend) FILE to SIZE" @@ -16,8 +16,8 @@ path = "src/truncate.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "truncate" diff --git a/src/uu/tsort/Cargo.toml b/src/uu/tsort/Cargo.toml index 055615003..bcc2dd757 100644 --- a/src/uu/tsort/Cargo.toml +++ b/src/uu/tsort/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_tsort" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "tsort ~ (uutils) topologically sort input (partially ordered) pairs" @@ -16,8 +16,8 @@ path = "src/tsort.rs" [dependencies] clap= "2.33" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "tsort" diff --git a/src/uu/tty/Cargo.toml b/src/uu/tty/Cargo.toml index 6a4cb73a6..5b2e18cd2 100644 --- a/src/uu/tty/Cargo.toml +++ b/src/uu/tty/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_tty" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "tty ~ (uutils) display the name of the terminal connected to standard input" @@ -18,8 +18,8 @@ path = "src/tty.rs" clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" atty = "0.2" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "tty" diff --git a/src/uu/uname/Cargo.toml b/src/uu/uname/Cargo.toml index a12b095aa..90a103075 100644 --- a/src/uu/uname/Cargo.toml +++ b/src/uu/uname/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_uname" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "uname ~ (uutils) display system information" @@ -17,8 +17,8 @@ path = "src/uname.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } platform-info = "0.1" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "uname" diff --git a/src/uu/unexpand/Cargo.toml b/src/uu/unexpand/Cargo.toml index 8b1169151..026a12872 100644 --- a/src/uu/unexpand/Cargo.toml +++ b/src/uu/unexpand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_unexpand" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "unexpand ~ (uutils) convert input spaces to tabs" @@ -17,8 +17,8 @@ path = "src/unexpand.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } unicode-width = "0.1.5" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "unexpand" diff --git a/src/uu/uniq/Cargo.toml b/src/uu/uniq/Cargo.toml index 06ba22688..a6fcebc92 100644 --- a/src/uu/uniq/Cargo.toml +++ b/src/uu/uniq/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_uniq" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "uniq ~ (uutils) filter identical adjacent lines from input" @@ -18,8 +18,8 @@ path = "src/uniq.rs" clap = { version = "2.33", features = ["wrap_help"] } strum = "0.21" strum_macros = "0.21" -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "uniq" diff --git a/src/uu/unlink/Cargo.toml b/src/uu/unlink/Cargo.toml index 558d18422..c4b7d49cb 100644 --- a/src/uu/unlink/Cargo.toml +++ b/src/uu/unlink/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_unlink" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "unlink ~ (uutils) remove a (file system) link to FILE" @@ -16,8 +16,8 @@ path = "src/unlink.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "unlink" diff --git a/src/uu/uptime/Cargo.toml b/src/uu/uptime/Cargo.toml index a5b7a3177..785955afc 100644 --- a/src/uu/uptime/Cargo.toml +++ b/src/uu/uptime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_uptime" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "uptime ~ (uutils) display dynamic system information" @@ -17,8 +17,8 @@ path = "src/uptime.rs" [dependencies] chrono = "0.4" clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["libc", "utmpx"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["libc", "utmpx"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "uptime" diff --git a/src/uu/users/Cargo.toml b/src/uu/users/Cargo.toml index ec4f0785e..05872e8bf 100644 --- a/src/uu/users/Cargo.toml +++ b/src/uu/users/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_users" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "users ~ (uutils) display names of currently logged-in users" @@ -16,8 +16,8 @@ path = "src/users.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["utmpx"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["utmpx"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [[bin]] name = "users" diff --git a/src/uu/wc/Cargo.toml b/src/uu/wc/Cargo.toml index 179b17c36..d76e0b176 100644 --- a/src/uu/wc/Cargo.toml +++ b/src/uu/wc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_wc" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "wc ~ (uutils) display newline, word, and byte counts for input" @@ -16,8 +16,8 @@ path = "src/wc.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["pipes"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["pipes"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } bytecount = "0.6.2" utf-8 = "0.7.6" unicode-width = "0.1.8" diff --git a/src/uu/who/Cargo.toml b/src/uu/who/Cargo.toml index 1b3abe91e..c660be59d 100644 --- a/src/uu/who/Cargo.toml +++ b/src/uu/who/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_who" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "who ~ (uutils) display information about currently logged-in users" @@ -15,8 +15,8 @@ edition = "2018" path = "src/who.rs" [dependencies] -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["utmpx"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["utmpx"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } clap = { version = "2.33", features = ["wrap_help"] } [[bin]] diff --git a/src/uu/whoami/Cargo.toml b/src/uu/whoami/Cargo.toml index d12ea1aea..5af93579f 100644 --- a/src/uu/whoami/Cargo.toml +++ b/src/uu/whoami/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_whoami" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "whoami ~ (uutils) display user name of current effective user ID" @@ -16,8 +16,8 @@ path = "src/whoami.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["lmcons"] } diff --git a/src/uu/yes/Cargo.toml b/src/uu/yes/Cargo.toml index ea2aae3b1..ad8a87b46 100644 --- a/src/uu/yes/Cargo.toml +++ b/src/uu/yes/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uu_yes" -version = "0.0.7" +version = "0.0.8" authors = ["uutils developers"] license = "MIT" description = "yes ~ (uutils) repeatedly display a line with STRING (or 'y')" @@ -16,8 +16,8 @@ path = "src/yes.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } -uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["pipes"] } -uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["pipes"] } +uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] nix = "0.20.0" diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 3f2276bd3..952eecc28 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uucore" -version = "0.0.9" +version = "0.0.10" authors = ["uutils developers"] license = "MIT" description = "uutils ~ 'core' uutils code library (cross-platform)" diff --git a/src/uucore_procs/Cargo.toml b/src/uucore_procs/Cargo.toml index 809bbc867..3efe80e00 100644 --- a/src/uucore_procs/Cargo.toml +++ b/src/uucore_procs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uucore_procs" -version = "0.0.6" +version = "0.0.7" authors = ["Roy Ivy III "] license = "MIT" description = "uutils ~ 'uucore' proc-macros" diff --git a/util/update-version.sh b/util/update-version.sh index eeacd500f..cbb811300 100755 --- a/util/update-version.sh +++ b/util/update-version.sh @@ -3,14 +3,14 @@ # So, it should be triple-checked -FROM="0.0.6" -TO="0.0.7" +FROM="0.0.7" +TO="0.0.8" -UUCORE_PROCS_FROM="0.0.5" -UUCORE_PROCS_TO="0.0.6" +UUCORE_PROCS_FROM="0.0.6" +UUCORE_PROCS_TO="0.0.7" -UUCORE_FROM="0.0.8" -UUCORE_TO="0.0.9" +UUCORE_FROM="0.0.9" +UUCORE_TO="0.0.10" PROGS=$(ls -1d src/uu/*/Cargo.toml src/uu/stdbuf/src/libstdbuf/Cargo.toml Cargo.toml src/uu/base64/Cargo.toml) From 0ffbf28cbae0fb78a9873163fdbb3d8955064af0 Mon Sep 17 00:00:00 2001 From: Smicry Date: Sun, 24 Oct 2021 02:02:49 +0800 Subject: [PATCH 065/131] uniq: use UResult in util --- src/uu/uniq/src/uniq.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/uu/uniq/src/uniq.rs b/src/uu/uniq/src/uniq.rs index f84bfc26d..382118bdb 100644 --- a/src/uu/uniq/src/uniq.rs +++ b/src/uu/uniq/src/uniq.rs @@ -15,6 +15,7 @@ use std::path::Path; use std::str::FromStr; use strum_macros::{AsRefStr, EnumString}; use uucore::display::Quotable; +use uucore::error::{UResult, USimpleError}; static ABOUT: &str = "Report or omit repeated lines."; pub mod options { @@ -245,7 +246,8 @@ fn get_long_usage() -> String { ) } -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let usage = usage(); let long_usage = get_long_usage(); @@ -265,7 +267,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { 2 => (files[0].clone(), files[1].clone()), _ => { // Cannot happen as clap will fail earlier - crash!(1, "Extra operand: {}", files[2]); + return Err(USimpleError::new(1, format!("Extra operand: {}", files[2]))); } }; @@ -288,7 +290,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { &mut open_output_file(out_file_name), ); - 0 + Ok(()) } pub fn uu_app() -> App<'static, 'static> { From a5bc2211d5e6761396d098363d3132efeeab487f Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 23 Oct 2021 20:59:19 +0200 Subject: [PATCH 066/131] Ignore two question-mark warnings --- src/uu/install/src/install.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index 1c09f7f34..a93add322 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -461,6 +461,8 @@ fn standard(mut paths: Vec, b: Behavior) -> UResult<()> { return Err(InstallError::CreateDirFailed(parent.to_path_buf(), e).into()); } + // Silent the warning as we want to the error message + #[allow(clippy::question_mark)] if mode::chmod(parent, b.mode()).is_err() { return Err(InstallError::ChmodFailed(parent.to_path_buf()).into()); } @@ -583,6 +585,8 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> { } } + // Silent the warning as we want to the error message + #[allow(clippy::question_mark)] if mode::chmod(to, b.mode()).is_err() { return Err(InstallError::ChmodFailed(to.to_path_buf()).into()); } From b5977021bca774aa8da6986d68a3111c0caccdb9 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 24 Oct 2021 00:11:30 +0200 Subject: [PATCH 067/131] Fix the uu_base32 dep --- src/uu/base64/Cargo.toml | 2 +- src/uu/basenc/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/base64/Cargo.toml b/src/uu/base64/Cargo.toml index 0121b78f6..c8d71b85a 100644 --- a/src/uu/base64/Cargo.toml +++ b/src/uu/base64/Cargo.toml @@ -18,7 +18,7 @@ path = "src/base64.rs" clap = { version = "2.33", features = ["wrap_help"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } -uu_base32 = { version=">=0.0.6", package="uu_base32", path="../base32"} +uu_base32 = { version=">=0.0.8", package="uu_base32", path="../base32"} [[bin]] name = "base64" diff --git a/src/uu/basenc/Cargo.toml b/src/uu/basenc/Cargo.toml index 609a2d453..165d3d3a0 100644 --- a/src/uu/basenc/Cargo.toml +++ b/src/uu/basenc/Cargo.toml @@ -18,7 +18,7 @@ path = "src/basenc.rs" clap = { version = "2.33", features = ["wrap_help"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } -uu_base32 = { version=">=0.0.6", package="uu_base32", path="../base32"} +uu_base32 = { version=">=0.0.8", package="uu_base32", path="../base32"} [[bin]] name = "basenc" From 6946377e0203ad88a8f701fe5dbc52ea09d84b5b Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 24 Oct 2021 00:29:24 +0200 Subject: [PATCH 068/131] cp: Also import entries from uucore --- src/uu/cp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/cp/Cargo.toml b/src/uu/cp/Cargo.toml index 02febab74..891bf0244 100644 --- a/src/uu/cp/Cargo.toml +++ b/src/uu/cp/Cargo.toml @@ -24,7 +24,7 @@ filetime = "0.2" libc = "0.2.85" quick-error = "1.2.3" selinux = { version="0.2.3", optional=true } -uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "perms", "mode"] } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs", "perms", "mode"] } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } walkdir = "2.2" From 43e85ae9ce96ef6892686a0f8e8fd9d916799bea Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 24 Oct 2021 00:33:00 +0200 Subject: [PATCH 069/131] install: Also import fs from uucore --- src/uu/install/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/install/Cargo.toml b/src/uu/install/Cargo.toml index 1b5af3dba..0ae11b3c4 100644 --- a/src/uu/install/Cargo.toml +++ b/src/uu/install/Cargo.toml @@ -22,7 +22,7 @@ clap = { version = "2.33", features = ["wrap_help"] } filetime = "0.2" file_diff = "1.0.0" libc = ">= 0.2" -uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["mode", "perms", "entries"] } +uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "mode", "perms", "entries"] } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [dev-dependencies] From d72a135b42b3c6eec2de587dd80a97f681cedd2f Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 24 Oct 2021 00:51:27 +0200 Subject: [PATCH 070/131] improve the publish script --- util/publish.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/publish.sh b/util/publish.sh index 0936238a8..ae171e39c 100755 --- a/util/publish.sh +++ b/util/publish.sh @@ -9,10 +9,17 @@ fi cd src/uucore/ cargo publish $ARG cd - +sleep 2s + +cd src/uucore_procs/ +cargo publish $ARG +cd - +sleep 2s cd src/uu/stdbuf/src/libstdbuf/ cargo publish $ARG cd - +sleep 2s PROGS=$(ls -1d src/uu/*/) for p in $PROGS; do From 5e5bb91043eb598b39adf46410f97b9ae5dbcf03 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 23 Oct 2021 23:21:45 -0300 Subject: [PATCH 071/131] sort: remove unecessary implementation --- src/uu/sort/src/sort.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index bd79a6811..84ded8ba9 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -171,10 +171,6 @@ impl UError for SortError { _ => 2, } } - - fn usage(&self) -> bool { - false - } } impl Display for SortError { From 21a2d0ce408166eac3850892e7ce2ed54488a20d Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 23 Oct 2021 23:22:21 -0300 Subject: [PATCH 072/131] sort: use Range.is_empty --- src/uu/sort/src/sort.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 84ded8ba9..ae1bcfa4c 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -659,18 +659,14 @@ impl<'a> Line<'a> { " ".repeat(UnicodeWidthStr::width(&line[..selection.start])) )?; - // TODO: Once our minimum supported rust version is at least 1.47, use selection.is_empty() instead. - #[allow(clippy::len_zero)] - { - if selection.len() == 0 { - writeln!(writer, "^ no match for key")?; - } else { - writeln!( - writer, - "{}", - "_".repeat(UnicodeWidthStr::width(&line[selection])) - )?; - } + if selection.is_empty() { + writeln!(writer, "^ no match for key")?; + } else { + writeln!( + writer, + "{}", + "_".repeat(UnicodeWidthStr::width(&line[selection])) + )?; } } if settings.mode != SortMode::Random From 1f15b8fce4b30c805aec5ea46360134fda82bc04 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 23 Oct 2021 23:25:22 -0300 Subject: [PATCH 073/131] cksum: use while loops instead of unroll! --- src/uu/cksum/src/cksum.rs | 73 +++++---------------------------------- 1 file changed, 9 insertions(+), 64 deletions(-) diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 55c4f980f..92853a3e8 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -25,60 +25,15 @@ const NAME: &str = "cksum"; const SYNTAX: &str = "[OPTIONS] [FILE]..."; const SUMMARY: &str = "Print CRC and size for each file"; -// this is basically a hack to get "loops" to work on Rust 1.33. Once we update to Rust 1.46 or -// greater, we can just use while loops -macro_rules! unroll { - (256, |$i:ident| $s:expr) => {{ - unroll!(@ 32, 0 * 32, $i, $s); - unroll!(@ 32, 1 * 32, $i, $s); - unroll!(@ 32, 2 * 32, $i, $s); - unroll!(@ 32, 3 * 32, $i, $s); - unroll!(@ 32, 4 * 32, $i, $s); - unroll!(@ 32, 5 * 32, $i, $s); - unroll!(@ 32, 6 * 32, $i, $s); - unroll!(@ 32, 7 * 32, $i, $s); - }}; - (8, |$i:ident| $s:expr) => {{ - unroll!(@ 8, 0, $i, $s); - }}; - - (@ 32, $start:expr, $i:ident, $s:expr) => {{ - unroll!(@ 8, $start + 0 * 8, $i, $s); - unroll!(@ 8, $start + 1 * 8, $i, $s); - unroll!(@ 8, $start + 2 * 8, $i, $s); - unroll!(@ 8, $start + 3 * 8, $i, $s); - }}; - (@ 8, $start:expr, $i:ident, $s:expr) => {{ - unroll!(@ 4, $start, $i, $s); - unroll!(@ 4, $start + 4, $i, $s); - }}; - (@ 4, $start:expr, $i:ident, $s:expr) => {{ - unroll!(@ 2, $start, $i, $s); - unroll!(@ 2, $start + 2, $i, $s); - }}; - (@ 2, $start:expr, $i:ident, $s:expr) => {{ - unroll!(@ 1, $start, $i, $s); - unroll!(@ 1, $start + 1, $i, $s); - }}; - (@ 1, $start:expr, $i:ident, $s:expr) => {{ - let $i = $start; - let _ = $s; - }}; -} - const fn generate_crc_table() -> [u32; CRC_TABLE_LEN] { let mut table = [0; CRC_TABLE_LEN]; - // NOTE: works on Rust 1.46 - //let mut i = 0; - //while i < CRC_TABLE_LEN { - // table[i] = crc_entry(i as u8) as u32; - // - // i += 1; - //} - unroll!(256, |i| { + let mut i = 0; + while i < CRC_TABLE_LEN { table[i] = crc_entry(i as u8) as u32; - }); + + i += 1; + } table } @@ -86,19 +41,8 @@ const fn generate_crc_table() -> [u32; CRC_TABLE_LEN] { const fn crc_entry(input: u8) -> u32 { let mut crc = (input as u32) << 24; - // NOTE: this does not work on Rust 1.33, but *does* on 1.46 - //let mut i = 0; - //while i < 8 { - // if crc & 0x8000_0000 != 0 { - // crc <<= 1; - // crc ^= 0x04c1_1db7; - // } else { - // crc <<= 1; - // } - // - // i += 1; - //} - unroll!(8, |_i| { + let mut i = 0; + while i < 8 { let if_condition = crc & 0x8000_0000; let if_body = (crc << 1) ^ 0x04c1_1db7; let else_body = crc << 1; @@ -108,7 +52,8 @@ const fn crc_entry(input: u8) -> u32 { let condition_table = [else_body, if_body]; crc = condition_table[(if_condition != 0) as usize]; - }); + i += 1; + } crc } From ea9b23984125cb5bf82afda05eb62bd460a811bc Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sun, 24 Oct 2021 00:03:33 -0300 Subject: [PATCH 074/131] uniq: use UResult --- src/uu/uniq/src/uniq.rs | 118 +++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/src/uu/uniq/src/uniq.rs b/src/uu/uniq/src/uniq.rs index 382118bdb..c6c6a444d 100644 --- a/src/uu/uniq/src/uniq.rs +++ b/src/uu/uniq/src/uniq.rs @@ -5,17 +5,14 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. -#[macro_use] -extern crate uucore; - use clap::{crate_version, App, Arg, ArgMatches}; use std::fs::File; -use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Result, Write}; +use std::io::{self, stdin, stdout, BufRead, BufReader, BufWriter, Read, Write}; use std::path::Path; use std::str::FromStr; use strum_macros::{AsRefStr, EnumString}; use uucore::display::Quotable; -use uucore::error::{UResult, USimpleError}; +use uucore::error::{FromIo, UResult, USimpleError}; static ABOUT: &str = "Report or omit repeated lines."; pub mod options { @@ -56,36 +53,45 @@ struct Uniq { zero_terminated: bool, } +macro_rules! write_line_terminator { + ($writer:expr, $line_terminator:expr) => { + $writer + .write_all(&[$line_terminator]) + .map_err_context(|| "Could not write line terminator".to_string()) + }; +} + impl Uniq { pub fn print_uniq( &self, reader: &mut BufReader, writer: &mut BufWriter, - ) { + ) -> UResult<()> { let mut first_line_printed = false; let mut group_count = 1; let line_terminator = self.get_line_terminator(); let mut lines = reader.split(line_terminator).map(get_line_string); let mut line = match lines.next() { - Some(l) => l, - None => return, + Some(l) => l?, + None => return Ok(()), }; // compare current `line` with consecutive lines (`next_line`) of the input // and if needed, print `line` based on the command line options provided for next_line in lines { + let next_line = next_line?; if self.cmp_keys(&line, &next_line) { if (group_count == 1 && !self.repeats_only) || (group_count > 1 && !self.uniques_only) { - self.print_line(writer, &line, group_count, first_line_printed); + self.print_line(writer, &line, group_count, first_line_printed)?; first_line_printed = true; } line = next_line; group_count = 1; } else { if self.all_repeated { - self.print_line(writer, &line, group_count, first_line_printed); + self.print_line(writer, &line, group_count, first_line_printed)?; first_line_printed = true; line = next_line; } @@ -93,14 +99,15 @@ impl Uniq { } } if (group_count == 1 && !self.repeats_only) || (group_count > 1 && !self.uniques_only) { - self.print_line(writer, &line, group_count, first_line_printed); + self.print_line(writer, &line, group_count, first_line_printed)?; first_line_printed = true; } if (self.delimiters == Delimiters::Append || self.delimiters == Delimiters::Both) && first_line_printed { - crash_if_err!(1, writer.write_all(&[line_terminator])); + write_line_terminator!(writer, line_terminator)?; } + Ok(()) } fn skip_fields<'a>(&self, line: &'a str) -> &'a str { @@ -192,41 +199,43 @@ impl Uniq { line: &str, count: usize, first_line_printed: bool, - ) { + ) -> UResult<()> { let line_terminator = self.get_line_terminator(); if self.should_print_delimiter(count, first_line_printed) { - crash_if_err!(1, writer.write_all(&[line_terminator])); + write_line_terminator!(writer, line_terminator)?; } - crash_if_err!( - 1, - if self.show_counts { - writer.write_all(format!("{:7} {}", count, line).as_bytes()) - } else { - writer.write_all(line.as_bytes()) - } - ); - crash_if_err!(1, writer.write_all(&[line_terminator])); + if self.show_counts { + writer.write_all(format!("{:7} {}", count, line).as_bytes()) + } else { + writer.write_all(line.as_bytes()) + } + .map_err_context(|| "Failed to write line".to_string())?; + + write_line_terminator!(writer, line_terminator) } } -fn get_line_string(io_line: Result>) -> String { - let line_bytes = crash_if_err!(1, io_line); - crash_if_err!(1, String::from_utf8(line_bytes)) +fn get_line_string(io_line: io::Result>) -> UResult { + let line_bytes = io_line.map_err_context(|| "failed to split lines".to_string())?; + String::from_utf8(line_bytes) + .map_err(|e| USimpleError::new(1, format!("failed to convert line to utf8: {}", e))) } -fn opt_parsed(opt_name: &str, matches: &ArgMatches) -> Option { - matches.value_of(opt_name).map(|arg_str| { - let opt_val: Option = arg_str.parse().ok(); - opt_val.unwrap_or_else(|| { - crash!( +fn opt_parsed(opt_name: &str, matches: &ArgMatches) -> UResult> { + Ok(match matches.value_of(opt_name) { + Some(arg_str) => Some(arg_str.parse().map_err(|_| { + USimpleError::new( 1, - "Invalid argument for {}: {}", - opt_name, - arg_str.maybe_quote() + format!( + "Invalid argument for {}: {}", + opt_name, + arg_str.maybe_quote() + ), ) - }) + })?), + None => None, }) } @@ -266,8 +275,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { 1 => (files[0].clone(), "-".to_owned()), 2 => (files[0].clone(), files[1].clone()), _ => { - // Cannot happen as clap will fail earlier - return Err(USimpleError::new(1, format!("Extra operand: {}", files[2]))); + unreachable!() // Cannot happen as clap will fail earlier } }; @@ -279,18 +287,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { || matches.is_present(options::GROUP), delimiters: get_delimiter(&matches), show_counts: matches.is_present(options::COUNT), - skip_fields: opt_parsed(options::SKIP_FIELDS, &matches), - slice_start: opt_parsed(options::SKIP_CHARS, &matches), - slice_stop: opt_parsed(options::CHECK_CHARS, &matches), + skip_fields: opt_parsed(options::SKIP_FIELDS, &matches)?, + slice_start: opt_parsed(options::SKIP_CHARS, &matches)?, + slice_stop: opt_parsed(options::CHECK_CHARS, &matches)?, ignore_case: matches.is_present(options::IGNORE_CASE), zero_terminated: matches.is_present(options::ZERO_TERMINATED), }; uniq.print_uniq( - &mut open_input_file(in_file_name), - &mut open_output_file(out_file_name), - ); - - Ok(()) + &mut open_input_file(in_file_name)?, + &mut open_output_file(out_file_name)?, + ) } pub fn uu_app() -> App<'static, 'static> { @@ -390,7 +396,7 @@ fn get_delimiter(matches: &ArgMatches) -> Delimiters { .value_of(options::ALL_REPEATED) .or_else(|| matches.value_of(options::GROUP)); if let Some(delimiter_arg) = value { - crash_if_err!(1, Delimiters::from_str(delimiter_arg)) + Delimiters::from_str(delimiter_arg).unwrap() // All possible values for ALL_REPEATED are &str's of Delimiters } else if matches.is_present(options::GROUP) { Delimiters::Separate } else { @@ -398,26 +404,26 @@ fn get_delimiter(matches: &ArgMatches) -> Delimiters { } } -fn open_input_file(in_file_name: String) -> BufReader> { +fn open_input_file(in_file_name: String) -> UResult>> { let in_file = if in_file_name == "-" { Box::new(stdin()) as Box } else { let path = Path::new(&in_file_name[..]); - let in_file = File::open(&path); - let r = crash_if_err!(1, in_file); - Box::new(r) as Box + let in_file = File::open(&path) + .map_err_context(|| format!("Could not open {}", in_file_name.maybe_quote()))?; + Box::new(in_file) as Box }; - BufReader::new(in_file) + Ok(BufReader::new(in_file)) } -fn open_output_file(out_file_name: String) -> BufWriter> { +fn open_output_file(out_file_name: String) -> UResult>> { let out_file = if out_file_name == "-" { Box::new(stdout()) as Box } else { let path = Path::new(&out_file_name[..]); - let in_file = File::create(&path); - let w = crash_if_err!(1, in_file); - Box::new(w) as Box + let out_file = File::create(&path) + .map_err_context(|| format!("Could not create {}", out_file_name.maybe_quote()))?; + Box::new(out_file) as Box }; - BufWriter::new(out_file) + Ok(BufWriter::new(out_file)) } From 1d8381064a69c3a14f5023b730b6fecd4673199a Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sun, 24 Oct 2021 02:16:22 -0300 Subject: [PATCH 075/131] tests/uniq: update test --- tests/by-util/test_uniq.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/by-util/test_uniq.rs b/tests/by-util/test_uniq.rs index c191ffcaf..cd013891b 100644 --- a/tests/by-util/test_uniq.rs +++ b/tests/by-util/test_uniq.rs @@ -145,7 +145,9 @@ fn test_invalid_utf8() { .arg("not-utf8-sequence.txt") .run() .failure() - .stderr_only("uniq: invalid utf-8 sequence of 1 bytes from index 0"); + .stderr_only( + "uniq: failed to convert line to utf8: invalid utf-8 sequence of 1 bytes from index 0", + ); } #[test] From 007f1b9f84a39bad9c5c36c785322df491f675d3 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Sat, 23 Oct 2021 22:41:51 -0300 Subject: [PATCH 076/131] uu+tests: use strip_prefix and strip_suffix --- src/uu/basename/src/basename.rs | 24 +++++++++--------------- src/uu/ls/src/ls.rs | 8 +++----- src/uu/sort/src/chunks.rs | 7 ++----- src/uucore/src/lib/features/mode.rs | 5 ++--- tests/common/util.rs | 14 +++----------- 5 files changed, 19 insertions(+), 39 deletions(-) diff --git a/src/uu/basename/src/basename.rs b/src/uu/basename/src/basename.rs index 464c0e4f1..f7f4a3d08 100644 --- a/src/uu/basename/src/basename.rs +++ b/src/uu/basename/src/basename.rs @@ -131,21 +131,15 @@ fn basename(fullname: &str, suffix: &str) -> String { // Convert to path buffer and get last path component let pb = PathBuf::from(path); match pb.components().last() { - Some(c) => strip_suffix(c.as_os_str().to_str().unwrap(), suffix), + Some(c) => { + let name = c.as_os_str().to_str().unwrap(); + if name == suffix { + name.to_string() + } else { + name.strip_suffix(suffix).unwrap_or(name).to_string() + } + } + None => "".to_owned(), } } - -// can be replaced with strip_suffix once MSRV is 1.45 -#[allow(clippy::manual_strip)] -fn strip_suffix(name: &str, suffix: &str) -> String { - if name == suffix { - return name.to_owned(); - } - - if name.ends_with(suffix) { - return name[..name.len() - suffix.len()].to_owned(); - } - - name.to_owned() -} diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 3fa3b4f8e..1b6a73b39 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -2103,11 +2103,9 @@ fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) - } Ok(None) => substitute_string, Ok(Some(context)) => { - let mut context = context.as_bytes(); - if context.ends_with(&[0]) { - // TODO: replace with `strip_prefix()` when MSRV >= 1.51 - context = &context[..context.len() - 1] - }; + let context = context.as_bytes(); + + let context = context.strip_suffix(&[0]).unwrap_or(context); String::from_utf8(context.to_vec()).unwrap_or_else(|e| { show_warning!( "getting security context of: {}: {}", diff --git a/src/uu/sort/src/chunks.rs b/src/uu/sort/src/chunks.rs index 80d6060d4..bfe6aa73b 100644 --- a/src/uu/sort/src/chunks.rs +++ b/src/uu/sort/src/chunks.rs @@ -193,16 +193,13 @@ pub fn read( /// Split `read` into `Line`s, and add them to `lines`. fn parse_lines<'a>( - mut read: &'a str, + read: &'a str, lines: &mut Vec>, line_data: &mut LineData<'a>, separator: u8, settings: &GlobalSettings, ) { - // Strip a trailing separator. TODO: Once our MinRustV is 1.45 or above, use strip_suffix() instead. - if read.ends_with(separator as char) { - read = &read[..read.len() - 1]; - } + let read = read.strip_suffix(separator as char).unwrap_or(read); assert!(lines.is_empty()); assert!(line_data.selections.is_empty()); diff --git a/src/uucore/src/lib/features/mode.rs b/src/uucore/src/lib/features/mode.rs index 5e299f988..2206177a3 100644 --- a/src/uucore/src/lib/features/mode.rs +++ b/src/uucore/src/lib/features/mode.rs @@ -163,12 +163,11 @@ pub fn strip_minus_from_mode(args: &mut Vec) -> bool { if arg == "--" { break; } - if arg.starts_with('-') { + if let Some(arg_stripped) = arg.strip_prefix('-') { if let Some(second) = arg.chars().nth(1) { match second { 'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'..='7' => { - // TODO: use strip_prefix() once minimum rust version reaches 1.45.0 - *arg = arg[1..arg.len()].to_string(); + *arg = arg_stripped.to_string(); return true; } _ => {} diff --git a/tests/common/util.rs b/tests/common/util.rs index c7ac816e1..e71f18573 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -728,20 +728,12 @@ impl AtPath { // Source: // http://stackoverflow.com/questions/31439011/getfinalpathnamebyhandle-without-prepended let prefix = "\\\\?\\"; - // FixME: replace ... - #[allow(clippy::manual_strip)] - if s.starts_with(prefix) { - String::from(&s[prefix.len()..]) + + if let Some(stripped) = s.strip_prefix(prefix) { + String::from(stripped) } else { s } - // ... with ... - // if let Some(stripped) = s.strip_prefix(prefix) { - // String::from(stripped) - // } else { - // s - // } - // ... when using MSRV with stabilized `strip_prefix()` } } From 3e1c5c2d990d1706f59731991350f21e91809893 Mon Sep 17 00:00:00 2001 From: Kevin Burke Date: Thu, 28 Oct 2021 21:50:58 -0700 Subject: [PATCH 077/131] rm: allow -r flag to be specified multiple times GNU rm allows the `-r` flag to be specified multiple times, but uutils/coreutils would previously exit with an error. I encountered this while attempting to run `make clean` on the Postgres source tree (github.com/postgres/postgres). Updates #1663. --- src/uu/rm/src/rm.rs | 1 + tests/by-util/test_rm.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 54fce52ff..4dcb18382 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -186,6 +186,7 @@ pub fn uu_app() -> App<'static, 'static> { ) .arg( Arg::with_name(OPT_RECURSIVE).short("r") + .multiple(true) .long(OPT_RECURSIVE) .help("remove directories and their contents recursively") ) diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index 32bc43837..092a5f00d 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -169,6 +169,29 @@ fn test_rm_recursive() { assert!(!at.file_exists(file_b)); } +#[test] +fn test_rm_recursive_multiple() { + let (at, mut ucmd) = at_and_ucmd!(); + let dir = "test_rm_recursive_directory"; + let file_a = "test_rm_recursive_directory/test_rm_recursive_file_a"; + let file_b = "test_rm_recursive_directory/test_rm_recursive_file_b"; + + at.mkdir(dir); + at.touch(file_a); + at.touch(file_b); + + ucmd.arg("-r") + .arg("-r") + .arg("-r") + .arg(dir) + .succeeds() + .no_stderr(); + + assert!(!at.dir_exists(dir)); + assert!(!at.file_exists(file_a)); + assert!(!at.file_exists(file_b)); +} + #[test] fn test_rm_directory_without_flag() { let (at, mut ucmd) = at_and_ucmd!(); From 0c33905e60f0343213891d8127fa7089fab97c85 Mon Sep 17 00:00:00 2001 From: Tom Parker-Shemilt Date: Tue, 2 Nov 2021 18:56:01 +0000 Subject: [PATCH 078/131] Fix FreeBSD build by downgrading MacOS version --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index d096ff43c..0372336a7 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -532,7 +532,7 @@ jobs: if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi test_freebsd: - runs-on: macos-latest + runs-on: macos-10.15 name: Tests/FreeBSD test suite env: mem: 2048 From c58bd9f569dee6cea1fe0d0d7408076bbd97cb4a Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Mon, 25 Oct 2021 14:41:15 -0300 Subject: [PATCH 079/131] env: don't panic when name is empty --- src/uu/env/src/env.rs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index aec97fc24..2bf5ed7d1 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -7,7 +7,7 @@ /* last synced with: env (GNU coreutils) 8.13 */ -// spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets +// spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets setenv putenv #[macro_use] extern crate clap; @@ -256,7 +256,32 @@ fn run_env(args: impl uucore::Args) -> UResult<()> { // set specified env vars for &(name, val) in &opts.sets { - // FIXME: set_var() panics if name is an empty string + /* + * set_var panics if name is an empty string + * set_var internally calls setenv (on unix at least), while GNU env calls putenv instead. + * + * putenv returns successfully if provided with something like "=a" and modifies the environ + * variable to contain "=a" inside it, effectively modifying the process' current environment + * to contain a malformed string in it. Using GNU's implementation, the command `env =a` + * prints out the malformed string and even invokes the child process with that environment. + * This can be seen by using `env -i =a env` or `env -i =a cat /proc/self/environ` + * + * POSIX.1-2017 doesn't seem to mention what to do if the string is malformed (at least + * not in "Chapter 8, Environment Variables" or in the definition for environ and various + * exec*'s or in the description of env in the "Shell & Utilities" volume). + * + * It also doesn't specify any checks for putenv before modifying the environ variable, which + * is likely why glibc doesn't do so. However, setenv's first argument cannot point to + * an empty string or a string containing '='. + * + * There is no benefit in replicating GNU's env behavior, since it will only modify the + * environment in weird ways + */ + + if name.is_empty() { + show_warning!("no name specified for value {}", val.quote()); + continue; + } env::set_var(name, val); } From f9512e5a90ca52dbc12bd967fc4a6341bcb95e08 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Mon, 25 Oct 2021 14:45:29 -0300 Subject: [PATCH 080/131] tests/env: add empty name test --- tests/by-util/test_env.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/by-util/test_env.rs b/tests/by-util/test_env.rs index 1d76c433d..ecb215f8c 100644 --- a/tests/by-util/test_env.rs +++ b/tests/by-util/test_env.rs @@ -108,6 +108,15 @@ fn test_ignore_environment() { scene.ucmd().arg("-").run().no_stdout(); } +#[test] +fn test_empty_name() { + new_ucmd!() + .arg("-i") + .arg("=xyz") + .run() + .stderr_only("env: warning: no name specified for value 'xyz'"); +} + #[test] fn test_null_delimiter() { let out = new_ucmd!() From 1afc7242a58d0f97a7335c369c4c8536ff80c9fa Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 2 Nov 2021 17:23:34 -0300 Subject: [PATCH 081/131] env: change comment --- src/uu/env/src/env.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 2bf5ed7d1..8967a798a 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -7,7 +7,7 @@ /* last synced with: env (GNU coreutils) 8.13 */ -// spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets setenv putenv +// spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets setenv putenv posix_spawnp #[macro_use] extern crate clap; @@ -289,7 +289,12 @@ fn run_env(args: impl uucore::Args) -> UResult<()> { // we need to execute a command let (prog, args) = build_command(&mut opts.program); - // FIXME: this should just use execvp() (no fork()) on Unix-like systems + /* + * On Unix-like systems Command::status either ends up calling either fork or posix_spawnp + * (which ends up calling clone). Keep using the current process would be ideal, but the + * standard library contains many checks and fail-safes to ensure the process ends up being + * created. This is much simpler than dealing with the hassles of calling execvp directly. + */ match Command::new(&*prog).args(args).status() { Ok(exit) if !exit.success() => return Err(exit.code().unwrap().into()), Err(ref err) if err.kind() == io::ErrorKind::NotFound => return Err(127.into()), From f2a3a1f9201340325dd459c28cb7bb66d4cd7c3e Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Fri, 29 Oct 2021 20:25:41 -0300 Subject: [PATCH 082/131] printenv: use UResult --- src/uu/printenv/src/printenv.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/uu/printenv/src/printenv.rs b/src/uu/printenv/src/printenv.rs index 5d32cfbcc..1bdd3e8c4 100644 --- a/src/uu/printenv/src/printenv.rs +++ b/src/uu/printenv/src/printenv.rs @@ -9,6 +9,7 @@ use clap::{crate_version, App, Arg}; use std::env; +use uucore::error::UResult; static ABOUT: &str = "Display the values of the specified environment VARIABLE(s), or (with no VARIABLE) display name and value pairs for them all."; @@ -20,7 +21,8 @@ fn usage() -> String { format!("{0} [VARIABLE]... [OPTION]...", uucore::execution_phrase()) } -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let usage = usage(); let matches = uu_app().usage(&usage[..]).get_matches_from(args); @@ -40,7 +42,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { for (env_var, value) in env::vars() { print!("{}={}{}", env_var, value, separator); } - return 0; + return Ok(()); } for env_var in variables { @@ -48,7 +50,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { print!("{}{}", var, separator); } } - 0 + + Ok(()) } pub fn uu_app() -> App<'static, 'static> { From b157a73a1fc9f699fc607ce28fdeece8b7cb6026 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Fri, 29 Oct 2021 20:27:06 -0300 Subject: [PATCH 083/131] printenv: change exit code when variable not found GNU printenv has this behavior --- src/uu/printenv/src/printenv.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/uu/printenv/src/printenv.rs b/src/uu/printenv/src/printenv.rs index 1bdd3e8c4..c3f996865 100644 --- a/src/uu/printenv/src/printenv.rs +++ b/src/uu/printenv/src/printenv.rs @@ -45,13 +45,20 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { return Ok(()); } + let mut not_found = false; for env_var in variables { if let Ok(var) = env::var(env_var) { print!("{}{}", var, separator); + } else { + not_found = true; } } - Ok(()) + if not_found { + Err(1.into()) + } else { + Ok(()) + } } pub fn uu_app() -> App<'static, 'static> { From db00fab7e4982c9e2bbbc7451975be171dc5d3e4 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 2 Nov 2021 19:17:54 -0300 Subject: [PATCH 084/131] env: use UResult everywhere --- src/uu/env/src/env.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 8967a798a..4fdf825dc 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -23,7 +23,7 @@ use std::io::{self, Write}; use std::iter::Iterator; use std::process::Command; use uucore::display::Quotable; -use uucore::error::{UResult, USimpleError}; +use uucore::error::{UResult, USimpleError, UUsageError}; const USAGE: &str = "env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]"; const AFTER_HELP: &str = "\ @@ -50,7 +50,7 @@ fn print_env(null: bool) { } } -fn parse_name_value_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> Result { +fn parse_name_value_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> UResult { // is it a NAME=VALUE like opt ? if let Some(idx) = opt.find('=') { // yes, so push name, value pair @@ -64,17 +64,12 @@ fn parse_name_value_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> Result(opts: &mut Options<'a>, opt: &'a str) -> Result<(), i32> { +fn parse_program_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> UResult<()> { if opts.null { - eprintln!( - "{}: cannot specify --null (-0) with command", - uucore::util_name() - ); - eprintln!( - "Type \"{} --help\" for detailed information", - uucore::execution_phrase() - ); - Err(1) + Err(UUsageError::new( + 125, + "cannot specify --null (-0) with command".to_string(), + )) } else { opts.program.push(opt); Ok(()) @@ -93,10 +88,8 @@ fn load_config_file(opts: &mut Options) -> UResult<()> { Ini::load_from_file(file) }; - let conf = conf.map_err(|error| { - show_error!("{}: {}", file.maybe_quote(), error); - 1 - })?; + let conf = + conf.map_err(|e| USimpleError::new(1, format!("{}: {}", file.maybe_quote(), e)))?; for (_, prop) in &conf { // ignore all INI section lines (treat them as comments) From 3d74e7b452e8473ebc3cc7afefa82e21c5bb030d Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 2 Nov 2021 19:22:03 -0300 Subject: [PATCH 085/131] env: prevent panic when unsetting invalid variable --- src/uu/env/src/env.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 4fdf825dc..042d2c380 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -244,6 +244,13 @@ fn run_env(args: impl uucore::Args) -> UResult<()> { // unset specified env vars for name in &opts.unsets { + if name.is_empty() || name.contains(0 as char) || name.contains('=') { + return Err(USimpleError::new( + 125, + format!("cannot unset {}: Invalid argument", name.quote()), + )); + } + env::remove_var(name); } From c44b5952b8ae2fef24f41a428a8de554964558f8 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 2 Nov 2021 19:32:41 -0300 Subject: [PATCH 086/131] tests/env: add unsetting invalid variables test --- tests/by-util/test_env.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/by-util/test_env.rs b/tests/by-util/test_env.rs index ecb215f8c..0d9d94b01 100644 --- a/tests/by-util/test_env.rs +++ b/tests/by-util/test_env.rs @@ -1,4 +1,4 @@ -// spell-checker:ignore (words) bamf chdir +// spell-checker:ignore (words) bamf chdir rlimit prlimit #[cfg(not(windows))] use std::fs; @@ -80,6 +80,20 @@ fn test_combined_file_set_unset() { ); } +#[test] +fn test_unset_invalid_variables() { + use uucore::display::Quotable; + + // Cannot test input with \0 in it, since output will also contain \0. rlimit::prlimit fails + // with this error: Error { kind: InvalidInput, message: "nul byte found in provided data" } + for var in &["", "a=b"] { + new_ucmd!().arg("-u").arg(var).run().stderr_only(format!( + "env: cannot unset {}: Invalid argument", + var.quote() + )); + } +} + #[test] fn test_single_name_value_pair() { let out = new_ucmd!().arg("FOO=bar").run(); From 013405d1e60761f02aeeca9b007b3c91df0ab60b Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 2 Nov 2021 19:18:59 -0300 Subject: [PATCH 087/131] env: change -c to -C --- src/uu/env/src/env.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 042d2c380..6d5431aa9 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -131,7 +131,7 @@ pub fn uu_app() -> App<'static, 'static> { .long("ignore-environment") .help("start with an empty environment")) .arg(Arg::with_name("chdir") - .short("c") + .short("C") // GNU env compatibility .long("chdir") .takes_value(true) .number_of_values(1) From 8ad95c375ad6376143bd8ba938525bb4c7894ccb Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 2 Nov 2021 19:53:40 -0300 Subject: [PATCH 088/131] env: force specifying command with --chdir --- src/uu/env/src/env.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 6d5431aa9..835c8766c 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -229,6 +229,14 @@ fn run_env(args: impl uucore::Args) -> UResult<()> { } } + // GNU env tests this behavior + if opts.program.is_empty() && running_directory.is_some() { + return Err(UUsageError::new( + 125, + "must specify command with --chdir (-C)".to_string(), + )); + } + // NOTE: we manually set and unset the env vars below rather than using Command::env() to more // easily handle the case where no command is given From 7baa05b2db54f96a5b168a55e2283ebe9f5790b1 Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Thu, 4 Nov 2021 17:04:26 +0800 Subject: [PATCH 089/131] Add tests for silently accepting presume-input-tty Signed-off-by: Hanif Bin Ariffin --- tests/by-util/test_rm.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index 092a5f00d..740c30bdd 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -314,3 +314,39 @@ fn test_rm_verbose_slash() { assert!(!at.dir_exists(dir)); assert!(!at.file_exists(file_a)); } + +#[test] +fn test_rm_silently_accepts_presume_input_tty1() { + let (at, mut ucmd) = at_and_ucmd!(); + let file_1 = "test_rm_silently_accepts_presume_input_tty1"; + + at.touch(file_1); + + ucmd.arg("--presume-input-tty").arg(file_1).succeeds(); + + assert!(!at.file_exists(file_1)); +} + +#[test] +fn test_rm_silently_accepts_presume_input_tty2() { + let (at, mut ucmd) = at_and_ucmd!(); + let file_2 = "test_rm_silently_accepts_presume_input_tty2"; + + at.touch(file_2); + + ucmd.arg("---presume-input-tty").arg(file_2).succeeds(); + + assert!(!at.file_exists(file_2)); +} + +#[test] +fn test_rm_silently_accepts_presume_input_tty3() { + let (at, mut ucmd) = at_and_ucmd!(); + let file_3 = "test_rm_silently_accepts_presume_input_tty3"; + + at.touch(file_3); + + ucmd.arg("----presume-input-tty").arg(file_3).succeeds(); + + assert!(!at.file_exists(file_3)); +} From a290c77cfc7276c7b2ae657180ae735b3d4ec752 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 2 Nov 2021 20:25:48 -0300 Subject: [PATCH 090/131] env: add contributor --- src/uu/env/src/env.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 835c8766c..346bf4c8e 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -1,6 +1,7 @@ // This file is part of the uutils coreutils package. // // (c) Jordi Boggiano +// (c) Thomas Queiroz // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. From cda3d5a29bfb732a0ea24fc9e79c3cab80eaf30c Mon Sep 17 00:00:00 2001 From: equal-l2 Date: Sat, 6 Nov 2021 03:05:31 +0900 Subject: [PATCH 091/131] ls: add possible value for `--color=` --- src/uu/ls/src/ls.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 1b6a73b39..32707da36 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1111,6 +1111,9 @@ only ignore '.' and '..'.", .long(options::COLOR) .help("Color output based on file type.") .takes_value(true) + .possible_values(&[ + "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", + ]) .require_equals(true) .min_values(0), ) From 2e12316ae1cea76e6b8a1ff696b6766c44f8a904 Mon Sep 17 00:00:00 2001 From: jfinkels Date: Sat, 6 Nov 2021 10:44:42 -0400 Subject: [PATCH 092/131] seq: use BigDecimal to represent floats (#2698) * seq: use BigDecimal to represent floats Use `BigDecimal` to represent arbitrary precision floats in order to prevent numerical precision issues when iterating over a sequence of numbers. This commit makes several changes at once to accomplish this goal. First, it creates a new struct, `PreciseNumber`, that is responsible for storing not only the number itself but also the number of digits (both integer and decimal) needed to display it. This information is collected at the time of parsing the number, which lives in the new `numberparse.rs` module. Second, it uses the `BigDecimal` struct to store arbitrary precision floating point numbers instead of the previous `f64` primitive type. This protects against issues of numerical precision when repeatedly accumulating a very small increment. Third, since neither the `BigDecimal` nor `BigInt` types have a representation of infinity, minus infinity, minus zero, or NaN, we add the `ExtendedBigDecimal` and `ExtendedBigInt` enumerations which extend the basic types with these concepts. * fixup! seq: use BigDecimal to represent floats * fixup! seq: use BigDecimal to represent floats * fixup! seq: use BigDecimal to represent floats * fixup! seq: use BigDecimal to represent floats * fixup! seq: use BigDecimal to represent floats --- Cargo.lock | 12 + src/uu/seq/BENCHMARKING.md | 19 + src/uu/seq/Cargo.toml | 2 + src/uu/seq/src/digits.rs | 190 --------- src/uu/seq/src/extendedbigdecimal.rs | 290 +++++++++++++ src/uu/seq/src/extendedbigint.rs | 218 ++++++++++ src/uu/seq/src/number.rs | 119 ++++++ src/uu/seq/src/numberparse.rs | 589 +++++++++++++++++++++++++++ src/uu/seq/src/seq.rs | 378 +++++++---------- tests/by-util/test_seq.rs | 79 +++- 10 files changed, 1471 insertions(+), 425 deletions(-) create mode 100644 src/uu/seq/BENCHMARKING.md delete mode 100644 src/uu/seq/src/digits.rs create mode 100644 src/uu/seq/src/extendedbigdecimal.rs create mode 100644 src/uu/seq/src/extendedbigint.rs create mode 100644 src/uu/seq/src/number.rs create mode 100644 src/uu/seq/src/numberparse.rs diff --git a/Cargo.lock b/Cargo.lock index 4a40fd883..5bd0e776a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,6 +76,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "bigdecimal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "binary-heap-plus" version = "0.4.1" @@ -2912,6 +2923,7 @@ dependencies = [ name = "uu_seq" version = "0.0.8" dependencies = [ + "bigdecimal", "clap", "num-bigint", "num-traits", diff --git a/src/uu/seq/BENCHMARKING.md b/src/uu/seq/BENCHMARKING.md new file mode 100644 index 000000000..4d2f82afe --- /dev/null +++ b/src/uu/seq/BENCHMARKING.md @@ -0,0 +1,19 @@ +# Benchmarking to measure performance + +To compare the performance of the `uutils` version of `seq` with the +GNU version of `seq`, you can use a benchmarking tool like +[hyperfine][0]. On Ubuntu 18.04 or later, you can install `hyperfine` by +running + + sudo apt-get install hyperfine + +Next, build the `seq` binary under the release profile: + + cargo build --release -p uu_seq + +Finally, you can compare the performance of the two versions of `head` +by running, for example, + + hyperfine "seq 1000000" "target/release/seq 1000000" + +[0]: https://github.com/sharkdp/hyperfine diff --git a/src/uu/seq/Cargo.toml b/src/uu/seq/Cargo.toml index f5a23310f..5aeffd3b9 100644 --- a/src/uu/seq/Cargo.toml +++ b/src/uu/seq/Cargo.toml @@ -1,3 +1,4 @@ +# spell-checker:ignore bigdecimal [package] name = "uu_seq" version = "0.0.8" @@ -15,6 +16,7 @@ edition = "2018" path = "src/seq.rs" [dependencies] +bigdecimal = "0.3" clap = { version = "2.33", features = ["wrap_help"] } num-bigint = "0.4.0" num-traits = "0.2.14" diff --git a/src/uu/seq/src/digits.rs b/src/uu/seq/src/digits.rs deleted file mode 100644 index bde933978..000000000 --- a/src/uu/seq/src/digits.rs +++ /dev/null @@ -1,190 +0,0 @@ -//! Counting number of digits needed to represent a number. -//! -//! The [`num_integral_digits`] and [`num_fractional_digits`] functions -//! count the number of digits needed to represent a number in decimal -//! notation (like "123.456"). -use std::convert::TryInto; -use std::num::ParseIntError; - -use uucore::display::Quotable; - -/// The number of digits after the decimal point in a given number. -/// -/// The input `s` is a string representing a number, either an integer -/// or a floating point number in either decimal notation or scientific -/// notation. This function returns the number of digits after the -/// decimal point needed to print the number in decimal notation. -/// -/// # Examples -/// -/// ```rust,ignore -/// assert_eq!(num_fractional_digits("123.45e-1").unwrap(), 3); -/// ``` -pub fn num_fractional_digits(s: &str) -> Result { - match (s.find('.'), s.find('e')) { - // For example, "123456". - (None, None) => Ok(0), - - // For example, "123e456". - (None, Some(j)) => { - let exponent: i64 = s[j + 1..].parse()?; - if exponent < 0 { - Ok(-exponent as usize) - } else { - Ok(0) - } - } - - // For example, "123.456". - (Some(i), None) => Ok(s.len() - (i + 1)), - - // For example, "123.456e789". - (Some(i), Some(j)) if i < j => { - // Because of the match guard, this subtraction will not underflow. - let num_digits_between_decimal_point_and_e = (j - (i + 1)) as i64; - let exponent: i64 = s[j + 1..].parse()?; - if num_digits_between_decimal_point_and_e < exponent { - Ok(0) - } else { - Ok((num_digits_between_decimal_point_and_e - exponent) - .try_into() - .unwrap()) - } - } - _ => crash!( - 1, - "invalid floating point argument: {}\n Try '{} --help' for more information.", - s.quote(), - uucore::execution_phrase() - ), - } -} - -/// The number of digits before the decimal point in a given number. -/// -/// The input `s` is a string representing a number, either an integer -/// or a floating point number in either decimal notation or scientific -/// notation. This function returns the number of digits before the -/// decimal point needed to print the number in decimal notation. -/// -/// # Examples -/// -/// ```rust,ignore -/// assert_eq!(num_fractional_digits("123.45e-1").unwrap(), 2); -/// ``` -pub fn num_integral_digits(s: &str) -> Result { - match (s.find('.'), s.find('e')) { - // For example, "123456". - (None, None) => Ok(s.len()), - - // For example, "123e456". - (None, Some(j)) => { - let exponent: i64 = s[j + 1..].parse()?; - let total = j as i64 + exponent; - if total < 1 { - Ok(1) - } else { - Ok(total.try_into().unwrap()) - } - } - - // For example, "123.456". - (Some(i), None) => Ok(i), - - // For example, "123.456e789". - (Some(i), Some(j)) => { - let exponent: i64 = s[j + 1..].parse()?; - let minimum: usize = { - let integral_part: f64 = crash_if_err!(1, s[..j].parse()); - if integral_part == -0.0 && integral_part.is_sign_negative() { - 2 - } else { - 1 - } - }; - - let total = i as i64 + exponent; - if total < minimum as i64 { - Ok(minimum) - } else { - Ok(total.try_into().unwrap()) - } - } - } -} - -#[cfg(test)] -mod tests { - - mod test_num_integral_digits { - use crate::num_integral_digits; - - #[test] - fn test_integer() { - assert_eq!(num_integral_digits("123").unwrap(), 3); - } - - #[test] - fn test_decimal() { - assert_eq!(num_integral_digits("123.45").unwrap(), 3); - } - - #[test] - fn test_scientific_no_decimal_positive_exponent() { - assert_eq!(num_integral_digits("123e4").unwrap(), 3 + 4); - } - - #[test] - fn test_scientific_with_decimal_positive_exponent() { - assert_eq!(num_integral_digits("123.45e6").unwrap(), 3 + 6); - } - - #[test] - fn test_scientific_no_decimal_negative_exponent() { - assert_eq!(num_integral_digits("123e-4").unwrap(), 1); - } - - #[test] - fn test_scientific_with_decimal_negative_exponent() { - assert_eq!(num_integral_digits("123.45e-6").unwrap(), 1); - assert_eq!(num_integral_digits("123.45e-1").unwrap(), 2); - } - } - - mod test_num_fractional_digits { - use crate::num_fractional_digits; - - #[test] - fn test_integer() { - assert_eq!(num_fractional_digits("123").unwrap(), 0); - } - - #[test] - fn test_decimal() { - assert_eq!(num_fractional_digits("123.45").unwrap(), 2); - } - - #[test] - fn test_scientific_no_decimal_positive_exponent() { - assert_eq!(num_fractional_digits("123e4").unwrap(), 0); - } - - #[test] - fn test_scientific_with_decimal_positive_exponent() { - assert_eq!(num_fractional_digits("123.45e6").unwrap(), 0); - assert_eq!(num_fractional_digits("123.45e1").unwrap(), 1); - } - - #[test] - fn test_scientific_no_decimal_negative_exponent() { - assert_eq!(num_fractional_digits("123e-4").unwrap(), 4); - assert_eq!(num_fractional_digits("123e-1").unwrap(), 1); - } - - #[test] - fn test_scientific_with_decimal_negative_exponent() { - assert_eq!(num_fractional_digits("123.45e-6").unwrap(), 8); - assert_eq!(num_fractional_digits("123.45e-1").unwrap(), 3); - } - } -} diff --git a/src/uu/seq/src/extendedbigdecimal.rs b/src/uu/seq/src/extendedbigdecimal.rs new file mode 100644 index 000000000..6cad83dad --- /dev/null +++ b/src/uu/seq/src/extendedbigdecimal.rs @@ -0,0 +1,290 @@ +// spell-checker:ignore bigdecimal extendedbigdecimal extendedbigint +//! An arbitrary precision float that can also represent infinity, NaN, etc. +//! +//! The finite values are stored as [`BigDecimal`] instances. Because +//! the `bigdecimal` library does not represent infinity, NaN, etc., we +//! need to represent them explicitly ourselves. The +//! [`ExtendedBigDecimal`] enumeration does that. +//! +//! # Examples +//! +//! Addition works for [`ExtendedBigDecimal`] as it does for floats. For +//! example, adding infinity to any finite value results in infinity: +//! +//! ```rust,ignore +//! let summand1 = ExtendedBigDecimal::BigDecimal(BigDecimal::zero()); +//! let summand2 = ExtendedBigDecimal::Infinity; +//! assert_eq!(summand1 + summand2, ExtendedBigDecimal::Infinity); +//! ``` +use std::cmp::Ordering; +use std::fmt::Display; +use std::ops::Add; + +use bigdecimal::BigDecimal; +use num_bigint::BigInt; +use num_bigint::ToBigInt; +use num_traits::One; +use num_traits::Zero; + +use crate::extendedbigint::ExtendedBigInt; + +#[derive(Debug, Clone)] +pub enum ExtendedBigDecimal { + /// Arbitrary precision floating point number. + BigDecimal(BigDecimal), + + /// Floating point positive infinity. + /// + /// This is represented as its own enumeration member instead of as + /// a [`BigDecimal`] because the `bigdecimal` library does not + /// support infinity, see [here][0]. + /// + /// [0]: https://github.com/akubera/bigdecimal-rs/issues/67 + Infinity, + + /// Floating point negative infinity. + /// + /// This is represented as its own enumeration member instead of as + /// a [`BigDecimal`] because the `bigdecimal` library does not + /// support infinity, see [here][0]. + /// + /// [0]: https://github.com/akubera/bigdecimal-rs/issues/67 + MinusInfinity, + + /// Floating point negative zero. + /// + /// This is represented as its own enumeration member instead of as + /// a [`BigDecimal`] because the `bigdecimal` library does not + /// support negative zero. + MinusZero, + + /// Floating point NaN. + /// + /// This is represented as its own enumeration member instead of as + /// a [`BigDecimal`] because the `bigdecimal` library does not + /// support NaN, see [here][0]. + /// + /// [0]: https://github.com/akubera/bigdecimal-rs/issues/67 + Nan, +} + +/// The smallest integer greater than or equal to this number. +fn ceil(x: BigDecimal) -> BigInt { + if x.is_integer() { + // Unwrapping the Option because it always returns Some + x.to_bigint().unwrap() + } else { + (x + BigDecimal::one().half()).round(0).to_bigint().unwrap() + } +} + +/// The largest integer less than or equal to this number. +fn floor(x: BigDecimal) -> BigInt { + if x.is_integer() { + // Unwrapping the Option because it always returns Some + x.to_bigint().unwrap() + } else { + (x - BigDecimal::one().half()).round(0).to_bigint().unwrap() + } +} + +impl ExtendedBigDecimal { + /// The smallest integer greater than or equal to this number. + pub fn ceil(self) -> ExtendedBigInt { + match self { + ExtendedBigDecimal::BigDecimal(x) => ExtendedBigInt::BigInt(ceil(x)), + other => From::from(other), + } + } + + /// The largest integer less than or equal to this number. + pub fn floor(self) -> ExtendedBigInt { + match self { + ExtendedBigDecimal::BigDecimal(x) => ExtendedBigInt::BigInt(floor(x)), + other => From::from(other), + } + } +} + +impl From for ExtendedBigDecimal { + fn from(big_int: ExtendedBigInt) -> Self { + match big_int { + ExtendedBigInt::BigInt(n) => Self::BigDecimal(BigDecimal::from(n)), + ExtendedBigInt::Infinity => ExtendedBigDecimal::Infinity, + ExtendedBigInt::MinusInfinity => ExtendedBigDecimal::MinusInfinity, + ExtendedBigInt::MinusZero => ExtendedBigDecimal::MinusZero, + ExtendedBigInt::Nan => ExtendedBigDecimal::Nan, + } + } +} + +impl Display for ExtendedBigDecimal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ExtendedBigDecimal::BigDecimal(x) => { + let (n, p) = x.as_bigint_and_exponent(); + match p { + 0 => ExtendedBigDecimal::BigDecimal(BigDecimal::new(n * 10, 1)).fmt(f), + _ => x.fmt(f), + } + } + ExtendedBigDecimal::Infinity => f32::INFINITY.fmt(f), + ExtendedBigDecimal::MinusInfinity => f32::NEG_INFINITY.fmt(f), + ExtendedBigDecimal::MinusZero => { + // FIXME In Rust version 1.53.0 and later, the display + // of floats was updated to allow displaying negative + // zero. See + // https://github.com/rust-lang/rust/pull/78618. Currently, + // this just formats "0.0". + (0.0f32).fmt(f) + } + ExtendedBigDecimal::Nan => "nan".fmt(f), + } + } +} + +impl Zero for ExtendedBigDecimal { + fn zero() -> Self { + ExtendedBigDecimal::BigDecimal(BigDecimal::zero()) + } + fn is_zero(&self) -> bool { + match self { + Self::BigDecimal(n) => n.is_zero(), + Self::MinusZero => true, + _ => false, + } + } +} + +impl Add for ExtendedBigDecimal { + type Output = Self; + + fn add(self, other: Self) -> Self { + match (self, other) { + (Self::BigDecimal(m), Self::BigDecimal(n)) => Self::BigDecimal(m.add(n)), + (Self::BigDecimal(_), Self::MinusInfinity) => Self::MinusInfinity, + (Self::BigDecimal(_), Self::Infinity) => Self::Infinity, + (Self::BigDecimal(_), Self::Nan) => Self::Nan, + (Self::BigDecimal(m), Self::MinusZero) => Self::BigDecimal(m), + (Self::Infinity, Self::BigDecimal(_)) => Self::Infinity, + (Self::Infinity, Self::Infinity) => Self::Infinity, + (Self::Infinity, Self::MinusZero) => Self::Infinity, + (Self::Infinity, Self::MinusInfinity) => Self::Nan, + (Self::Infinity, Self::Nan) => Self::Nan, + (Self::MinusInfinity, Self::BigDecimal(_)) => Self::MinusInfinity, + (Self::MinusInfinity, Self::MinusInfinity) => Self::MinusInfinity, + (Self::MinusInfinity, Self::MinusZero) => Self::MinusInfinity, + (Self::MinusInfinity, Self::Infinity) => Self::Nan, + (Self::MinusInfinity, Self::Nan) => Self::Nan, + (Self::Nan, _) => Self::Nan, + (Self::MinusZero, other) => other, + } + } +} + +impl PartialEq for ExtendedBigDecimal { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::BigDecimal(m), Self::BigDecimal(n)) => m.eq(n), + (Self::BigDecimal(_), Self::MinusInfinity) => false, + (Self::BigDecimal(_), Self::Infinity) => false, + (Self::BigDecimal(_), Self::Nan) => false, + (Self::BigDecimal(_), Self::MinusZero) => false, + (Self::Infinity, Self::BigDecimal(_)) => false, + (Self::Infinity, Self::Infinity) => true, + (Self::Infinity, Self::MinusZero) => false, + (Self::Infinity, Self::MinusInfinity) => false, + (Self::Infinity, Self::Nan) => false, + (Self::MinusInfinity, Self::BigDecimal(_)) => false, + (Self::MinusInfinity, Self::Infinity) => false, + (Self::MinusInfinity, Self::MinusZero) => false, + (Self::MinusInfinity, Self::MinusInfinity) => true, + (Self::MinusInfinity, Self::Nan) => false, + (Self::Nan, _) => false, + (Self::MinusZero, Self::BigDecimal(_)) => false, + (Self::MinusZero, Self::Infinity) => false, + (Self::MinusZero, Self::MinusZero) => true, + (Self::MinusZero, Self::MinusInfinity) => false, + (Self::MinusZero, Self::Nan) => false, + } + } +} + +impl PartialOrd for ExtendedBigDecimal { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Self::BigDecimal(m), Self::BigDecimal(n)) => m.partial_cmp(n), + (Self::BigDecimal(_), Self::MinusInfinity) => Some(Ordering::Greater), + (Self::BigDecimal(_), Self::Infinity) => Some(Ordering::Less), + (Self::BigDecimal(_), Self::Nan) => None, + (Self::BigDecimal(m), Self::MinusZero) => m.partial_cmp(&BigDecimal::zero()), + (Self::Infinity, Self::BigDecimal(_)) => Some(Ordering::Greater), + (Self::Infinity, Self::Infinity) => Some(Ordering::Equal), + (Self::Infinity, Self::MinusZero) => Some(Ordering::Greater), + (Self::Infinity, Self::MinusInfinity) => Some(Ordering::Greater), + (Self::Infinity, Self::Nan) => None, + (Self::MinusInfinity, Self::BigDecimal(_)) => Some(Ordering::Less), + (Self::MinusInfinity, Self::Infinity) => Some(Ordering::Less), + (Self::MinusInfinity, Self::MinusZero) => Some(Ordering::Less), + (Self::MinusInfinity, Self::MinusInfinity) => Some(Ordering::Equal), + (Self::MinusInfinity, Self::Nan) => None, + (Self::Nan, _) => None, + (Self::MinusZero, Self::BigDecimal(n)) => BigDecimal::zero().partial_cmp(n), + (Self::MinusZero, Self::Infinity) => Some(Ordering::Less), + (Self::MinusZero, Self::MinusZero) => Some(Ordering::Equal), + (Self::MinusZero, Self::MinusInfinity) => Some(Ordering::Greater), + (Self::MinusZero, Self::Nan) => None, + } + } +} + +#[cfg(test)] +mod tests { + + use bigdecimal::BigDecimal; + use num_traits::Zero; + + use crate::extendedbigdecimal::ExtendedBigDecimal; + + #[test] + fn test_addition_infinity() { + let summand1 = ExtendedBigDecimal::BigDecimal(BigDecimal::zero()); + let summand2 = ExtendedBigDecimal::Infinity; + assert_eq!(summand1 + summand2, ExtendedBigDecimal::Infinity); + } + + #[test] + fn test_addition_minus_infinity() { + let summand1 = ExtendedBigDecimal::BigDecimal(BigDecimal::zero()); + let summand2 = ExtendedBigDecimal::MinusInfinity; + assert_eq!(summand1 + summand2, ExtendedBigDecimal::MinusInfinity); + } + + #[test] + fn test_addition_nan() { + let summand1 = ExtendedBigDecimal::BigDecimal(BigDecimal::zero()); + let summand2 = ExtendedBigDecimal::Nan; + let sum = summand1 + summand2; + match sum { + ExtendedBigDecimal::Nan => (), + _ => unreachable!(), + } + } + + #[test] + fn test_display() { + assert_eq!( + format!("{}", ExtendedBigDecimal::BigDecimal(BigDecimal::zero())), + "0.0" + ); + assert_eq!(format!("{}", ExtendedBigDecimal::Infinity), "inf"); + assert_eq!(format!("{}", ExtendedBigDecimal::MinusInfinity), "-inf"); + assert_eq!(format!("{}", ExtendedBigDecimal::Nan), "nan"); + // FIXME In Rust version 1.53.0 and later, the display of floats + // was updated to allow displaying negative zero. Until then, we + // just display `MinusZero` as "0.0". + // + // assert_eq!(format!("{}", ExtendedBigDecimal::MinusZero), "-0.0"); + // + } +} diff --git a/src/uu/seq/src/extendedbigint.rs b/src/uu/seq/src/extendedbigint.rs new file mode 100644 index 000000000..4a33fa617 --- /dev/null +++ b/src/uu/seq/src/extendedbigint.rs @@ -0,0 +1,218 @@ +// spell-checker:ignore bigint extendedbigint extendedbigdecimal +//! An arbitrary precision integer that can also represent infinity, NaN, etc. +//! +//! Usually infinity, NaN, and negative zero are only represented for +//! floating point numbers. The [`ExtendedBigInt`] enumeration provides +//! a representation of those things with the set of integers. The +//! finite values are stored as [`BigInt`] instances. +//! +//! # Examples +//! +//! Addition works for [`ExtendedBigInt`] as it does for floats. For +//! example, adding infinity to any finite value results in infinity: +//! +//! ```rust,ignore +//! let summand1 = ExtendedBigInt::BigInt(BigInt::zero()); +//! let summand2 = ExtendedBigInt::Infinity; +//! assert_eq!(summand1 + summand2, ExtendedBigInt::Infinity); +//! ``` +use std::cmp::Ordering; +use std::fmt::Display; +use std::ops::Add; + +use num_bigint::BigInt; +use num_bigint::ToBigInt; +use num_traits::One; +use num_traits::Zero; + +use crate::extendedbigdecimal::ExtendedBigDecimal; + +#[derive(Debug, Clone)] +pub enum ExtendedBigInt { + BigInt(BigInt), + Infinity, + MinusInfinity, + MinusZero, + Nan, +} + +impl ExtendedBigInt { + /// The integer number one. + pub fn one() -> Self { + // We would like to implement `num_traits::One`, but it requires + // a multiplication implementation, and we don't want to + // implement that here. + ExtendedBigInt::BigInt(BigInt::one()) + } +} + +impl From for ExtendedBigInt { + fn from(big_decimal: ExtendedBigDecimal) -> Self { + match big_decimal { + // TODO When can this fail? + ExtendedBigDecimal::BigDecimal(x) => Self::BigInt(x.to_bigint().unwrap()), + ExtendedBigDecimal::Infinity => ExtendedBigInt::Infinity, + ExtendedBigDecimal::MinusInfinity => ExtendedBigInt::MinusInfinity, + ExtendedBigDecimal::MinusZero => ExtendedBigInt::MinusZero, + ExtendedBigDecimal::Nan => ExtendedBigInt::Nan, + } + } +} + +impl Display for ExtendedBigInt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ExtendedBigInt::BigInt(n) => n.fmt(f), + ExtendedBigInt::Infinity => f32::INFINITY.fmt(f), + ExtendedBigInt::MinusInfinity => f32::NEG_INFINITY.fmt(f), + ExtendedBigInt::MinusZero => { + // FIXME Come up with a way of formatting this with a + // "-" prefix. + 0.fmt(f) + } + ExtendedBigInt::Nan => "nan".fmt(f), + } + } +} + +impl Zero for ExtendedBigInt { + fn zero() -> Self { + ExtendedBigInt::BigInt(BigInt::zero()) + } + fn is_zero(&self) -> bool { + match self { + Self::BigInt(n) => n.is_zero(), + Self::MinusZero => true, + _ => false, + } + } +} + +impl Add for ExtendedBigInt { + type Output = Self; + + fn add(self, other: Self) -> Self { + match (self, other) { + (Self::BigInt(m), Self::BigInt(n)) => Self::BigInt(m.add(n)), + (Self::BigInt(_), Self::MinusInfinity) => Self::MinusInfinity, + (Self::BigInt(_), Self::Infinity) => Self::Infinity, + (Self::BigInt(_), Self::Nan) => Self::Nan, + (Self::BigInt(m), Self::MinusZero) => Self::BigInt(m), + (Self::Infinity, Self::BigInt(_)) => Self::Infinity, + (Self::Infinity, Self::Infinity) => Self::Infinity, + (Self::Infinity, Self::MinusZero) => Self::Infinity, + (Self::Infinity, Self::MinusInfinity) => Self::Nan, + (Self::Infinity, Self::Nan) => Self::Nan, + (Self::MinusInfinity, Self::BigInt(_)) => Self::MinusInfinity, + (Self::MinusInfinity, Self::MinusInfinity) => Self::MinusInfinity, + (Self::MinusInfinity, Self::MinusZero) => Self::MinusInfinity, + (Self::MinusInfinity, Self::Infinity) => Self::Nan, + (Self::MinusInfinity, Self::Nan) => Self::Nan, + (Self::Nan, _) => Self::Nan, + (Self::MinusZero, other) => other, + } + } +} + +impl PartialEq for ExtendedBigInt { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::BigInt(m), Self::BigInt(n)) => m.eq(n), + (Self::BigInt(_), Self::MinusInfinity) => false, + (Self::BigInt(_), Self::Infinity) => false, + (Self::BigInt(_), Self::Nan) => false, + (Self::BigInt(_), Self::MinusZero) => false, + (Self::Infinity, Self::BigInt(_)) => false, + (Self::Infinity, Self::Infinity) => true, + (Self::Infinity, Self::MinusZero) => false, + (Self::Infinity, Self::MinusInfinity) => false, + (Self::Infinity, Self::Nan) => false, + (Self::MinusInfinity, Self::BigInt(_)) => false, + (Self::MinusInfinity, Self::Infinity) => false, + (Self::MinusInfinity, Self::MinusZero) => false, + (Self::MinusInfinity, Self::MinusInfinity) => true, + (Self::MinusInfinity, Self::Nan) => false, + (Self::Nan, _) => false, + (Self::MinusZero, Self::BigInt(_)) => false, + (Self::MinusZero, Self::Infinity) => false, + (Self::MinusZero, Self::MinusZero) => true, + (Self::MinusZero, Self::MinusInfinity) => false, + (Self::MinusZero, Self::Nan) => false, + } + } +} + +impl PartialOrd for ExtendedBigInt { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Self::BigInt(m), Self::BigInt(n)) => m.partial_cmp(n), + (Self::BigInt(_), Self::MinusInfinity) => Some(Ordering::Greater), + (Self::BigInt(_), Self::Infinity) => Some(Ordering::Less), + (Self::BigInt(_), Self::Nan) => None, + (Self::BigInt(m), Self::MinusZero) => m.partial_cmp(&BigInt::zero()), + (Self::Infinity, Self::BigInt(_)) => Some(Ordering::Greater), + (Self::Infinity, Self::Infinity) => Some(Ordering::Equal), + (Self::Infinity, Self::MinusZero) => Some(Ordering::Greater), + (Self::Infinity, Self::MinusInfinity) => Some(Ordering::Greater), + (Self::Infinity, Self::Nan) => None, + (Self::MinusInfinity, Self::BigInt(_)) => Some(Ordering::Less), + (Self::MinusInfinity, Self::Infinity) => Some(Ordering::Less), + (Self::MinusInfinity, Self::MinusZero) => Some(Ordering::Less), + (Self::MinusInfinity, Self::MinusInfinity) => Some(Ordering::Equal), + (Self::MinusInfinity, Self::Nan) => None, + (Self::Nan, _) => None, + (Self::MinusZero, Self::BigInt(n)) => BigInt::zero().partial_cmp(n), + (Self::MinusZero, Self::Infinity) => Some(Ordering::Less), + (Self::MinusZero, Self::MinusZero) => Some(Ordering::Equal), + (Self::MinusZero, Self::MinusInfinity) => Some(Ordering::Greater), + (Self::MinusZero, Self::Nan) => None, + } + } +} + +#[cfg(test)] +mod tests { + + use num_bigint::BigInt; + use num_traits::Zero; + + use crate::extendedbigint::ExtendedBigInt; + + #[test] + fn test_addition_infinity() { + let summand1 = ExtendedBigInt::BigInt(BigInt::zero()); + let summand2 = ExtendedBigInt::Infinity; + assert_eq!(summand1 + summand2, ExtendedBigInt::Infinity); + } + + #[test] + fn test_addition_minus_infinity() { + let summand1 = ExtendedBigInt::BigInt(BigInt::zero()); + let summand2 = ExtendedBigInt::MinusInfinity; + assert_eq!(summand1 + summand2, ExtendedBigInt::MinusInfinity); + } + + #[test] + fn test_addition_nan() { + let summand1 = ExtendedBigInt::BigInt(BigInt::zero()); + let summand2 = ExtendedBigInt::Nan; + let sum = summand1 + summand2; + match sum { + ExtendedBigInt::Nan => (), + _ => unreachable!(), + } + } + + #[test] + fn test_display() { + assert_eq!(format!("{}", ExtendedBigInt::BigInt(BigInt::zero())), "0"); + assert_eq!(format!("{}", ExtendedBigInt::Infinity), "inf"); + assert_eq!(format!("{}", ExtendedBigInt::MinusInfinity), "-inf"); + assert_eq!(format!("{}", ExtendedBigInt::Nan), "nan"); + // FIXME Come up with a way of displaying negative zero as + // "-0". Currently it displays as just "0". + // + // assert_eq!(format!("{}", ExtendedBigInt::MinusZero), "-0"); + // + } +} diff --git a/src/uu/seq/src/number.rs b/src/uu/seq/src/number.rs new file mode 100644 index 000000000..9062fa1a1 --- /dev/null +++ b/src/uu/seq/src/number.rs @@ -0,0 +1,119 @@ +// spell-checker:ignore extendedbigdecimal extendedbigint +//! A type to represent the possible start, increment, and end values for seq. +//! +//! The [`Number`] enumeration represents the possible values for the +//! start, increment, and end values for `seq`. These may be integers, +//! floating point numbers, negative zero, etc. A [`Number`] can be +//! parsed from a string by calling [`parse`]. +use num_traits::Zero; + +use crate::extendedbigdecimal::ExtendedBigDecimal; +use crate::extendedbigint::ExtendedBigInt; + +/// An integral or floating point number. +#[derive(Debug, PartialEq)] +pub enum Number { + Int(ExtendedBigInt), + Float(ExtendedBigDecimal), +} + +impl Number { + /// Decide whether this number is zero (either positive or negative). + pub fn is_zero(&self) -> bool { + // We would like to implement `num_traits::Zero`, but it + // requires an addition implementation, and we don't want to + // implement that here. + match self { + Number::Int(n) => n.is_zero(), + Number::Float(x) => x.is_zero(), + } + } + + /// Convert this number into an `ExtendedBigDecimal`. + pub fn into_extended_big_decimal(self) -> ExtendedBigDecimal { + match self { + Number::Int(n) => ExtendedBigDecimal::from(n), + Number::Float(x) => x, + } + } + + /// The integer number one. + pub fn one() -> Self { + // We would like to implement `num_traits::One`, but it requires + // a multiplication implementation, and we don't want to + // implement that here. + Number::Int(ExtendedBigInt::one()) + } + + /// Round this number towards the given other number. + /// + /// If `other` is greater, then round up. If `other` is smaller, + /// then round down. + pub fn round_towards(self, other: &ExtendedBigInt) -> ExtendedBigInt { + match self { + // If this number is already an integer, it is already + // rounded to the nearest integer in the direction of + // `other`. + Number::Int(num) => num, + // Otherwise, if this number is a float, we need to decide + // whether `other` is larger or smaller than it, and thus + // whether to round up or round down, respectively. + Number::Float(num) => { + let other: ExtendedBigDecimal = From::from(other.clone()); + if other > num { + num.ceil() + } else { + // If they are equal, then `self` is already an + // integer, so calling `floor()` does no harm and + // will just return that integer anyway. + num.floor() + } + } + } + } +} + +/// A number with a specified number of integer and fractional digits. +/// +/// This struct can be used to represent a number along with information +/// on how many significant digits to use when displaying the number. +/// The [`num_integral_digits`] field also includes the width needed to +/// display the "-" character for a negative number. +/// +/// You can get an instance of this struct by calling [`str::parse`]. +#[derive(Debug)] +pub struct PreciseNumber { + pub number: Number, + pub num_integral_digits: usize, + pub num_fractional_digits: usize, +} + +impl PreciseNumber { + pub fn new( + number: Number, + num_integral_digits: usize, + num_fractional_digits: usize, + ) -> PreciseNumber { + PreciseNumber { + number, + num_integral_digits, + num_fractional_digits, + } + } + + /// The integer number one. + pub fn one() -> Self { + // We would like to implement `num_traits::One`, but it requires + // a multiplication implementation, and we don't want to + // implement that here. + PreciseNumber::new(Number::one(), 1, 0) + } + + /// Decide whether this number is zero (either positive or negative). + pub fn is_zero(&self) -> bool { + // We would like to implement `num_traits::Zero`, but it + // requires an addition implementation, and we don't want to + // implement that here. + self.number.is_zero() + } +} diff --git a/src/uu/seq/src/numberparse.rs b/src/uu/seq/src/numberparse.rs new file mode 100644 index 000000000..da235790d --- /dev/null +++ b/src/uu/seq/src/numberparse.rs @@ -0,0 +1,589 @@ +// spell-checker:ignore extendedbigdecimal extendedbigint bigdecimal numberparse +//! Parsing numbers for use in `seq`. +//! +//! This module provides an implementation of [`FromStr`] for the +//! [`PreciseNumber`] struct. +use std::convert::TryInto; +use std::str::FromStr; + +use bigdecimal::BigDecimal; +use num_bigint::BigInt; +use num_bigint::Sign; +use num_traits::Num; +use num_traits::Zero; + +use crate::extendedbigdecimal::ExtendedBigDecimal; +use crate::extendedbigint::ExtendedBigInt; +use crate::number::Number; +use crate::number::PreciseNumber; + +/// An error returned when parsing a number fails. +#[derive(Debug, PartialEq)] +pub enum ParseNumberError { + Float, + Nan, + Hex, +} + +/// Decide whether a given string and its parsed `BigInt` is negative zero. +fn is_minus_zero_int(s: &str, n: &BigInt) -> bool { + s.starts_with('-') && n == &BigInt::zero() +} + +/// Decide whether a given string and its parsed `BigDecimal` is negative zero. +fn is_minus_zero_float(s: &str, x: &BigDecimal) -> bool { + s.starts_with('-') && x == &BigDecimal::zero() +} + +/// Parse a number with neither a decimal point nor an exponent. +/// +/// # Errors +/// +/// This function returns an error if the input string is a variant of +/// "NaN" or if no [`BigInt`] could be parsed from the string. +/// +/// # Examples +/// +/// ```rust,ignore +/// let actual = "0".parse::().unwrap().number; +/// let expected = Number::BigInt(BigInt::zero()); +/// assert_eq!(actual, expected); +/// ``` +fn parse_no_decimal_no_exponent(s: &str) -> Result { + match s.parse::() { + Ok(n) => { + // If `s` is '-0', then `parse()` returns `BigInt::zero()`, + // but we need to return `Number::MinusZeroInt` instead. + if is_minus_zero_int(s, &n) { + Ok(PreciseNumber::new( + Number::Int(ExtendedBigInt::MinusZero), + s.len(), + 0, + )) + } else { + Ok(PreciseNumber::new( + Number::Int(ExtendedBigInt::BigInt(n)), + s.len(), + 0, + )) + } + } + Err(_) => { + // Possibly "NaN" or "inf". + // + // TODO In Rust v1.53.0, this change + // https://github.com/rust-lang/rust/pull/78618 improves the + // parsing of floats to include being able to parse "NaN" + // and "inf". So when the minimum version of this crate is + // increased to 1.53.0, we should just use the built-in + // `f32` parsing instead. + if s.eq_ignore_ascii_case("inf") { + Ok(PreciseNumber::new( + Number::Float(ExtendedBigDecimal::Infinity), + 0, + 0, + )) + } else if s.eq_ignore_ascii_case("-inf") { + Ok(PreciseNumber::new( + Number::Float(ExtendedBigDecimal::MinusInfinity), + 0, + 0, + )) + } else if s.eq_ignore_ascii_case("nan") || s.eq_ignore_ascii_case("-nan") { + Err(ParseNumberError::Nan) + } else { + Err(ParseNumberError::Float) + } + } + } +} + +/// Parse a number with an exponent but no decimal point. +/// +/// # Errors +/// +/// This function returns an error if `s` is not a valid number. +/// +/// # Examples +/// +/// ```rust,ignore +/// let actual = "1e2".parse::().unwrap().number; +/// let expected = "100".parse::().unwrap(); +/// assert_eq!(actual, expected); +/// ``` +fn parse_exponent_no_decimal(s: &str, j: usize) -> Result { + let exponent: i64 = s[j + 1..].parse().map_err(|_| ParseNumberError::Float)?; + // If the exponent is strictly less than zero, then the number + // should be treated as a floating point number that will be + // displayed in decimal notation. For example, "1e-2" will be + // displayed as "0.01", but "1e2" will be displayed as "100", + // without a decimal point. + let x: BigDecimal = s.parse().map_err(|_| ParseNumberError::Float)?; + let num_integral_digits = if is_minus_zero_float(s, &x) { + 2 + } else { + let total = j as i64 + exponent; + let result = if total < 1 { + 1 + } else { + total.try_into().unwrap() + }; + if x.sign() == Sign::Minus { + result + 1 + } else { + result + } + }; + let num_fractional_digits = if exponent < 0 { -exponent as usize } else { 0 }; + + if exponent < 0 { + if is_minus_zero_float(s, &x) { + Ok(PreciseNumber::new( + Number::Float(ExtendedBigDecimal::MinusZero), + num_integral_digits, + num_fractional_digits, + )) + } else { + Ok(PreciseNumber::new( + Number::Float(ExtendedBigDecimal::BigDecimal(x)), + num_integral_digits, + num_fractional_digits, + )) + } + } else { + let zeros = "0".repeat(exponent.try_into().unwrap()); + let expanded = [&s[0..j], &zeros].concat(); + parse_no_decimal_no_exponent(&expanded) + } +} + +/// Parse a number with a decimal point but no exponent. +/// +/// # Errors +/// +/// This function returns an error if `s` is not a valid number. +/// +/// # Examples +/// +/// ```rust,ignore +/// let actual = "1.2".parse::().unwrap().number; +/// let expected = "1.2".parse::().unwrap(); +/// assert_eq!(actual, expected); +/// ``` +fn parse_decimal_no_exponent(s: &str, i: usize) -> Result { + let x: BigDecimal = s.parse().map_err(|_| ParseNumberError::Float)?; + let num_integral_digits = i; + let num_fractional_digits = s.len() - (i + 1); + if is_minus_zero_float(s, &x) { + Ok(PreciseNumber::new( + Number::Float(ExtendedBigDecimal::MinusZero), + num_integral_digits, + num_fractional_digits, + )) + } else { + Ok(PreciseNumber::new( + Number::Float(ExtendedBigDecimal::BigDecimal(x)), + num_integral_digits, + num_fractional_digits, + )) + } +} + +/// Parse a number with both a decimal point and an exponent. +/// +/// # Errors +/// +/// This function returns an error if `s` is not a valid number. +/// +/// # Examples +/// +/// ```rust,ignore +/// let actual = "1.2e3".parse::().unwrap().number; +/// let expected = "1200".parse::().unwrap(); +/// assert_eq!(actual, expected); +/// ``` +fn parse_decimal_and_exponent( + s: &str, + i: usize, + j: usize, +) -> Result { + // Because of the match guard, this subtraction will not underflow. + let num_digits_between_decimal_point_and_e = (j - (i + 1)) as i64; + let exponent: i64 = s[j + 1..].parse().map_err(|_| ParseNumberError::Float)?; + let val: BigDecimal = s.parse().map_err(|_| ParseNumberError::Float)?; + + let num_integral_digits = { + let minimum: usize = { + let integral_part: f64 = s[..j].parse().map_err(|_| ParseNumberError::Float)?; + if integral_part == -0.0 && integral_part.is_sign_negative() { + 2 + } else { + 1 + } + }; + + let total = i as i64 + exponent; + if total < minimum as i64 { + minimum + } else { + total.try_into().unwrap() + } + }; + let num_fractional_digits = if num_digits_between_decimal_point_and_e < exponent { + 0 + } else { + (num_digits_between_decimal_point_and_e - exponent) + .try_into() + .unwrap() + }; + + if num_digits_between_decimal_point_and_e <= exponent { + if is_minus_zero_float(s, &val) { + Ok(PreciseNumber::new( + Number::Int(ExtendedBigInt::MinusZero), + num_integral_digits, + num_fractional_digits, + )) + } else { + let zeros: String = "0".repeat( + (exponent - num_digits_between_decimal_point_and_e) + .try_into() + .unwrap(), + ); + let expanded = [&s[0..i], &s[i + 1..j], &zeros].concat(); + let n = expanded + .parse::() + .map_err(|_| ParseNumberError::Float)?; + Ok(PreciseNumber::new( + Number::Int(ExtendedBigInt::BigInt(n)), + num_integral_digits, + num_fractional_digits, + )) + } + } else if is_minus_zero_float(s, &val) { + Ok(PreciseNumber::new( + Number::Float(ExtendedBigDecimal::MinusZero), + num_integral_digits, + num_fractional_digits, + )) + } else { + Ok(PreciseNumber::new( + Number::Float(ExtendedBigDecimal::BigDecimal(val)), + num_integral_digits, + num_fractional_digits, + )) + } +} + +/// Parse a hexadecimal integer from a string. +/// +/// # Errors +/// +/// This function returns an error if no [`BigInt`] could be parsed from +/// the string. +/// +/// # Examples +/// +/// ```rust,ignore +/// let actual = "0x0".parse::().unwrap().number; +/// let expected = Number::BigInt(BigInt::zero()); +/// assert_eq!(actual, expected); +/// ``` +fn parse_hexadecimal(s: &str) -> Result { + let (is_neg, s) = if s.starts_with('-') { + (true, &s[3..]) + } else { + (false, &s[2..]) + }; + + if s.starts_with('-') || s.starts_with('+') { + // Even though this is more like an invalid hexadecimal number, + // GNU reports this as an invalid floating point number, so we + // use `ParseNumberError::Float` to match that behavior. + return Err(ParseNumberError::Float); + } + + let num = BigInt::from_str_radix(s, 16).map_err(|_| ParseNumberError::Hex)?; + + match (is_neg, num == BigInt::zero()) { + (true, true) => Ok(PreciseNumber::new( + Number::Int(ExtendedBigInt::MinusZero), + 2, + 0, + )), + (true, false) => Ok(PreciseNumber::new( + Number::Int(ExtendedBigInt::BigInt(-num)), + 0, + 0, + )), + (false, _) => Ok(PreciseNumber::new( + Number::Int(ExtendedBigInt::BigInt(num)), + 0, + 0, + )), + } +} + +impl FromStr for PreciseNumber { + type Err = ParseNumberError; + fn from_str(mut s: &str) -> Result { + // Trim leading whitespace. + s = s.trim_start(); + + // Trim a single leading "+" character. + if s.starts_with('+') { + s = &s[1..]; + } + + // Check if the string seems to be in hexadecimal format. + // + // May be 0x123 or -0x123, so the index `i` may be either 0 or 1. + if let Some(i) = s.to_lowercase().find("0x") { + if i <= 1 { + return parse_hexadecimal(s); + } + } + + // Find the decimal point and the exponent symbol. Parse the + // number differently depending on its form. This is important + // because the form of the input dictates how the output will be + // presented. + match (s.find('.'), s.find('e')) { + // For example, "123456" or "inf". + (None, None) => parse_no_decimal_no_exponent(s), + // For example, "123e456" or "1e-2". + (None, Some(j)) => parse_exponent_no_decimal(s, j), + // For example, "123.456". + (Some(i), None) => parse_decimal_no_exponent(s, i), + // For example, "123.456e789". + (Some(i), Some(j)) if i < j => parse_decimal_and_exponent(s, i, j), + // For example, "1e2.3" or "1.2.3". + _ => Err(ParseNumberError::Float), + } + } +} + +#[cfg(test)] +mod tests { + + use bigdecimal::BigDecimal; + use num_bigint::BigInt; + use num_traits::Zero; + + use crate::extendedbigdecimal::ExtendedBigDecimal; + use crate::extendedbigint::ExtendedBigInt; + use crate::number::Number; + use crate::number::PreciseNumber; + use crate::numberparse::ParseNumberError; + + /// Convenience function for parsing a [`Number`] and unwrapping. + fn parse(s: &str) -> Number { + s.parse::().unwrap().number + } + + /// Convenience function for getting the number of integral digits. + fn num_integral_digits(s: &str) -> usize { + s.parse::().unwrap().num_integral_digits + } + + /// Convenience function for getting the number of fractional digits. + fn num_fractional_digits(s: &str) -> usize { + s.parse::().unwrap().num_fractional_digits + } + + #[test] + fn test_parse_minus_zero_int() { + assert_eq!(parse("-0e0"), Number::Int(ExtendedBigInt::MinusZero)); + assert_eq!(parse("-0e-0"), Number::Int(ExtendedBigInt::MinusZero)); + assert_eq!(parse("-0e1"), Number::Int(ExtendedBigInt::MinusZero)); + assert_eq!(parse("-0e+1"), Number::Int(ExtendedBigInt::MinusZero)); + assert_eq!(parse("-0.0e1"), Number::Int(ExtendedBigInt::MinusZero)); + assert_eq!(parse("-0x0"), Number::Int(ExtendedBigInt::MinusZero)); + } + + #[test] + fn test_parse_minus_zero_float() { + assert_eq!(parse("-0.0"), Number::Float(ExtendedBigDecimal::MinusZero)); + assert_eq!(parse("-0e-1"), Number::Float(ExtendedBigDecimal::MinusZero)); + assert_eq!( + parse("-0.0e-1"), + Number::Float(ExtendedBigDecimal::MinusZero) + ); + } + + #[test] + fn test_parse_big_int() { + assert_eq!(parse("0"), Number::Int(ExtendedBigInt::zero())); + assert_eq!(parse("0.1e1"), Number::Int(ExtendedBigInt::one())); + assert_eq!( + parse("1.0e1"), + Number::Int(ExtendedBigInt::BigInt("10".parse::().unwrap())) + ); + } + + #[test] + fn test_parse_hexadecimal_big_int() { + assert_eq!(parse("0x0"), Number::Int(ExtendedBigInt::zero())); + assert_eq!( + parse("0x10"), + Number::Int(ExtendedBigInt::BigInt("16".parse::().unwrap())) + ); + } + + #[test] + fn test_parse_big_decimal() { + assert_eq!( + parse("0.0"), + Number::Float(ExtendedBigDecimal::BigDecimal( + "0.0".parse::().unwrap() + )) + ); + assert_eq!( + parse(".0"), + Number::Float(ExtendedBigDecimal::BigDecimal( + "0.0".parse::().unwrap() + )) + ); + assert_eq!( + parse("1.0"), + Number::Float(ExtendedBigDecimal::BigDecimal( + "1.0".parse::().unwrap() + )) + ); + assert_eq!( + parse("10e-1"), + Number::Float(ExtendedBigDecimal::BigDecimal( + "1.0".parse::().unwrap() + )) + ); + assert_eq!( + parse("-1e-3"), + Number::Float(ExtendedBigDecimal::BigDecimal( + "-0.001".parse::().unwrap() + )) + ); + } + + #[test] + fn test_parse_inf() { + assert_eq!(parse("inf"), Number::Float(ExtendedBigDecimal::Infinity)); + assert_eq!(parse("+inf"), Number::Float(ExtendedBigDecimal::Infinity)); + assert_eq!( + parse("-inf"), + Number::Float(ExtendedBigDecimal::MinusInfinity) + ); + } + + #[test] + fn test_parse_invalid_float() { + assert_eq!( + "1.2.3".parse::().unwrap_err(), + ParseNumberError::Float + ); + assert_eq!( + "1e2e3".parse::().unwrap_err(), + ParseNumberError::Float + ); + assert_eq!( + "1e2.3".parse::().unwrap_err(), + ParseNumberError::Float + ); + assert_eq!( + "-+-1".parse::().unwrap_err(), + ParseNumberError::Float + ); + } + + #[test] + fn test_parse_invalid_hex() { + assert_eq!( + "0xg".parse::().unwrap_err(), + ParseNumberError::Hex + ); + } + + #[test] + fn test_parse_invalid_nan() { + assert_eq!( + "nan".parse::().unwrap_err(), + ParseNumberError::Nan + ); + assert_eq!( + "NAN".parse::().unwrap_err(), + ParseNumberError::Nan + ); + assert_eq!( + "NaN".parse::().unwrap_err(), + ParseNumberError::Nan + ); + assert_eq!( + "nAn".parse::().unwrap_err(), + ParseNumberError::Nan + ); + assert_eq!( + "-nan".parse::().unwrap_err(), + ParseNumberError::Nan + ); + } + + #[test] + fn test_num_integral_digits() { + // no decimal, no exponent + assert_eq!(num_integral_digits("123"), 3); + // decimal, no exponent + assert_eq!(num_integral_digits("123.45"), 3); + // exponent, no decimal + assert_eq!(num_integral_digits("123e4"), 3 + 4); + assert_eq!(num_integral_digits("123e-4"), 1); + assert_eq!(num_integral_digits("-1e-3"), 2); + // decimal and exponent + assert_eq!(num_integral_digits("123.45e6"), 3 + 6); + assert_eq!(num_integral_digits("123.45e-6"), 1); + assert_eq!(num_integral_digits("123.45e-1"), 2); + // minus zero int + assert_eq!(num_integral_digits("-0e0"), 2); + assert_eq!(num_integral_digits("-0e-0"), 2); + assert_eq!(num_integral_digits("-0e1"), 3); + assert_eq!(num_integral_digits("-0e+1"), 3); + assert_eq!(num_integral_digits("-0.0e1"), 3); + // minus zero float + assert_eq!(num_integral_digits("-0.0"), 2); + assert_eq!(num_integral_digits("-0e-1"), 2); + assert_eq!(num_integral_digits("-0.0e-1"), 2); + + // TODO In GNU `seq`, the `-w` option does not seem to work with + // hexadecimal arguments. In order to match that behavior, we + // report the number of integral digits as zero for hexadecimal + // inputs. + assert_eq!(num_integral_digits("0xff"), 0); + } + + #[test] + fn test_num_fractional_digits() { + // no decimal, no exponent + assert_eq!(num_fractional_digits("123"), 0); + assert_eq!(num_fractional_digits("0xff"), 0); + // decimal, no exponent + assert_eq!(num_fractional_digits("123.45"), 2); + // exponent, no decimal + assert_eq!(num_fractional_digits("123e4"), 0); + assert_eq!(num_fractional_digits("123e-4"), 4); + assert_eq!(num_fractional_digits("123e-1"), 1); + assert_eq!(num_fractional_digits("-1e-3"), 3); + // decimal and exponent + assert_eq!(num_fractional_digits("123.45e6"), 0); + assert_eq!(num_fractional_digits("123.45e1"), 1); + assert_eq!(num_fractional_digits("123.45e-6"), 8); + assert_eq!(num_fractional_digits("123.45e-1"), 3); + // minus zero int + assert_eq!(num_fractional_digits("-0e0"), 0); + assert_eq!(num_fractional_digits("-0e-0"), 0); + assert_eq!(num_fractional_digits("-0e1"), 0); + assert_eq!(num_fractional_digits("-0e+1"), 0); + assert_eq!(num_fractional_digits("-0.0e1"), 0); + // minus zero float + assert_eq!(num_fractional_digits("-0.0"), 1); + assert_eq!(num_fractional_digits("-0e-1"), 1); + assert_eq!(num_fractional_digits("-0.0e-1"), 2); + } +} diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index a76a23c4e..f28b4d6e8 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -1,23 +1,24 @@ // TODO: Make -w flag work with decimals // TODO: Support -f flag -// spell-checker:ignore (ToDO) istr chiter argptr ilen +// spell-checker:ignore (ToDO) istr chiter argptr ilen extendedbigdecimal extendedbigint numberparse #[macro_use] extern crate uucore; use clap::{crate_version, App, AppSettings, Arg}; -use num_bigint::BigInt; -use num_traits::One; use num_traits::Zero; -use num_traits::{Num, ToPrimitive}; -use std::cmp; use std::io::{stdout, ErrorKind, Write}; -use std::str::FromStr; -mod digits; -use crate::digits::num_fractional_digits; -use crate::digits::num_integral_digits; +mod extendedbigdecimal; +mod extendedbigint; +mod number; +mod numberparse; +use crate::extendedbigdecimal::ExtendedBigDecimal; +use crate::extendedbigint::ExtendedBigInt; +use crate::number::Number; +use crate::number::PreciseNumber; +use crate::numberparse::ParseNumberError; use uucore::display::Quotable; @@ -43,124 +44,55 @@ struct SeqOptions { widths: bool, } -enum Number { - /// Negative zero, as if it were an integer. - MinusZero, - BigInt(BigInt), - F64(f64), -} - -impl Number { - fn is_zero(&self) -> bool { - match self { - Number::MinusZero => true, - Number::BigInt(n) => n.is_zero(), - Number::F64(n) => n.is_zero(), - } - } - - fn into_f64(self) -> f64 { - match self { - Number::MinusZero => -0., - // BigInt::to_f64() can not return None. - Number::BigInt(n) => n.to_f64().unwrap(), - Number::F64(n) => n, - } - } - - /// Convert this number into a bigint, consuming it. - /// - /// For floats, this returns the [`BigInt`] corresponding to the - /// floor of the number. - fn into_bigint(self) -> BigInt { - match self { - Number::MinusZero => BigInt::zero(), - Number::F64(x) => BigInt::from(x.floor() as i64), - Number::BigInt(n) => n, - } - } -} - -impl FromStr for Number { - type Err = String; - fn from_str(mut s: &str) -> Result { - s = s.trim_start(); - if s.starts_with('+') { - s = &s[1..]; - } - let is_neg = s.starts_with('-'); - - match s.to_lowercase().find("0x") { - Some(i) if i <= 1 => match &s.as_bytes()[i + 2] { - b'-' | b'+' => Err(format!( - "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", - s.quote(), - uucore::execution_phrase(), - )), - // TODO: hexadecimal floating point parsing (see #2660) - b'.' => Err(format!( - "NotImplemented: hexadecimal floating point numbers: {}\nTry '{} --help' for more information.", - s.quote(), - uucore::execution_phrase(), - )), - _ => { - let num = BigInt::from_str_radix(&s[i + 2..], 16) - .map_err(|_| format!( - "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", - s.quote(), - uucore::execution_phrase(), - ))?; - match (is_neg, num == BigInt::zero()) { - (true, true) => Ok(Number::MinusZero), - (true, false) => Ok(Number::BigInt(-num)), - (false, _) => Ok(Number::BigInt(num)), - } - } - }, - Some(_) => Err(format!( - "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", - s.quote(), - uucore::execution_phrase(), - )), - - None => match s.parse::() { - Ok(n) => { - // If `s` is '-0', then `parse()` returns - // `BigInt::zero()`, but we need to return - // `Number::MinusZero` instead. - if n == BigInt::zero() && is_neg { - Ok(Number::MinusZero) - } else { - Ok(Number::BigInt(n)) - } - } - Err(_) => match s.parse::() { - Ok(value) if value.is_nan() => Err(format!( - "invalid 'not-a-number' argument: {}\nTry '{} --help' for more information.", - s.quote(), - uucore::execution_phrase(), - )), - Ok(value) => Ok(Number::F64(value)), - Err(_) => Err(format!( - "invalid floating point argument: {}\nTry '{} --help' for more information.", - s.quote(), - uucore::execution_phrase(), - )), - }, - }, - } - } -} - /// A range of integers. /// /// The elements are (first, increment, last). -type RangeInt = (BigInt, BigInt, BigInt); +type RangeInt = (ExtendedBigInt, ExtendedBigInt, ExtendedBigInt); -/// A range of f64. +/// A range of floats. /// /// The elements are (first, increment, last). -type RangeF64 = (f64, f64, f64); +type RangeFloat = (ExtendedBigDecimal, ExtendedBigDecimal, ExtendedBigDecimal); + +/// Terminate the process with error code 1. +/// +/// Before terminating the process, this function prints an error +/// message that depends on `arg` and `e`. +/// +/// Although the signature of this function states that it returns a +/// [`PreciseNumber`], it never reaches the return statement. It is just +/// there to make it easier to use this function when unwrapping the +/// result of calling [`str::parse`] when attempting to parse a +/// [`PreciseNumber`]. +/// +/// # Examples +/// +/// ```rust,ignore +/// let s = "1.2e-3"; +/// s.parse::.unwrap_or_else(|e| exit_with_error(s, e)) +/// ``` +fn exit_with_error(arg: &str, e: ParseNumberError) -> ! { + match e { + ParseNumberError::Float => crash!( + 1, + "invalid floating point argument: {}\nTry '{} --help' for more information.", + arg.quote(), + uucore::execution_phrase() + ), + ParseNumberError::Nan => crash!( + 1, + "invalid 'not-a-number' argument: {}\nTry '{} --help' for more information.", + arg.quote(), + uucore::execution_phrase() + ), + ParseNumberError::Hex => crash!( + 1, + "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", + arg.quote(), + uucore::execution_phrase() + ), + } +} pub fn uumain(args: impl uucore::Args) -> i32 { let usage = usage(); @@ -174,53 +106,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 { widths: matches.is_present(OPT_WIDTHS), }; - let mut largest_dec = 0; - let mut padding = 0; let first = if numbers.len() > 1 { let slice = numbers[0]; - largest_dec = num_fractional_digits(slice).unwrap_or_else(|_| { - crash!( - 1, - "invalid floating point argument: {}\n Try '{} --help' for more information.", - slice.quote(), - uucore::execution_phrase() - ) - }); - padding = num_integral_digits(slice).unwrap_or_else(|_| { - crash!( - 1, - "invalid floating point argument: {}\n Try '{} --help' for more information.", - slice.quote(), - uucore::execution_phrase() - ) - }); - crash_if_err!(1, slice.parse()) + slice.parse().unwrap_or_else(|e| exit_with_error(slice, e)) } else { - Number::BigInt(BigInt::one()) + PreciseNumber::one() }; let increment = if numbers.len() > 2 { let slice = numbers[1]; - let dec = num_fractional_digits(slice).unwrap_or_else(|_| { - crash!( - 1, - "invalid floating point argument: {}\n Try '{} --help' for more information.", - slice.quote(), - uucore::execution_phrase() - ) - }); - let int_digits = num_integral_digits(slice).unwrap_or_else(|_| { - crash!( - 1, - "invalid floating point argument: {}\n Try '{} --help' for more information.", - slice.quote(), - uucore::execution_phrase() - ) - }); - largest_dec = cmp::max(largest_dec, dec); - padding = cmp::max(padding, int_digits); - crash_if_err!(1, slice.parse()) + slice.parse().unwrap_or_else(|e| exit_with_error(slice, e)) } else { - Number::BigInt(BigInt::one()) + PreciseNumber::one() }; if increment.is_zero() { show_error!( @@ -230,54 +126,36 @@ pub fn uumain(args: impl uucore::Args) -> i32 { ); return 1; } - let last: Number = { + let last: PreciseNumber = { let slice = numbers[numbers.len() - 1]; - let int_digits = num_integral_digits(slice).unwrap_or_else(|_| { - crash!( - 1, - "invalid floating point argument: {}\n Try '{} --help' for more information.", - slice.quote(), - uucore::execution_phrase() - ) - }); - padding = cmp::max(padding, int_digits); - crash_if_err!(1, slice.parse()) + slice.parse().unwrap_or_else(|e| exit_with_error(slice, e)) }; - let is_negative_zero_f64 = |x: f64| x == -0.0 && x.is_sign_negative() && largest_dec == 0; - let result = match (first, last, increment) { - // For example, `seq -0 1 2` or `seq -0 1 2.0`. - (Number::MinusZero, last, Number::BigInt(increment)) => print_seq_integers( - (BigInt::zero(), increment, last.into_bigint()), - options.separator, - options.terminator, - options.widths, - padding, - true, - ), - // For example, `seq -0e0 1 2` or `seq -0e0 1 2.0`. - (Number::F64(x), last, Number::BigInt(increment)) if is_negative_zero_f64(x) => { + let padding = first + .num_integral_digits + .max(increment.num_integral_digits) + .max(last.num_integral_digits); + let largest_dec = first + .num_fractional_digits + .max(increment.num_fractional_digits); + + let result = match (first.number, increment.number, last.number) { + (Number::Int(first), Number::Int(increment), last) => { + let last = last.round_towards(&first); print_seq_integers( - (BigInt::zero(), increment, last.into_bigint()), + (first, increment, last), options.separator, options.terminator, options.widths, padding, - true, ) } - // For example, `seq 0 1 2` or `seq 0 1 2.0`. - (Number::BigInt(first), last, Number::BigInt(increment)) => print_seq_integers( - (first, increment, last.into_bigint()), - options.separator, - options.terminator, - options.widths, - padding, - false, - ), - // For example, `seq 0 0.5 1` or `seq 0.0 0.5 1` or `seq 0.0 0.5 1.0`. - (first, last, increment) => print_seq( - (first.into_f64(), increment.into_f64(), last.into_f64()), + (first, increment, last) => print_seq( + ( + first.into_extended_big_decimal(), + increment.into_extended_big_decimal(), + last.into_extended_big_decimal(), + ), largest_dec, options.separator, options.terminator, @@ -329,7 +207,7 @@ pub fn uu_app() -> App<'static, 'static> { ) } -fn done_printing(next: &T, increment: &T, last: &T) -> bool { +fn done_printing(next: &T, increment: &T, last: &T) -> bool { if increment >= &T::zero() { next > last } else { @@ -337,9 +215,65 @@ fn done_printing(next: &T, increment: &T, last: &T) -> bool } } +/// Write a big decimal formatted according to the given parameters. +/// +/// This method is an adapter to support displaying negative zero on +/// Rust versions earlier than 1.53.0. After that version, we should be +/// able to display negative zero using the default formatting provided +/// by `-0.0f32`, for example. +fn write_value_float( + writer: &mut impl Write, + value: &ExtendedBigDecimal, + width: usize, + precision: usize, + is_first_iteration: bool, +) -> std::io::Result<()> { + let value_as_str = if *value == ExtendedBigDecimal::MinusZero && is_first_iteration { + format!( + "-{value:>0width$.precision$}", + value = value, + width = if width > 0 { width - 1 } else { width }, + precision = precision, + ) + } else { + format!( + "{value:>0width$.precision$}", + value = value, + width = width, + precision = precision, + ) + }; + write!(writer, "{}", value_as_str) +} + +/// Write a big int formatted according to the given parameters. +fn write_value_int( + writer: &mut impl Write, + value: &ExtendedBigInt, + width: usize, + pad: bool, + is_first_iteration: bool, +) -> std::io::Result<()> { + let value_as_str = if pad { + if *value == ExtendedBigInt::MinusZero && is_first_iteration { + format!("-{value:>0width$}", value = value, width = width - 1,) + } else { + format!("{value:>0width$}", value = value, width = width,) + } + } else if *value == ExtendedBigInt::MinusZero && is_first_iteration { + format!("-{}", value) + } else { + format!("{}", value) + }; + write!(writer, "{}", value_as_str) +} + +// TODO `print_seq()` and `print_seq_integers()` are nearly identical, +// they could be refactored into a single more general function. + /// Floating point based code path fn print_seq( - range: RangeF64, + range: RangeFloat, largest_dec: usize, separator: String, terminator: String, @@ -349,30 +283,23 @@ fn print_seq( let stdout = stdout(); let mut stdout = stdout.lock(); let (first, increment, last) = range; - let mut i = 0isize; - let is_first_minus_zero = first == -0.0 && first.is_sign_negative(); - let mut value = first + i as f64 * increment; + let mut value = first; let padding = if pad { padding + 1 + largest_dec } else { 0 }; let mut is_first_iteration = true; while !done_printing(&value, &increment, &last) { if !is_first_iteration { write!(stdout, "{}", separator)?; } - let mut width = padding; - if is_first_iteration && is_first_minus_zero { - write!(stdout, "-")?; - width -= 1; - } - is_first_iteration = false; - write!( - stdout, - "{value:>0width$.precision$}", - value = value, - width = width, - precision = largest_dec, + write_value_float( + &mut stdout, + &value, + padding, + largest_dec, + is_first_iteration, )?; - i += 1; - value = first + i as f64 * increment; + // TODO Implement augmenting addition. + value = value + increment.clone(); + is_first_iteration = false; } if !is_first_iteration { write!(stdout, "{}", terminator)?; @@ -401,7 +328,6 @@ fn print_seq_integers( terminator: String, pad: bool, padding: usize, - is_first_minus_zero: bool, ) -> std::io::Result<()> { let stdout = stdout(); let mut stdout = stdout.lock(); @@ -412,18 +338,10 @@ fn print_seq_integers( if !is_first_iteration { write!(stdout, "{}", separator)?; } - let mut width = padding; - if is_first_iteration && is_first_minus_zero { - write!(stdout, "-")?; - width -= 1; - } + write_value_int(&mut stdout, &value, padding, pad, is_first_iteration)?; + // TODO Implement augmenting addition. + value = value + increment.clone(); is_first_iteration = false; - if pad { - write!(stdout, "{number:>0width$}", number = value, width = width)?; - } else { - write!(stdout, "{}", value)?; - } - value += &increment; } if !is_first_iteration { diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 6ed3cb67d..2a2e31f83 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -7,25 +7,25 @@ fn test_hex_rejects_sign_after_identifier() { .args(&["0x-123ABC"]) .fails() .no_stdout() - .stderr_contains("invalid hexadecimal argument: '0x-123ABC'") + .stderr_contains("invalid floating point argument: '0x-123ABC'") .stderr_contains("for more information."); new_ucmd!() .args(&["0x+123ABC"]) .fails() .no_stdout() - .stderr_contains("invalid hexadecimal argument: '0x+123ABC'") + .stderr_contains("invalid floating point argument: '0x+123ABC'") .stderr_contains("for more information."); new_ucmd!() .args(&["-0x-123ABC"]) .fails() .no_stdout() - .stderr_contains("invalid hexadecimal argument: '-0x-123ABC'") + .stderr_contains("invalid floating point argument: '-0x-123ABC'") .stderr_contains("for more information."); new_ucmd!() .args(&["-0x+123ABC"]) .fails() .no_stdout() - .stderr_contains("invalid hexadecimal argument: '-0x+123ABC'") + .stderr_contains("invalid floating point argument: '-0x+123ABC'") .stderr_contains("for more information."); } @@ -60,7 +60,7 @@ fn test_hex_identifier_in_wrong_place() { .args(&["1234ABCD0x"]) .fails() .no_stdout() - .stderr_contains("invalid hexadecimal argument: '1234ABCD0x'") + .stderr_contains("invalid floating point argument: '1234ABCD0x'") .stderr_contains("for more information."); } @@ -479,6 +479,15 @@ fn test_width_decimal_scientific_notation_trailing_zeros_increment() { .no_stderr(); } +#[test] +fn test_width_negative_scientific_notation() { + new_ucmd!() + .args(&["-w", "-1e-3", "1"]) + .succeeds() + .stdout_is("-0.001\n00.999\n") + .no_stderr(); +} + /// Test that trailing zeros in the end argument do not contribute to width. #[test] fn test_width_decimal_scientific_notation_trailing_zeros_end() { @@ -544,3 +553,63 @@ fn test_trailing_whitespace_error() { // --help' for more information." .stderr_contains("for more information."); } + +#[test] +fn test_negative_zero_int_start_float_increment() { + new_ucmd!() + .args(&["-0", "0.1", "0.1"]) + .succeeds() + .stdout_is("-0.0\n0.1\n") + .no_stderr(); +} + +#[test] +fn test_float_precision_increment() { + new_ucmd!() + .args(&["999", "0.1", "1000.1"]) + .succeeds() + .stdout_is( + "999.0 +999.1 +999.2 +999.3 +999.4 +999.5 +999.6 +999.7 +999.8 +999.9 +1000.0 +1000.1 +", + ) + .no_stderr(); +} + +/// Test for floating point precision issues. +#[test] +fn test_negative_increment_decimal() { + new_ucmd!() + .args(&["0.1", "-0.1", "-0.2"]) + .succeeds() + .stdout_is("0.1\n0.0\n-0.1\n-0.2\n") + .no_stderr(); +} + +#[test] +fn test_zero_not_first() { + new_ucmd!() + .args(&["-w", "-0.1", "0.1", "0.1"]) + .succeeds() + .stdout_is("-0.1\n00.0\n00.1\n") + .no_stderr(); +} + +#[test] +fn test_rounding_end() { + new_ucmd!() + .args(&["1", "-1", "0.1"]) + .succeeds() + .stdout_is("1\n") + .no_stderr(); +} From 124f92984809a740e2a5da362f6c0c727e71b05b Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 2 Nov 2021 20:06:59 -0300 Subject: [PATCH 093/131] tests/env: change Windows test_change_directory Invoke `cmd.exe /C cd` to determine the current working directory instead of relying on the output of environment variables. --- tests/by-util/test_env.rs | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/tests/by-util/test_env.rs b/tests/by-util/test_env.rs index 0d9d94b01..135ee72ef 100644 --- a/tests/by-util/test_env.rs +++ b/tests/by-util/test_env.rs @@ -1,7 +1,4 @@ -// spell-checker:ignore (words) bamf chdir rlimit prlimit - -#[cfg(not(windows))] -use std::fs; +// spell-checker:ignore (words) bamf chdir rlimit prlimit COMSPEC use crate::common::util::*; use std::env; @@ -177,7 +174,7 @@ fn test_fail_null_with_program() { fn test_change_directory() { let scene = TestScenario::new(util_name!()); let temporary_directory = tempdir().unwrap(); - let temporary_path = fs::canonicalize(temporary_directory.path()).unwrap(); + let temporary_path = std::fs::canonicalize(temporary_directory.path()).unwrap(); assert_ne!(env::current_dir().unwrap(), temporary_path); // command to print out current working directory @@ -193,27 +190,36 @@ fn test_change_directory() { assert_eq!(out.trim(), temporary_path.as_os_str()) } -// no way to consistently get "current working directory", `cd` doesn't work @ CI -// instead, we test that the unique temporary directory appears somewhere in the printed variables #[cfg(windows)] #[test] fn test_change_directory() { let scene = TestScenario::new(util_name!()); let temporary_directory = tempdir().unwrap(); - let temporary_path = temporary_directory.path(); - assert_ne!(env::current_dir().unwrap(), temporary_path); + let temporary_path = temporary_directory.path(); + let temporary_path = temporary_path + .strip_prefix(r"\\?\") + .unwrap_or(temporary_path); + + let env_cd = env::current_dir().unwrap(); + let env_cd = env_cd.strip_prefix(r"\\?\").unwrap_or(&env_cd); + + assert_ne!(env_cd, temporary_path); + + // COMSPEC is a variable that contains the full path to cmd.exe + let cmd_path = env::var("COMSPEC").unwrap(); + + // command to print out current working directory + let pwd = [&*cmd_path, "/C", "cd"]; let out = scene .ucmd() .arg("--chdir") .arg(&temporary_path) + .args(&pwd) .succeeds() .stdout_move_str(); - - assert!(!out - .lines() - .any(|line| line.ends_with(temporary_path.file_name().unwrap().to_str().unwrap()))); + assert_eq!(out.trim(), temporary_path.as_os_str()) } #[test] From 77e1570ea0624c9b71cd3d1eca1c97f2c5e8d893 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Wed, 13 Oct 2021 17:29:03 +0200 Subject: [PATCH 094/131] Move display::Quotable into its own crate The standalone version has a number of bugfixes compared to the old version. --- Cargo.lock | 14 +- src/uucore/Cargo.toml | 1 + src/uucore/src/lib/mods/display.rs | 492 +---------------------------- 3 files changed, 15 insertions(+), 492 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5bd0e776a..e262c29cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1291,6 +1291,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "os_display" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "748cc1d0dc55247316a5bedd8dc8c5478c8a0c2e2001176b38ce7c0ed732c7a5" +dependencies = [ + "unicode-width", +] + [[package]] name = "ouroboros" version = "0.10.1" @@ -2102,9 +2111,9 @@ checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" @@ -3276,6 +3285,7 @@ dependencies = [ "libc", "nix 0.20.0", "once_cell", + "os_display", "termion", "thiserror", "time", diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 952eecc28..153e3cd57 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -30,6 +30,7 @@ data-encoding-macro = { version="0.1.12", optional=true } z85 = { version="3.0.3", optional=true } libc = { version="0.2.15", optional=true } once_cell = "1.8.0" +os_display = "0.1.0" [target.'cfg(unix)'.dependencies] walkdir = { version="2.3.2", optional=true } diff --git a/src/uucore/src/lib/mods/display.rs b/src/uucore/src/lib/mods/display.rs index dfe64184f..95288973a 100644 --- a/src/uucore/src/lib/mods/display.rs +++ b/src/uucore/src/lib/mods/display.rs @@ -19,378 +19,16 @@ /// println_verbatim(path)?; // Prints "foo/bar.baz" /// # Ok::<(), std::io::Error>(()) /// ``` -// spell-checker:ignore Fbar -use std::borrow::Cow; use std::ffi::OsStr; -#[cfg(any(unix, target_os = "wasi", windows))] -use std::fmt::Write as FmtWrite; -use std::fmt::{self, Display, Formatter}; use std::io::{self, Write as IoWrite}; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; #[cfg(target_os = "wasi")] use std::os::wasi::ffi::OsStrExt; -#[cfg(any(unix, target_os = "wasi"))] -use std::str::from_utf8; -/// An extension trait for displaying filenames to users. -pub trait Quotable { - /// Returns an object that implements [`Display`] for printing filenames with - /// proper quoting and escaping for the platform. - /// - /// On Unix this corresponds to sh/bash syntax, on Windows Powershell syntax - /// is used. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// use uucore::display::Quotable; - /// - /// let path = Path::new("foo/bar.baz"); - /// - /// println!("Found file {}", path.quote()); // Prints "Found file 'foo/bar.baz'" - /// ``` - fn quote(&self) -> Quoted<'_>; - - /// Like `quote()`, but don't actually add quotes unless necessary because of - /// whitespace or special characters. - /// - /// # Examples - /// - /// ``` - /// use std::path::Path; - /// use uucore::display::Quotable; - /// use uucore::show_error; - /// - /// let foo = Path::new("foo/bar.baz"); - /// let bar = Path::new("foo bar"); - /// - /// show_error!("{}: Not found", foo.maybe_quote()); // Prints "util: foo/bar.baz: Not found" - /// show_error!("{}: Not found", bar.maybe_quote()); // Prints "util: 'foo bar': Not found" - /// ``` - fn maybe_quote(&self) -> Quoted<'_> { - let mut quoted = self.quote(); - quoted.force_quote = false; - quoted - } -} - -macro_rules! impl_as_ref { - ($type: ty) => { - impl Quotable for $type { - fn quote(&self) -> Quoted<'_> { - Quoted::new(self.as_ref()) - } - } - }; -} - -impl_as_ref!(str); -impl_as_ref!(&'_ str); -impl_as_ref!(String); -impl_as_ref!(std::path::Path); -impl_as_ref!(&'_ std::path::Path); -impl_as_ref!(std::path::PathBuf); -impl_as_ref!(std::path::Component<'_>); -impl_as_ref!(std::path::Components<'_>); -impl_as_ref!(std::path::Iter<'_>); -impl_as_ref!(std::ffi::OsStr); -impl_as_ref!(&'_ std::ffi::OsStr); -impl_as_ref!(std::ffi::OsString); - -// Cow<'_, str> does not implement AsRef and this is unlikely to be fixed -// for backward compatibility reasons. Otherwise we'd use a blanket impl. -impl Quotable for Cow<'_, str> { - fn quote(&self) -> Quoted<'_> { - let text: &str = self.as_ref(); - Quoted::new(text.as_ref()) - } -} - -impl Quotable for Cow<'_, std::path::Path> { - fn quote(&self) -> Quoted<'_> { - let text: &std::path::Path = self.as_ref(); - Quoted::new(text.as_ref()) - } -} - -/// A wrapper around [`OsStr`] for printing paths with quoting and escaping applied. -#[derive(Debug, Copy, Clone)] -pub struct Quoted<'a> { - text: &'a OsStr, - force_quote: bool, -} - -impl<'a> Quoted<'a> { - fn new(text: &'a OsStr) -> Self { - Quoted { - text, - force_quote: true, - } - } -} - -impl Display for Quoted<'_> { - #[cfg(any(windows, unix, target_os = "wasi"))] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - // On Unix we emulate sh syntax. On Windows Powershell. - // They're just similar enough to share some code. - - /// Characters with special meaning outside quotes. - // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02 - // I don't know why % is in there, and GNU doesn't quote it either. - // {} were used in a version elsewhere but seem unnecessary, GNU doesn't - // quote them. They're used in function definitions but not in a way we - // have to worry about. - #[cfg(any(unix, target_os = "wasi"))] - const SPECIAL_SHELL_CHARS: &[u8] = b"|&;<>()$`\\\"'*?[]="; - // FIXME: I'm not a PowerShell wizard and don't know if this is correct. - // I just copied the Unix version, removed \, and added ,{} based on - // experimentation. - // I have noticed that ~?*[] only get expanded in some contexts, so watch - // out for that if doing your own tests. - // Get-ChildItem seems unwilling to quote anything so it doesn't help. - // There's the additional wrinkle that Windows has stricter requirements - // for filenames: I've been testing using a Linux build of PowerShell, but - // this code doesn't even compile on Linux. - #[cfg(windows)] - const SPECIAL_SHELL_CHARS: &[u8] = b"|&;<>()$`\"'*?[]=,{}"; - - /// Characters with a special meaning at the beginning of a name. - // ~ expands a home directory. - // # starts a comment. - // ! is a common extension for expanding the shell history. - #[cfg(any(unix, target_os = "wasi"))] - const SPECIAL_SHELL_CHARS_START: &[char] = &['~', '#', '!']; - // Same deal as before, this is possibly incomplete. - // A single stand-alone exclamation mark seems to have some special meaning. - #[cfg(windows)] - const SPECIAL_SHELL_CHARS_START: &[char] = &['~', '#', '@', '!']; - - /// Characters that are interpreted specially in a double-quoted string. - #[cfg(any(unix, target_os = "wasi"))] - const DOUBLE_UNSAFE: &[u8] = &[b'"', b'`', b'$', b'\\']; - #[cfg(windows)] - const DOUBLE_UNSAFE: &[u8] = &[b'"', b'`', b'$']; - - let text = match self.text.to_str() { - None => return write_escaped(f, self.text), - Some(text) => text, - }; - - let mut is_single_safe = true; - let mut is_double_safe = true; - let mut requires_quote = self.force_quote; - - if let Some(first) = text.chars().next() { - if SPECIAL_SHELL_CHARS_START.contains(&first) { - requires_quote = true; - } - // Unlike in Unix, quoting an argument may stop it - // from being recognized as an option. I like that very much. - // But we don't want to quote "-" because that's a common - // special argument and PowerShell doesn't mind it. - #[cfg(windows)] - if first == '-' && text.len() > 1 { - requires_quote = true; - } - } else { - // Empty string - requires_quote = true; - } - - for ch in text.chars() { - if ch.is_ascii() { - let ch = ch as u8; - if ch == b'\'' { - is_single_safe = false; - } - if DOUBLE_UNSAFE.contains(&ch) { - is_double_safe = false; - } - if !requires_quote && SPECIAL_SHELL_CHARS.contains(&ch) { - requires_quote = true; - } - if ch.is_ascii_control() { - return write_escaped(f, self.text); - } - } - if !requires_quote && ch.is_whitespace() { - // This includes unicode whitespace. - // We maybe don't have to escape it, we don't escape other lookalike - // characters either, but it's confusing if it goes unquoted. - requires_quote = true; - } - } - - if !requires_quote { - return f.write_str(text); - } else if is_single_safe { - return write_simple(f, text, '\''); - } else if is_double_safe { - return write_simple(f, text, '\"'); - } else { - return write_single_escaped(f, text); - } - - fn write_simple(f: &mut Formatter<'_>, text: &str, quote: char) -> fmt::Result { - f.write_char(quote)?; - f.write_str(text)?; - f.write_char(quote)?; - Ok(()) - } - - #[cfg(any(unix, target_os = "wasi"))] - fn write_single_escaped(f: &mut Formatter<'_>, text: &str) -> fmt::Result { - let mut iter = text.split('\''); - if let Some(chunk) = iter.next() { - if !chunk.is_empty() { - write_simple(f, chunk, '\'')?; - } - } - for chunk in iter { - f.write_str("\\'")?; - if !chunk.is_empty() { - write_simple(f, chunk, '\'')?; - } - } - Ok(()) - } - - /// Write using the syntax described here: - /// https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html - /// - /// Supported by these shells: - /// - bash - /// - zsh - /// - busybox sh - /// - mksh - /// - /// Not supported by these: - /// - fish - /// - dash - /// - tcsh - #[cfg(any(unix, target_os = "wasi"))] - fn write_escaped(f: &mut Formatter<'_>, text: &OsStr) -> fmt::Result { - f.write_str("$'")?; - for chunk in from_utf8_iter(text.as_bytes()) { - match chunk { - Ok(chunk) => { - for ch in chunk.chars() { - match ch { - '\n' => f.write_str("\\n")?, - '\t' => f.write_str("\\t")?, - '\r' => f.write_str("\\r")?, - // We could do \b, \f, \v, etc., but those are - // rare enough to be confusing. - // \0 doesn't work consistently because of the - // octal \nnn syntax, and null bytes can't appear - // in filenames anyway. - ch if ch.is_ascii_control() => write!(f, "\\x{:02X}", ch as u8)?, - '\\' | '\'' => { - // '?' and '"' can also be escaped this way - // but AFAICT there's no reason to do so - f.write_char('\\')?; - f.write_char(ch)?; - } - ch => { - f.write_char(ch)?; - } - } - } - } - Err(unit) => write!(f, "\\x{:02X}", unit)?, - } - } - f.write_char('\'')?; - Ok(()) - } - - #[cfg(windows)] - fn write_single_escaped(f: &mut Formatter<'_>, text: &str) -> fmt::Result { - // Quotes in Powershell can be escaped by doubling them - f.write_char('\'')?; - let mut iter = text.split('\''); - if let Some(chunk) = iter.next() { - f.write_str(chunk)?; - } - for chunk in iter { - f.write_str("''")?; - f.write_str(chunk)?; - } - f.write_char('\'')?; - Ok(()) - } - - #[cfg(windows)] - fn write_escaped(f: &mut Formatter<'_>, text: &OsStr) -> fmt::Result { - // ` takes the role of \ since \ is already used as the path separator. - // Things are UTF-16-oriented, so we escape code units as "`u{1234}". - use std::char::decode_utf16; - use std::os::windows::ffi::OsStrExt; - - f.write_char('"')?; - for ch in decode_utf16(text.encode_wide()) { - match ch { - Ok(ch) => match ch { - '\0' => f.write_str("`0")?, - '\r' => f.write_str("`r")?, - '\n' => f.write_str("`n")?, - '\t' => f.write_str("`t")?, - ch if ch.is_ascii_control() => write!(f, "`u{{{:04X}}}", ch as u8)?, - '`' => f.write_str("``")?, - '$' => f.write_str("`$")?, - '"' => f.write_str("\"\"")?, - ch => f.write_char(ch)?, - }, - Err(err) => write!(f, "`u{{{:04X}}}", err.unpaired_surrogate())?, - } - } - f.write_char('"')?; - Ok(()) - } - } - - #[cfg(not(any(unix, target_os = "wasi", windows)))] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - // As a fallback, we use Rust's own escaping rules. - // This is reasonably sane and very easy to implement. - // We use single quotes because that's hardcoded in a lot of tests. - let text = self.text.to_string_lossy(); - if self.force_quote || !text.chars().all(|ch| ch.is_alphanumeric() || ch == '.') { - write!(f, "'{}'", text.escape_debug()) - } else { - f.write_str(&text) - } - } -} - -#[cfg(any(unix, target_os = "wasi"))] -fn from_utf8_iter(mut bytes: &[u8]) -> impl Iterator> { - std::iter::from_fn(move || { - if bytes.is_empty() { - return None; - } - match from_utf8(bytes) { - Ok(text) => { - bytes = &[]; - Some(Ok(text)) - } - Err(err) if err.valid_up_to() == 0 => { - let res = bytes[0]; - bytes = &bytes[1..]; - Some(Err(res)) - } - Err(err) => { - let (valid, rest) = bytes.split_at(err.valid_up_to()); - bytes = rest; - Some(Ok(from_utf8(valid).unwrap())) - } - } - }) -} +// These used to be defined here, but they live in their own crate now. +pub use os_display::{Quotable, Quoted}; /// Print a path (or `OsStr`-like object) directly to stdout, with a trailing newline, /// without losing any information if its encoding is invalid. @@ -429,129 +67,3 @@ pub fn print_verbatim>(text: S) -> io::Result<()> { write!(stdout, "{}", std::path::Path::new(text.as_ref()).display()) } } - -#[cfg(test)] -mod tests { - use super::*; - - fn verify_quote(cases: &[(impl Quotable, &str)]) { - for (case, expected) in cases { - assert_eq!(case.quote().to_string(), *expected); - } - } - - fn verify_maybe(cases: &[(impl Quotable, &str)]) { - for (case, expected) in cases { - assert_eq!(case.maybe_quote().to_string(), *expected); - } - } - - /// This should hold on any platform, or else other tests fail. - #[test] - fn test_basic() { - verify_quote(&[ - ("foo", "'foo'"), - ("", "''"), - ("foo/bar.baz", "'foo/bar.baz'"), - ]); - verify_maybe(&[ - ("foo", "foo"), - ("", "''"), - ("foo bar", "'foo bar'"), - ("$foo", "'$foo'"), - ("-", "-"), - ]); - } - - #[cfg(any(unix, target_os = "wasi", windows))] - #[test] - fn test_common() { - verify_maybe(&[ - ("a#b", "a#b"), - ("#ab", "'#ab'"), - ("a~b", "a~b"), - ("!", "'!'"), - ]); - } - - #[cfg(any(unix, target_os = "wasi"))] - #[test] - fn test_unix() { - verify_quote(&[ - ("can't", r#""can't""#), - (r#"can'"t"#, r#"'can'\''"t'"#), - (r#"can'$t"#, r#"'can'\''$t'"#), - ("foo\nb\ta\r\\\0`r", r#"$'foo\nb\ta\r\\\x00`r'"#), - ("foo\x02", r#"$'foo\x02'"#), - (r#"'$''"#, r#"\''$'\'\'"#), - ]); - verify_quote(&[(OsStr::from_bytes(b"foo\xFF"), r#"$'foo\xFF'"#)]); - verify_maybe(&[ - ("-x", "-x"), - ("a,b", "a,b"), - ("a\\b", "'a\\b'"), - ("}", ("}")), - ]); - } - - #[cfg(windows)] - #[test] - fn test_windows() { - use std::ffi::OsString; - use std::os::windows::ffi::OsStringExt; - verify_quote(&[ - (r#"foo\bar"#, r#"'foo\bar'"#), - ("can't", r#""can't""#), - (r#"can'"t"#, r#"'can''"t'"#), - (r#"can'$t"#, r#"'can''$t'"#), - ("foo\nb\ta\r\\\0`r", r#""foo`nb`ta`r\`0``r""#), - ("foo\x02", r#""foo`u{0002}""#), - (r#"'$''"#, r#"'''$'''''"#), - ]); - verify_quote(&[( - OsString::from_wide(&[b'x' as u16, 0xD800]), - r#""x`u{D800}""#, - )]); - verify_maybe(&[ - ("-x", "'-x'"), - ("a,b", "'a,b'"), - ("a\\b", "a\\b"), - ("}", "'}'"), - ]); - } - - #[cfg(any(unix, target_os = "wasi"))] - #[test] - fn test_utf8_iter() { - type ByteStr = &'static [u8]; - type Chunk = Result<&'static str, u8>; - const CASES: &[(ByteStr, &[Chunk])] = &[ - (b"", &[]), - (b"hello", &[Ok("hello")]), - // Immediately invalid - (b"\xFF", &[Err(b'\xFF')]), - // Incomplete UTF-8 - (b"\xC2", &[Err(b'\xC2')]), - (b"\xF4\x8F", &[Err(b'\xF4'), Err(b'\x8F')]), - (b"\xFF\xFF", &[Err(b'\xFF'), Err(b'\xFF')]), - (b"hello\xC2", &[Ok("hello"), Err(b'\xC2')]), - (b"\xFFhello", &[Err(b'\xFF'), Ok("hello")]), - (b"\xFF\xC2hello", &[Err(b'\xFF'), Err(b'\xC2'), Ok("hello")]), - (b"foo\xFFbar", &[Ok("foo"), Err(b'\xFF'), Ok("bar")]), - ( - b"foo\xF4\x8Fbar", - &[Ok("foo"), Err(b'\xF4'), Err(b'\x8F'), Ok("bar")], - ), - ( - b"foo\xFF\xC2bar", - &[Ok("foo"), Err(b'\xFF'), Err(b'\xC2'), Ok("bar")], - ), - ]; - for &(case, expected) in CASES { - assert_eq!( - from_utf8_iter(case).collect::>().as_slice(), - expected - ); - } - } -} From 0b86afa858be5e9149573b06a15b80ee129329c9 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Tue, 5 Oct 2021 20:59:51 -0400 Subject: [PATCH 095/131] seq: correct fixed-width spacing for inf sequences Pad infinity and negative infinity values with spaces when using the `-w` option to `seq`. This corrects the behavior of `seq` to match that of the GNU version: $ seq -w 1.000 inf inf | head -n 4 1.000 inf inf inf Previously, it incorrectly padded with 0s instead of spaces. --- src/uu/seq/src/seq.rs | 8 ++++++++ tests/by-util/test_seq.rs | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index f28b4d6e8..75e9b1598 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -235,6 +235,14 @@ fn write_value_float( width = if width > 0 { width - 1 } else { width }, precision = precision, ) + } else if *value == ExtendedBigDecimal::Infinity || *value == ExtendedBigDecimal::MinusInfinity + { + format!( + "{value:>width$.precision$}", + value = value, + width = width, + precision = precision, + ) } else { format!( "{value:>0width$.precision$}", diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 2a2e31f83..312707753 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -530,6 +530,22 @@ fn test_inf() { run(&["inf"], b"1\n2\n3\n"); } +#[test] +fn test_inf_width() { + run( + &["-w", "1.000", "inf", "inf"], + b"1.000\n inf\n inf\n inf\n", + ); +} + +#[test] +fn test_neg_inf_width() { + run( + &["-w", "1.000", "-inf", "-inf"], + b"1.000\n -inf\n -inf\n -inf\n", + ); +} + #[test] fn test_ignore_leading_whitespace() { new_ucmd!() From fc300dda24faa4d659a3f8039a7beb14bb9615b1 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 9 Nov 2021 00:09:18 -0300 Subject: [PATCH 096/131] tests/common: UCommand::new rename arg to bin_path Merge and remove unecessary `.as_ref()` --- tests/common/util.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/common/util.rs b/tests/common/util.rs index e71f18573..22c87a95c 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -838,12 +838,17 @@ pub struct UCommand { } impl UCommand { - pub fn new, U: AsRef>(arg: T, curdir: U, env_clear: bool) -> UCommand { + pub fn new, U: AsRef>( + bin_path: T, + curdir: U, + env_clear: bool, + ) -> UCommand { + let bin_path = bin_path.as_ref(); UCommand { tmpd: None, has_run: false, raw: { - let mut cmd = Command::new(arg.as_ref()); + let mut cmd = Command::new(bin_path); cmd.current_dir(curdir.as_ref()); if env_clear { if cfg!(windows) { @@ -863,7 +868,7 @@ impl UCommand { } cmd }, - comm_string: String::from(arg.as_ref().to_str().unwrap()), + comm_string: String::from(bin_path.to_str().unwrap()), ignore_stdin_write_error: false, bytes_into_stdin: None, stdin: None, @@ -874,9 +879,13 @@ impl UCommand { } } - pub fn new_from_tmp>(arg: T, tmpd: Rc, env_clear: bool) -> UCommand { + pub fn new_from_tmp>( + bin_path: T, + tmpd: Rc, + env_clear: bool, + ) -> UCommand { let tmpd_path_buf = String::from(&(*tmpd.as_ref().path().to_str().unwrap())); - let mut ucmd: UCommand = UCommand::new(arg.as_ref(), tmpd_path_buf, env_clear); + let mut ucmd: UCommand = UCommand::new(bin_path, tmpd_path_buf, env_clear); ucmd.tmpd = Some(tmpd); ucmd } From d4ca4371d7bf53cf3dfa6c60b3b9ef20c5599ee3 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 9 Nov 2021 02:16:06 -0300 Subject: [PATCH 097/131] tests/common: add util_name+bin_path to UCommand --- tests/common/util.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/common/util.rs b/tests/common/util.rs index 22c87a95c..fd2a00c4e 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -788,7 +788,7 @@ impl TestScenario { /// Returns builder for invoking any system command. Paths given are treated /// relative to the environment's unique temporary test directory. pub fn cmd>(&self, bin: S) -> UCommand { - UCommand::new_from_tmp(bin, self.tmpd.clone(), true) + UCommand::new_from_tmp::(bin, None, self.tmpd.clone(), true) } /// Returns builder for invoking any uutils command. Paths given are treated @@ -812,7 +812,7 @@ impl TestScenario { /// Differs from the builder returned by `cmd` in that `cmd_keepenv` does not call /// `Command::env_clear` (Clears the entire environment map for the child process.) pub fn cmd_keepenv>(&self, bin: S) -> UCommand { - UCommand::new_from_tmp(bin, self.tmpd.clone(), false) + UCommand::new_from_tmp::(bin, None, self.tmpd.clone(), false) } } @@ -826,6 +826,8 @@ impl TestScenario { pub struct UCommand { pub raw: Command, comm_string: String, + bin_path: String, + util_name: Option, tmpd: Option>, has_run: bool, ignore_stdin_write_error: bool, @@ -838,13 +840,16 @@ pub struct UCommand { } impl UCommand { - pub fn new, U: AsRef>( + pub fn new, S: AsRef, U: AsRef>( bin_path: T, + util_name: Option, curdir: U, env_clear: bool, ) -> UCommand { let bin_path = bin_path.as_ref(); - UCommand { + let util_name = util_name.as_ref().map(|un| un.as_ref()); + + let mut ucmd = UCommand { tmpd: None, has_run: false, raw: { @@ -869,6 +874,8 @@ impl UCommand { cmd }, comm_string: String::from(bin_path.to_str().unwrap()), + bin_path: bin_path.to_str().unwrap().to_string(), + util_name: util_name.map(|un| un.to_str().unwrap().to_string()), ignore_stdin_write_error: false, bytes_into_stdin: None, stdin: None, @@ -876,16 +883,23 @@ impl UCommand { stderr: None, #[cfg(target_os = "linux")] limits: vec![], + }; + + if let Some(un) = util_name { + ucmd.arg(un); } + + ucmd } - pub fn new_from_tmp>( + pub fn new_from_tmp, S: AsRef>( bin_path: T, + util_name: Option, tmpd: Rc, env_clear: bool, ) -> UCommand { let tmpd_path_buf = String::from(&(*tmpd.as_ref().path().to_str().unwrap())); - let mut ucmd: UCommand = UCommand::new(bin_path, tmpd_path_buf, env_clear); + let mut ucmd: UCommand = UCommand::new(bin_path, util_name, tmpd_path_buf, env_clear); ucmd.tmpd = Some(tmpd); ucmd } From ab4573bde9ee7af533a132c8817d0c28a52d8b3d Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 9 Nov 2021 04:20:43 -0300 Subject: [PATCH 098/131] tests/common: create TestScenario::composite_cmd This is made to call UCommand::new with Some(util_name) --- tests/common/util.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tests/common/util.rs b/tests/common/util.rs index fd2a00c4e..d649cd7ca 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -780,9 +780,18 @@ impl TestScenario { /// Returns builder for invoking the target uutils binary. Paths given are /// treated relative to the environment's unique temporary test directory. pub fn ucmd(&self) -> UCommand { - let mut cmd = self.cmd(&self.bin_path); - cmd.arg(&self.util_name); - cmd + self.composite_cmd(&self.bin_path, &self.util_name, true) + } + + /// Returns builder for invoking the target uutils binary. Paths given are + /// treated relative to the environment's unique temporary test directory. + pub fn composite_cmd, T: AsRef>( + &self, + bin: S, + util_name: T, + env_clear: bool, + ) -> UCommand { + UCommand::new_from_tmp(bin, Some(util_name), self.tmpd.clone(), env_clear) } /// Returns builder for invoking any system command. Paths given are treated @@ -794,17 +803,13 @@ impl TestScenario { /// Returns builder for invoking any uutils command. Paths given are treated /// relative to the environment's unique temporary test directory. pub fn ccmd>(&self, bin: S) -> UCommand { - let mut cmd = self.cmd(&self.bin_path); - cmd.arg(bin); - cmd + self.composite_cmd(&self.bin_path, bin, true) } // different names are used rather than an argument // because the need to keep the environment is exceedingly rare. pub fn ucmd_keepenv(&self) -> UCommand { - let mut cmd = self.cmd_keepenv(&self.bin_path); - cmd.arg(&self.util_name); - cmd + self.composite_cmd(&self.bin_path, &self.util_name, false) } /// Returns builder for invoking any system command. Paths given are treated From 0bbc805e43f99b2760bd9d3a0791808b015cc04b Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 9 Nov 2021 14:37:38 -0300 Subject: [PATCH 099/131] tests/common: add util_name+bin_path to CmdResult --- tests/common/util.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/common/util.rs b/tests/common/util.rs index d649cd7ca..d860e0c0b 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -62,6 +62,10 @@ fn read_scenario_fixture>(tmpd: &Option>, file_rel_p /// within a struct which has convenience assertion functions about those outputs #[derive(Debug, Clone)] pub struct CmdResult { + /// bin_path provided by `TestScenario` or `UCommand` + bin_path: String, + /// util_name provided by `TestScenario` or `UCommand` + util_name: Option, //tmpd is used for convenience functions for asserts against fixtures tmpd: Option>, /// exit status for command (if there is one) @@ -77,6 +81,8 @@ pub struct CmdResult { impl CmdResult { pub fn new( + bin_path: String, + util_name: Option, tmpd: Option>, code: Option, success: bool, @@ -84,6 +90,8 @@ impl CmdResult { stderr: &[u8], ) -> CmdResult { CmdResult { + bin_path, + util_name, tmpd, code, success, @@ -1049,6 +1057,8 @@ impl UCommand { let prog = self.run_no_wait().wait_with_output().unwrap(); CmdResult { + bin_path: self.bin_path.clone(), + util_name: self.util_name.clone(), tmpd: self.tmpd.clone(), code: prog.status.code(), success: prog.status.success(), @@ -1296,6 +1306,8 @@ pub fn expected_result(ts: &TestScenario, args: &[&str]) -> std::result::Result< }; Ok(CmdResult::new( + ts.bin_path.as_os_str().to_str().unwrap().to_string(), + Some(ts.util_name.clone()), Some(result.tmpd()), Some(result.code()), result.succeeded(), @@ -1313,6 +1325,8 @@ mod tests { #[test] fn test_code_is() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: Some(32), success: false, @@ -1326,6 +1340,8 @@ mod tests { #[should_panic] fn test_code_is_fail() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: Some(32), success: false, @@ -1338,6 +1354,8 @@ mod tests { #[test] fn test_failure() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: false, @@ -1351,6 +1369,8 @@ mod tests { #[should_panic] fn test_failure_fail() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1363,6 +1383,8 @@ mod tests { #[test] fn test_success() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1376,6 +1398,8 @@ mod tests { #[should_panic] fn test_success_fail() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: false, @@ -1388,6 +1412,8 @@ mod tests { #[test] fn test_no_stderr_output() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1402,6 +1428,8 @@ mod tests { #[should_panic] fn test_no_stderr_fail() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1416,6 +1444,8 @@ mod tests { #[should_panic] fn test_no_stdout_fail() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1429,6 +1459,8 @@ mod tests { #[test] fn test_std_does_not_contain() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1443,6 +1475,8 @@ mod tests { #[should_panic] fn test_stdout_does_not_contain_fail() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1457,6 +1491,8 @@ mod tests { #[should_panic] fn test_stderr_does_not_contain_fail() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1470,6 +1506,8 @@ mod tests { #[test] fn test_stdout_matches() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1486,6 +1524,8 @@ mod tests { #[should_panic] fn test_stdout_matches_fail() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1501,6 +1541,8 @@ mod tests { #[should_panic] fn test_stdout_not_matches_fail() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1515,6 +1557,8 @@ mod tests { #[test] fn test_normalized_newlines_stdout_is() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, @@ -1531,6 +1575,8 @@ mod tests { #[should_panic] fn test_normalized_newlines_stdout_is_fail() { let res = CmdResult { + bin_path: "".into(), + util_name: None, tmpd: None, code: None, success: true, From f43dfa9a61ece3cde6c434e2164b0046dcc7d047 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 9 Nov 2021 14:43:55 -0300 Subject: [PATCH 100/131] tests/common: implement CmdResult::usage_error --- tests/common/util.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/common/util.rs b/tests/common/util.rs index d860e0c0b..cfde5f229 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -365,6 +365,23 @@ impl CmdResult { self } + /// asserts that + /// 1. the command resulted in stderr stream output that equals the + /// the following format when both are trimmed of trailing whitespace + /// `"{util_name}: {msg}\nTry '{bin_path} {util_name} --help' for more information."` + /// This the expected format when a UUsageError is returned or when show_error! is called + /// `msg` should be the same as the one provided to UUsageError::new or show_error! + /// + /// 2. the command resulted in empty (zero-length) stdout stream output + pub fn usage_error>(&self, msg: T) -> &CmdResult { + self.stderr_only(format!( + "{0}: {2}\nTry '{1} {0} --help' for more information.", + self.util_name.as_ref().unwrap(), // This shouldn't be called using a normal command + self.bin_path, + msg.as_ref() + )) + } + pub fn stdout_contains>(&self, cmp: T) -> &CmdResult { assert!( self.stdout_str().contains(cmp.as_ref()), From c9624725ab532f513d64392d38e5dcbab92e722e Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 9 Nov 2021 17:23:41 -0300 Subject: [PATCH 101/131] tests: use CmdResult::usage_error --- tests/by-util/test_base32.rs | 10 ++-------- tests/by-util/test_base64.rs | 10 ++-------- tests/by-util/test_basename.rs | 17 +++++------------ tests/by-util/test_cp.rs | 10 +++------- tests/by-util/test_more.rs | 13 ++++--------- tests/by-util/test_mv.rs | 10 +++------- tests/by-util/test_nice.rs | 13 ++++--------- tests/by-util/test_seq.rs | 28 +++++++++------------------- tests/by-util/test_stdbuf.rs | 10 ++-------- 9 files changed, 34 insertions(+), 87 deletions(-) diff --git a/tests/by-util/test_base32.rs b/tests/by-util/test_base32.rs index ffe2cf74c..4d244704d 100644 --- a/tests/by-util/test_base32.rs +++ b/tests/by-util/test_base32.rs @@ -113,18 +113,12 @@ fn test_wrap_bad_arg() { #[test] fn test_base32_extra_operand() { - let ts = TestScenario::new(util_name!()); - // Expect a failure when multiple files are specified. - ts.ucmd() + new_ucmd!() .arg("a.txt") .arg("b.txt") .fails() - .stderr_only(format!( - "{0}: extra operand 'b.txt'\nTry '{1} {0} --help' for more information.", - ts.util_name, - ts.bin_path.to_string_lossy() - )); + .usage_error("extra operand 'b.txt'"); } #[test] diff --git a/tests/by-util/test_base64.rs b/tests/by-util/test_base64.rs index 87aa0db44..9a7d525bb 100644 --- a/tests/by-util/test_base64.rs +++ b/tests/by-util/test_base64.rs @@ -95,18 +95,12 @@ fn test_wrap_bad_arg() { #[test] fn test_base64_extra_operand() { - let ts = TestScenario::new(util_name!()); - // Expect a failure when multiple files are specified. - ts.ucmd() + new_ucmd!() .arg("a.txt") .arg("b.txt") .fails() - .stderr_only(format!( - "{0}: extra operand 'b.txt'\nTry '{1} {0} --help' for more information.", - ts.util_name, - ts.bin_path.to_string_lossy() - )); + .usage_error("extra operand 'b.txt'"); } #[test] diff --git a/tests/by-util/test_basename.rs b/tests/by-util/test_basename.rs index 141745ac3..962d7373d 100644 --- a/tests/by-util/test_basename.rs +++ b/tests/by-util/test_basename.rs @@ -114,12 +114,7 @@ fn test_no_args() { #[test] fn test_no_args_output() { - let ts = TestScenario::new(util_name!()); - ts.ucmd().fails().stderr_is(&format!( - "{0}: missing operand\nTry '{1} {0} --help' for more information.", - ts.util_name, - ts.bin_path.to_string_lossy() - )); + new_ucmd!().fails().usage_error("missing operand"); } #[test] @@ -129,12 +124,10 @@ fn test_too_many_args() { #[test] fn test_too_many_args_output() { - let ts = TestScenario::new(util_name!()); - ts.ucmd().args(&["a", "b", "c"]).fails().stderr_is(format!( - "{0}: extra operand 'c'\nTry '{1} {0} --help' for more information.", - ts.util_name, - ts.bin_path.to_string_lossy() - )); + new_ucmd!() + .args(&["a", "b", "c"]) + .fails() + .usage_error("extra operand 'c'"); } #[cfg(any(unix, target_os = "redox"))] diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index e86f35833..50abfe967 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -563,17 +563,13 @@ fn test_cp_backup_off() { #[test] fn test_cp_backup_no_clobber_conflicting_options() { - let ts = TestScenario::new(util_name!()); - ts.ucmd() + new_ucmd!() .arg("--backup") .arg("--no-clobber") .arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE) - .fails().stderr_is(&format!( - "{0}: options --backup and --no-clobber are mutually exclusive\nTry '{1} {0} --help' for more information.", - ts.util_name, - ts.bin_path.to_string_lossy() - )); + .fails() + .usage_error("options --backup and --no-clobber are mutually exclusive"); } #[test] diff --git a/tests/by-util/test_more.rs b/tests/by-util/test_more.rs index 4b2719d8f..5c2b3c0f6 100644 --- a/tests/by-util/test_more.rs +++ b/tests/by-util/test_more.rs @@ -15,15 +15,10 @@ fn test_more_dir_arg() { // Maybe we could capture the error, i.e. "Device not found" in that case // but I am leaving this for later if atty::is(atty::Stream::Stdout) { - let ts = TestScenario::new(util_name!()); - let result = ts.ucmd().arg(".").run(); - result.failure(); - let expected_error_message = &format!( - "{0}: '.' is a directory.\nTry '{1} {0} --help' for more information.", - ts.util_name, - ts.bin_path.to_string_lossy() - ); - assert_eq!(result.stderr_str().trim(), expected_error_message); + new_ucmd!() + .arg(".") + .fails() + .usage_error("'.' is a directory."); } else { } } diff --git a/tests/by-util/test_mv.rs b/tests/by-util/test_mv.rs index 8d9b00664..f6650cdba 100644 --- a/tests/by-util/test_mv.rs +++ b/tests/by-util/test_mv.rs @@ -522,17 +522,13 @@ fn test_mv_backup_off() { #[test] fn test_mv_backup_no_clobber_conflicting_options() { - let ts = TestScenario::new(util_name!()); - - ts.ucmd().arg("--backup") + new_ucmd!() + .arg("--backup") .arg("--no-clobber") .arg("file1") .arg("file2") .fails() - .stderr_is(&format!("{0}: options --backup and --no-clobber are mutually exclusive\nTry '{1} {0} --help' for more information.", - ts.util_name, - ts.bin_path.to_string_lossy() - )); + .usage_error("options --backup and --no-clobber are mutually exclusive"); } #[test] diff --git a/tests/by-util/test_nice.rs b/tests/by-util/test_nice.rs index 7a99a333d..4a77ae24e 100644 --- a/tests/by-util/test_nice.rs +++ b/tests/by-util/test_nice.rs @@ -22,15 +22,10 @@ fn test_negative_adjustment() { #[test] fn test_adjustment_with_no_command_should_error() { - let ts = TestScenario::new(util_name!()); - - ts.ucmd() - .args(&["-n", "19"]) - .run() - .stderr_is(&format!("{0}: A command must be given with an adjustment.\nTry '{1} {0} --help' for more information.\n", - ts.util_name, - ts.bin_path.to_string_lossy() - )); + new_ucmd!() + .args(&["-n", "19"]) + .fails() + .usage_error("A command must be given with an adjustment."); } #[test] diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 2a2e31f83..90166de92 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -66,24 +66,18 @@ fn test_hex_identifier_in_wrong_place() { #[test] fn test_rejects_nan() { - let ts = TestScenario::new(util_name!()); - - ts.ucmd().args(&["NaN"]).fails().stderr_only(format!( - "{0}: invalid 'not-a-number' argument: 'NaN'\nTry '{1} {0} --help' for more information.", - ts.util_name, - ts.bin_path.to_string_lossy() - )); + new_ucmd!() + .arg("NaN") + .fails() + .usage_error("invalid 'not-a-number' argument: 'NaN'"); } #[test] fn test_rejects_non_floats() { - let ts = TestScenario::new(util_name!()); - - ts.ucmd().args(&["foo"]).fails().stderr_only(&format!( - "{0}: invalid floating point argument: 'foo'\nTry '{1} {0} --help' for more information.", - ts.util_name, - ts.bin_path.to_string_lossy() - )); + new_ucmd!() + .arg("foo") + .fails() + .usage_error("invalid floating point argument: 'foo'"); } #[test] @@ -547,11 +541,7 @@ fn test_trailing_whitespace_error() { new_ucmd!() .arg("1 ") .fails() - .no_stdout() - .stderr_contains("seq: invalid floating point argument: '1 '") - // FIXME The second line of the error message is "Try 'seq - // --help' for more information." - .stderr_contains("for more information."); + .usage_error("invalid floating point argument: '1 '"); } #[test] diff --git a/tests/by-util/test_stdbuf.rs b/tests/by-util/test_stdbuf.rs index c05b65d70..3b03a1d4c 100644 --- a/tests/by-util/test_stdbuf.rs +++ b/tests/by-util/test_stdbuf.rs @@ -53,16 +53,10 @@ fn test_stdbuf_trailing_var_arg() { #[cfg(not(target_os = "windows"))] #[test] fn test_stdbuf_line_buffering_stdin_fails() { - let ts = TestScenario::new(util_name!()); - - ts.ucmd() + new_ucmd!() .args(&["-i", "L", "head"]) .fails() - .stderr_is(&format!( - "{0}: line buffering stdin is meaningless\nTry '{1} {0} --help' for more information.", - ts.util_name, - ts.bin_path.to_string_lossy() - )); + .usage_error("line buffering stdin is meaningless"); } #[cfg(not(target_os = "windows"))] From 235152a6b7e4e842d121bea3d6fff716255f1f55 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 9 Nov 2021 19:57:24 -0300 Subject: [PATCH 102/131] uucore/utmpx: remove unwrap in cannon_host Default to hostname if getaddrinfo fails --- src/uucore/src/lib/features/utmpx.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/uucore/src/lib/features/utmpx.rs b/src/uucore/src/lib/features/utmpx.rs index a96c3f48c..a3078b818 100644 --- a/src/uucore/src/lib/features/utmpx.rs +++ b/src/uucore/src/lib/features/utmpx.rs @@ -236,17 +236,20 @@ impl Utmpx { flags: AI_CANONNAME, ..AddrInfoHints::default() }; - let sockets = getaddrinfo(Some(hostname), None, Some(hints)) - .unwrap() - .collect::>>()?; - for socket in sockets { - if let Some(ai_canonname) = socket.canonname { - return Ok(if display.is_empty() { - ai_canonname - } else { - format!("{}:{}", ai_canonname, display) - }); + if let Ok(sockets) = getaddrinfo(Some(hostname), None, Some(hints)) { + let sockets = sockets.collect::>>()?; + for socket in sockets { + if let Some(ai_canonname) = socket.canonname { + return Ok(if display.is_empty() { + ai_canonname + } else { + format!("{}:{}", ai_canonname, display) + }); + } } + } else { + // GNU coreutils has this behavior + return Ok(hostname.to_string()); } } From cbe6d7d5c1ed5aabe6a6a0a319d1c805756e7fbc Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Tue, 9 Nov 2021 19:58:54 -0300 Subject: [PATCH 103/131] who: use UResult --- src/uu/who/src/who.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/uu/who/src/who.rs b/src/uu/who/src/who.rs index a975c82ba..14f39536d 100644 --- a/src/uu/who/src/who.rs +++ b/src/uu/who/src/who.rs @@ -7,8 +7,8 @@ // spell-checker:ignore (ToDO) ttyname hostnames runlevel mesg wtmp statted boottime deadprocs initspawn clockchange curr runlvline pidstr exitstr hoststr -#[macro_use] -extern crate uucore; +use uucore::display::Quotable; +use uucore::error::{FromIo, UResult}; use uucore::libc::{ttyname, STDIN_FILENO, S_IWGRP}; use uucore::utmpx::{self, time, Utmpx}; @@ -59,7 +59,8 @@ fn get_long_usage() -> String { ) } -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = args .collect_str(InvalidEncodingHandling::Ignore) .accept_any(); @@ -157,9 +158,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { args: files, }; - who.exec(); - - 0 + who.exec() } pub fn uu_app() -> App<'static, 'static> { @@ -326,7 +325,7 @@ fn current_tty() -> String { } impl Who { - fn exec(&mut self) { + fn exec(&mut self) -> UResult<()> { let run_level_chk = |_record: i16| { #[cfg(not(target_os = "linux"))] return false; @@ -362,7 +361,7 @@ impl Who { for ut in records { if !self.my_line_only || cur_tty == ut.tty_device() { if self.need_users && ut.is_user_process() { - self.print_user(&ut); + self.print_user(&ut)?; } else if self.need_runlevel && run_level_chk(ut.record_type()) { if cfg!(target_os = "linux") { self.print_runlevel(&ut); @@ -383,6 +382,7 @@ impl Who { if ut.record_type() == utmpx::BOOT_TIME {} } } + Ok(()) } #[inline] @@ -464,7 +464,7 @@ impl Who { self.print_line("", ' ', "system boot", &time_string(ut), "", "", "", ""); } - fn print_user(&self, ut: &Utmpx) { + fn print_user(&self, ut: &Utmpx) -> UResult<()> { let mut p = PathBuf::from("/dev"); p.push(ut.tty_device().as_str()); let mesg; @@ -491,7 +491,13 @@ impl Who { }; let s = if self.do_lookup { - crash_if_err!(1, ut.canon_host()) + ut.canon_host().map_err_context(|| { + let host = ut.host(); + format!( + "failed to canonicalize {}", + host.split(':').next().unwrap_or(&host).quote() + ) + })? } else { ut.host() }; @@ -507,6 +513,8 @@ impl Who { hoststr.as_str(), "", ); + + Ok(()) } #[allow(clippy::too_many_arguments)] From 32b0178a720286ae64d64fcdd2ced51d69126067 Mon Sep 17 00:00:00 2001 From: nicoo Date: Tue, 16 Mar 2021 21:27:24 +0100 Subject: [PATCH 104/131] factor: Update to current versions of `smallvec` smallvec 1.0 and later wasn't compatible with Rust 1.33 but the minimum supported Rust version for coreutils moved on. --- Cargo.lock | 231 +++++++++++++++++++-------------------- src/uu/factor/Cargo.toml | 4 +- 2 files changed, 116 insertions(+), 119 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5bd0e776a..cc33d49e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,7 +20,7 @@ version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ - "memchr 2.4.0", + "memchr 2.4.1", ] [[package]] @@ -112,7 +112,7 @@ dependencies = [ "log", "peeking_take_while", "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "regex", "rustc-hash", "shlex", @@ -175,12 +175,12 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ "lazy_static", - "memchr 2.4.0", + "memchr 2.4.1", "regex-automata", ] @@ -192,9 +192,9 @@ checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" [[package]] name = "byte-unit" -version = "4.0.12" +version = "4.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063197e6eb4b775b64160dedde7a0986bb2836cce140e9492e9e96f28e18bcd8" +checksum = "956ffc5b0ec7d7a6949e3f21fd63ba5af4cffdc2ba1e0b7bf62b481458c4ae7f" dependencies = [ "utf8-width", ] @@ -213,9 +213,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.69" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" +checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" [[package]] name = "cexpr" @@ -253,9 +253,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853eda514c284c2287f4bf20ae614f8781f40a81d32ecda6e91449304dfe077c" +checksum = "fa66045b9cb23c2e9c1520732030608b02ee07e5cfaa5a521ec15ded7fa24c90" dependencies = [ "glob", "libc", @@ -505,7 +505,7 @@ dependencies = [ "if_rust_version", "lazy_static", "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "syn", ] @@ -586,9 +586,9 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.20.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d" +checksum = "c85525306c4291d1b73ce93c8acf9c339f9b213aef6c1d85c3830cbf1c16325c" dependencies = [ "bitflags", "crossterm_winapi", @@ -602,30 +602,30 @@ dependencies = [ [[package]] name = "crossterm_winapi" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" dependencies = [ "winapi 0.3.9", ] [[package]] name = "ctor" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" +checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" dependencies = [ - "quote 1.0.9", + "quote 1.0.10", "syn", ] [[package]] name = "ctrlc" -version = "3.1.9" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232295399409a8b7ae41276757b5a1cc21032848d42bff2352261f958b3ca29a" +checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf" dependencies = [ - "nix 0.20.0", + "nix 0.23.0", "winapi 0.3.9", ] @@ -668,7 +668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "syn", ] @@ -698,9 +698,9 @@ dependencies = [ [[package]] name = "dns-lookup" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093d88961fd18c4ecacb8c80cd0b356463ba941ba11e0e01f9cf5271380b79dc" +checksum = "53ecafc952c4528d9b51a458d1a8904b81783feff9fde08ab6ed2545ff396872" dependencies = [ "cfg-if 1.0.0", "libc", @@ -818,9 +818,9 @@ checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" [[package]] name = "gcd" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7cd301bf2ab11ae4e5bdfd79c221d97a25e46c089144a62ee9d09cb32d2b92" +checksum = "6c8763772808ee8fe3128f0fc424bed6d9942293fddbcfd595ecfa58a81fe00b" [[package]] name = "generic-array" @@ -884,9 +884,9 @@ dependencies = [ [[package]] name = "half" -version = "1.7.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" @@ -946,9 +946,9 @@ checksum = "46dbcb333e86939721589d25a3557e180b52778cb33c7fdfe9e0158ff790d5ec" [[package]] name = "instant" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", ] @@ -1001,15 +1001,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.101" +version = "0.2.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" [[package]] name = "libloading" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" +checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0" dependencies = [ "cfg-if 1.0.0", "winapi 0.3.9", @@ -1017,9 +1017,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" dependencies = [ "scopeguard", ] @@ -1048,12 +1048,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "md5" version = "0.3.8" @@ -1071,9 +1065,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "memmap2" @@ -1095,9 +1089,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.7" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", "log", @@ -1152,6 +1146,19 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nix" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -1166,7 +1173,7 @@ checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" dependencies = [ "bitvec", "funty", - "memchr 2.4.0", + "memchr 2.4.1", "version_check", ] @@ -1181,9 +1188,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", @@ -1237,7 +1244,7 @@ checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "syn", ] @@ -1311,7 +1318,7 @@ dependencies = [ "Inflector", "proc-macro-error", "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "syn", ] @@ -1326,9 +1333,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", @@ -1337,15 +1344,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ "cfg-if 1.0.0", "instant", "libc", "redox_syscall", - "smallvec 1.6.1", + "smallvec", "winapi 0.3.9", ] @@ -1376,9 +1383,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pkg-config" -version = "0.3.19" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" [[package]] name = "platform-info" @@ -1392,9 +1399,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] name = "pretty_assertions" @@ -1410,9 +1417,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fdbd1df62156fbc5945f4762632564d7d038153091c3fcf1067f6aef7cff92" +checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" dependencies = [ "thiserror", "toml", @@ -1426,7 +1433,7 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "syn", "version_check", ] @@ -1438,7 +1445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "version_check", ] @@ -1450,9 +1457,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.28" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" dependencies = [ "unicode-xid 0.2.2", ] @@ -1489,9 +1496,9 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ "proc-macro2", ] @@ -1677,7 +1684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", - "memchr 2.4.0", + "memchr 2.4.1", "regex-syntax", ] @@ -1704,9 +1711,9 @@ dependencies = [ [[package]] name = "retain_mut" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c17925a9027d298a4603d286befe3f9dc0e8ed02523141914eb628798d6e5b" +checksum = "448296241d034b96c11173591deaa1302f2c17b56092106c1f92c1bc0183a8c9" [[package]] name = "rlimit" @@ -1751,9 +1758,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "selinux" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cf704a543fe60d898f3253f1cc37655d0f0e9cdb68ef6230557e0e031b80608" +checksum = "09715d6b4356e916047e61e4dce40a67ac93036851957b91713d3d9c282d1548" dependencies = [ "bitflags", "libc", @@ -1791,7 +1798,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "syn", ] @@ -1828,15 +1835,15 @@ dependencies = [ [[package]] name = "shlex" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39" +checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1" dependencies = [ "libc", "signal-hook-registry", @@ -1864,18 +1871,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "0.6.14" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "smawk" @@ -1885,11 +1883,10 @@ checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" [[package]] name = "socket2" -version = "0.3.19" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" dependencies = [ - "cfg-if 1.0.0", "libc", "winapi 0.3.9", ] @@ -1920,18 +1917,18 @@ checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" dependencies = [ "heck", "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "syn", ] [[package]] name = "syn" -version = "1.0.74" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" dependencies = [ "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "unicode-xid 0.2.2", ] @@ -2042,21 +2039,21 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.26" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.26" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "syn", ] @@ -2081,9 +2078,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" [[package]] name = "unicode-linebreak" @@ -2102,9 +2099,9 @@ checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" @@ -2325,7 +2322,7 @@ dependencies = [ "atty", "bstr", "clap", - "memchr 2.4.0", + "memchr 2.4.1", "uucore", "uucore_procs", ] @@ -2450,7 +2447,7 @@ dependencies = [ "paste", "quickcheck", "rand 0.7.3", - "smallvec 0.6.14", + "smallvec", "uucore", "uucore_procs", ] @@ -2503,7 +2500,7 @@ dependencies = [ "hex", "libc", "md5", - "memchr 2.4.0", + "memchr 2.4.1", "regex", "regex-syntax", "sha1", @@ -2518,7 +2515,7 @@ name = "uu_head" version = "0.0.8" dependencies = [ "clap", - "memchr 2.4.0", + "memchr 2.4.1", "uucore", "uucore_procs", ] @@ -2722,7 +2719,7 @@ dependencies = [ "aho-corasick", "clap", "libc", - "memchr 2.4.0", + "memchr 2.4.1", "regex", "regex-syntax", "uucore", @@ -2840,7 +2837,7 @@ dependencies = [ "aho-corasick", "clap", "libc", - "memchr 2.4.0", + "memchr 2.4.1", "regex", "regex-syntax", "uucore", @@ -2971,7 +2968,7 @@ dependencies = [ "ctrlc", "fnv", "itertools 0.10.1", - "memchr 2.4.0", + "memchr 2.4.1", "ouroboros", "rand 0.7.3", "rayon", @@ -3046,7 +3043,7 @@ name = "uu_tac" version = "0.0.8" dependencies = [ "clap", - "memchr 2.4.0", + "memchr 2.4.1", "memmap2", "regex", "uucore", @@ -3290,7 +3287,7 @@ name = "uucore_procs" version = "0.0.7" dependencies = [ "proc-macro2", - "quote 1.0.9", + "quote 1.0.10", "syn", ] @@ -3413,6 +3410,6 @@ dependencies = [ [[package]] name = "z85" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b56e4f9906a4ef5412875e9ce448364023335cec645fd457ecf51d4f2781" +checksum = "af896e93db81340b74b65f74276a99b210c086f3d34ed0abf433182a462af856" diff --git a/src/uu/factor/Cargo.toml b/src/uu/factor/Cargo.toml index 2d2fa236b..1142560d9 100644 --- a/src/uu/factor/Cargo.toml +++ b/src/uu/factor/Cargo.toml @@ -15,13 +15,13 @@ edition = "2018" num-traits = "0.2.13" # used in src/numerics.rs, which is included by build.rs [dependencies] +clap = { version = "2.33", features = ["wrap_help"] } coz = { version = "0.1.3", optional = true } num-traits = "0.2.13" # Needs at least version 0.2.13 for "OverflowingAdd" rand = { version = "0.7", features = ["small_rng"] } -smallvec = { version = "0.6.14, < 1.0" } +smallvec = "1.7" uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" } uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" } -clap = { version = "2.33", features = ["wrap_help"] } [dev-dependencies] paste = "0.1.18" From bed45602a71e02b2447ed4374c75c38742697599 Mon Sep 17 00:00:00 2001 From: nicoo Date: Wed, 10 Nov 2021 15:26:36 +0100 Subject: [PATCH 105/131] factor/Cargo.toml: Document feature pending a MinRustV bump --- src/uu/factor/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/factor/Cargo.toml b/src/uu/factor/Cargo.toml index 1142560d9..f79afeecf 100644 --- a/src/uu/factor/Cargo.toml +++ b/src/uu/factor/Cargo.toml @@ -19,7 +19,7 @@ clap = { version = "2.33", features = ["wrap_help"] } coz = { version = "0.1.3", optional = true } num-traits = "0.2.13" # Needs at least version 0.2.13 for "OverflowingAdd" rand = { version = "0.7", features = ["small_rng"] } -smallvec = "1.7" +smallvec = "1.7" # TODO(nicoo): Use `union` feature, requires Rust 1.49 or later. uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" } uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" } From 670ed6324b6c8e7c2cde005e5c304ee76e1ee56c Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Fri, 12 Nov 2021 18:29:08 -0300 Subject: [PATCH 106/131] chmod: use UResult --- src/uu/chmod/src/chmod.rs | 100 ++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index 68c55b4cb..09ed3cda6 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -7,19 +7,17 @@ // spell-checker:ignore (ToDO) Chmoder cmode fmode fperm fref ugoa RFILE RFILE's -#[macro_use] -extern crate uucore; - use clap::{crate_version, App, Arg}; use std::fs; use std::os::unix::fs::{MetadataExt, PermissionsExt}; use std::path::Path; use uucore::display::Quotable; +use uucore::error::{ExitCode, UResult, USimpleError, UUsageError}; use uucore::fs::display_permissions_unix; use uucore::libc::mode_t; #[cfg(not(windows))] use uucore::mode; -use uucore::InvalidEncodingHandling; +use uucore::{show_error, InvalidEncodingHandling}; use walkdir::WalkDir; static ABOUT: &str = "Change the mode of each FILE to MODE. @@ -50,7 +48,8 @@ fn get_long_usage() -> String { String::from("Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.") } -pub fn uumain(args: impl uucore::Args) -> i32 { +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { let mut args = args .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); @@ -72,12 +71,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let verbose = matches.is_present(options::VERBOSE); let preserve_root = matches.is_present(options::PRESERVE_ROOT); let recursive = matches.is_present(options::RECURSIVE); - let fmode = matches - .value_of(options::REFERENCE) - .and_then(|fref| match fs::metadata(fref) { + let fmode = match matches.value_of(options::REFERENCE) { + Some(fref) => match fs::metadata(fref) { Ok(meta) => Some(meta.mode()), - Err(err) => crash!(1, "cannot stat attributes of {}: {}", fref.quote(), err), - }); + Err(err) => { + return Err(USimpleError::new( + 1, + format!("cannot stat attributes of {}: {}", fref.quote(), err), + )) + } + }, + None => None, + }; let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required let cmode = if mode_had_minus_prefix { // clap parsing is finished, now put prefix back @@ -100,7 +105,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { }; if files.is_empty() { - crash!(1, "missing operand"); + return Err(UUsageError::new(1, "missing operand".to_string())); } let chmoder = Chmoder { @@ -112,12 +117,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { fmode, cmode, }; - match chmoder.chmod(files) { - Ok(()) => {} - Err(e) => return e, - } - 0 + chmoder.chmod(files) } pub fn uu_app() -> App<'static, 'static> { @@ -191,7 +192,7 @@ struct Chmoder { } impl Chmoder { - fn chmod(&self, files: Vec) -> Result<(), i32> { + fn chmod(&self, files: Vec) -> UResult<()> { let mut r = Ok(()); for filename in &files { @@ -204,22 +205,30 @@ impl Chmoder { filename.quote() ); if !self.quiet { - show_error!("cannot operate on dangling symlink {}", filename.quote()); + return Err(USimpleError::new( + 1, + format!("cannot operate on dangling symlink {}", filename.quote()), + )); } } else if !self.quiet { - show_error!( - "cannot access {}: No such file or directory", - filename.quote() - ); + return Err(USimpleError::new( + 1, + format!( + "cannot access {}: No such file or directory", + filename.quote() + ), + )); } - return Err(1); + return Err(ExitCode::new(1)); } if self.recursive && self.preserve_root && filename == "/" { - show_error!( - "it is dangerous to operate recursively on {}\nuse --no-preserve-root to override this failsafe", - filename.quote() - ); - return Err(1); + return Err(USimpleError::new( + 1, + format!( + "it is dangerous to operate recursively on {}\nuse --no-preserve-root to override this failsafe", + filename.quote() + ) + )); } if !self.recursive { r = self.chmod_file(file).and(r); @@ -234,14 +243,14 @@ impl Chmoder { } #[cfg(windows)] - fn chmod_file(&self, file: &Path) -> Result<(), i32> { + fn chmod_file(&self, file: &Path) -> UResult<()> { // chmod is useless on Windows // it doesn't set any permissions at all // instead it just sets the readonly attribute on the file - Err(0) + Ok(()) } #[cfg(unix)] - fn chmod_file(&self, file: &Path) -> Result<(), i32> { + fn chmod_file(&self, file: &Path) -> UResult<()> { use uucore::mode::get_umask; let fperm = match fs::metadata(file) { @@ -258,11 +267,13 @@ impl Chmoder { } else if err.kind() == std::io::ErrorKind::PermissionDenied { // These two filenames would normally be conditionally // quoted, but GNU's tests expect them to always be quoted - show_error!("{}: Permission denied", file.quote()); + return Err(USimpleError::new( + 1, + format!("{}: Permission denied", file.quote()), + )); } else { - show_error!("{}: {}", file.quote(), err); + return Err(USimpleError::new(1, format!("{}: {}", file.quote(), err))); } - return Err(1); } }; match self.fmode { @@ -296,22 +307,25 @@ impl Chmoder { } Err(f) => { if !self.quiet { - show_error!("{}", f); + return Err(USimpleError::new(1, f)); + } else { + return Err(ExitCode::new(1)); } - return Err(1); } } } self.change_file(fperm, new_mode, file)?; // if a permission would have been removed if umask was 0, but it wasn't because umask was not 0, print an error and fail if (new_mode & !naively_expected_new_mode) != 0 { - show_error!( - "{}: new permissions are {}, not {}", - file.maybe_quote(), - display_permissions_unix(new_mode as mode_t, false), - display_permissions_unix(naively_expected_new_mode as mode_t, false) - ); - return Err(1); + return Err(USimpleError::new( + 1, + format!( + "{}: new permissions are {}, not {}", + file.maybe_quote(), + display_permissions_unix(new_mode as mode_t, false), + display_permissions_unix(naively_expected_new_mode as mode_t, false) + ), + )); } } } From fed596a23b04a9d3d32f5dfa9530e4ee1a41fe79 Mon Sep 17 00:00:00 2001 From: Thomas Queiroz Date: Fri, 12 Nov 2021 18:29:28 -0300 Subject: [PATCH 107/131] tests/chmod: change normal error to usage error --- tests/by-util/test_chmod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_chmod.rs b/tests/by-util/test_chmod.rs index c8348d491..1b0b7131b 100644 --- a/tests/by-util/test_chmod.rs +++ b/tests/by-util/test_chmod.rs @@ -541,7 +541,7 @@ fn test_no_operands() { .arg("777") .fails() .code_is(1) - .stderr_is("chmod: missing operand"); + .usage_error("missing operand"); } #[test] From 740d8e9bc5a70aa44d0796f9ff26d9c7b910d0a4 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 13 Nov 2021 10:58:43 -0600 Subject: [PATCH 108/131] docs/factor ~ (BENCHMARKING.md) fix formatting, returning missing newlines --- src/uu/factor/BENCHMARKING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/uu/factor/BENCHMARKING.md b/src/uu/factor/BENCHMARKING.md index 6bf9cbf90..0f9afaff0 100644 --- a/src/uu/factor/BENCHMARKING.md +++ b/src/uu/factor/BENCHMARKING.md @@ -44,7 +44,8 @@ on Daniel Lemire's [*Microbenchmarking calls for idealized conditions*][lemire], which I recommend reading if you want to add benchmarks to `factor`. 1. Select a small, self-contained, deterministic component - `gcd` and `table::factor` are good example of such: + (`gcd` and `table::factor` are good examples): + - no I/O or access to external data structures ; - no call into other components ; - behavior is deterministic: no RNG, no concurrency, ... ; @@ -53,16 +54,19 @@ which I recommend reading if you want to add benchmarks to `factor`. maximizing the numbers of samples we can take in a given time. 2. Benchmarks are immutable (once merged in `uutils`) + Modifying a benchmark means previously-collected values cannot meaningfully be compared, silently giving nonsensical results. If you must modify an existing benchmark, rename it. 3. Test common cases + We are interested in overall performance, rather than specific edge-cases; use **reproducibly-randomized inputs**, sampling from either all possible input values or some subset of interest. 4. Use [`criterion`], `criterion::black_box`, ... + `criterion` isn't perfect, but it is also much better than ad-hoc solutions in each benchmark. From 363453f5e4b135c337691dd43a248990fd323876 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sat, 29 May 2021 23:48:15 -0500 Subject: [PATCH 109/131] tests ~ (factor) refactor `divisor()` to return quickcheck::TestResult - return standard quickcheck results - drop `a == 0 && b == 0` from test domain via TestResult::discard() - avoid divide by zero panics - ref: #1589 --- src/uu/factor/src/numeric/gcd.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/uu/factor/src/numeric/gcd.rs b/src/uu/factor/src/numeric/gcd.rs index 004ef1515..78197c722 100644 --- a/src/uu/factor/src/numeric/gcd.rs +++ b/src/uu/factor/src/numeric/gcd.rs @@ -52,7 +52,7 @@ pub fn gcd(mut u: u64, mut v: u64) -> u64 { #[cfg(test)] mod tests { use super::*; - use quickcheck::quickcheck; + use quickcheck::{quickcheck, TestResult}; quickcheck! { fn euclidean(a: u64, b: u64) -> bool { @@ -76,13 +76,12 @@ mod tests { gcd(0, a) == a } - fn divisor(a: u64, b: u64) -> () { + fn divisor(a: u64, b: u64) -> TestResult { // Test that gcd(a, b) divides a and b, unless a == b == 0 - if a == 0 && b == 0 { return; } + if a == 0 && b == 0 { return TestResult::discard(); } // restrict test domain to !(a == b == 0) let g = gcd(a, b); - assert_eq!(a % g, 0); - assert_eq!(b % g, 0); + TestResult::from_bool( g != 0 && a % g == 0 && b % g == 0 ) } fn commutative(a: u64, b: u64) -> bool { From c0af58881e66c68c2025f7c8f742b3acfd6f3719 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 16 Nov 2021 10:09:42 +0100 Subject: [PATCH 110/131] remove some unused codecov declarations --- .github/workflows/CICD.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 0372336a7..cab39a872 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -615,11 +615,6 @@ jobs: # staging directory STAGING='_staging' outputs STAGING - ## # check for CODECOV_TOKEN availability (work-around for inaccessible 'secrets' object for 'if'; see ) - ## # note: CODECOV_TOKEN / HAS_CODECOV_TOKEN is not needed for public repositories when using AppVeyor, Azure Pipelines, CircleCI, GitHub Actions, Travis (see ) - ## unset HAS_CODECOV_TOKEN - ## if [ -n $CODECOV_TOKEN ]; then HAS_CODECOV_TOKEN='true' ; fi - ## outputs HAS_CODECOV_TOKEN # target-specific options # * CARGO_FEATURES_OPTION CARGO_FEATURES_OPTION='--all-features' ; ## default to '--all-features' for code coverage From fc851e036b863099615369a12350c409cbfe4145 Mon Sep 17 00:00:00 2001 From: Smicry Date: Sat, 20 Nov 2021 04:37:47 +0800 Subject: [PATCH 111/131] Implement tail - (#2747) And add obsolete_syntax test --- src/uu/tail/src/parse.rs | 161 ++++++++++++++++++++++++++++++ src/uu/tail/src/tail.rs | 197 +++++++++++++++++++++++-------------- tests/by-util/test_tail.rs | 30 ++++++ 3 files changed, 313 insertions(+), 75 deletions(-) create mode 100644 src/uu/tail/src/parse.rs diff --git a/src/uu/tail/src/parse.rs b/src/uu/tail/src/parse.rs new file mode 100644 index 000000000..929681811 --- /dev/null +++ b/src/uu/tail/src/parse.rs @@ -0,0 +1,161 @@ +// * This file is part of the uutils coreutils package. +// * +// * For the full copyright and license information, please view the LICENSE +// * file that was distributed with this source code. + +use std::ffi::OsString; + +#[derive(PartialEq, Debug)] +pub enum ParseError { + Syntax, + Overflow, +} +/// Parses obsolete syntax +/// tail -NUM[kmzv] // spell-checker:disable-line +pub fn parse_obsolete(src: &str) -> Option, ParseError>> { + let mut chars = src.char_indices(); + if let Some((_, '-')) = chars.next() { + let mut num_end = 0usize; + let mut has_num = false; + let mut last_char = 0 as char; + for (n, c) in &mut chars { + if c.is_numeric() { + has_num = true; + num_end = n; + } else { + last_char = c; + break; + } + } + if has_num { + match src[1..=num_end].parse::() { + Ok(num) => { + let mut quiet = false; + let mut verbose = false; + let mut zero_terminated = false; + let mut multiplier = None; + let mut c = last_char; + loop { + // not that here, we only match lower case 'k', 'c', and 'm' + match c { + // we want to preserve order + // this also saves us 1 heap allocation + 'q' => { + quiet = true; + verbose = false + } + 'v' => { + verbose = true; + quiet = false + } + 'z' => zero_terminated = true, + 'c' => multiplier = Some(1), + 'b' => multiplier = Some(512), + 'k' => multiplier = Some(1024), + 'm' => multiplier = Some(1024 * 1024), + '\0' => {} + _ => return Some(Err(ParseError::Syntax)), + } + if let Some((_, next)) = chars.next() { + c = next + } else { + break; + } + } + let mut options = Vec::new(); + if quiet { + options.push(OsString::from("-q")) + } + if verbose { + options.push(OsString::from("-v")) + } + if zero_terminated { + options.push(OsString::from("-z")) + } + if let Some(n) = multiplier { + options.push(OsString::from("-c")); + let num = match num.checked_mul(n) { + Some(n) => n, + None => return Some(Err(ParseError::Overflow)), + }; + options.push(OsString::from(format!("{}", num))); + } else { + options.push(OsString::from("-n")); + options.push(OsString::from(format!("{}", num))); + } + Some(Ok(options.into_iter())) + } + Err(_) => Some(Err(ParseError::Overflow)), + } + } else { + None + } + } else { + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + fn obsolete(src: &str) -> Option, ParseError>> { + let r = parse_obsolete(src); + match r { + Some(s) => match s { + Ok(v) => Some(Ok(v.map(|s| s.to_str().unwrap().to_owned()).collect())), + Err(e) => Some(Err(e)), + }, + None => None, + } + } + fn obsolete_result(src: &[&str]) -> Option, ParseError>> { + Some(Ok(src.iter().map(|s| s.to_string()).collect())) + } + #[test] + fn test_parse_numbers_obsolete() { + assert_eq!(obsolete("-5"), obsolete_result(&["-n", "5"])); + assert_eq!(obsolete("-100"), obsolete_result(&["-n", "100"])); + assert_eq!(obsolete("-5m"), obsolete_result(&["-c", "5242880"])); + assert_eq!(obsolete("-1k"), obsolete_result(&["-c", "1024"])); + assert_eq!(obsolete("-2b"), obsolete_result(&["-c", "1024"])); + assert_eq!(obsolete("-1mmk"), obsolete_result(&["-c", "1024"])); + assert_eq!(obsolete("-1vz"), obsolete_result(&["-v", "-z", "-n", "1"])); + assert_eq!( + obsolete("-1vzqvq"), // spell-checker:disable-line + obsolete_result(&["-q", "-z", "-n", "1"]) + ); + assert_eq!(obsolete("-1vzc"), obsolete_result(&["-v", "-z", "-c", "1"])); + assert_eq!( + obsolete("-105kzm"), + obsolete_result(&["-z", "-c", "110100480"]) + ); + } + #[test] + fn test_parse_errors_obsolete() { + assert_eq!(obsolete("-5n"), Some(Err(ParseError::Syntax))); + assert_eq!(obsolete("-5c5"), Some(Err(ParseError::Syntax))); + } + #[test] + fn test_parse_obsolete_no_match() { + assert_eq!(obsolete("-k"), None); + assert_eq!(obsolete("asd"), None); + } + #[test] + #[cfg(target_pointer_width = "64")] + fn test_parse_obsolete_overflow_x64() { + assert_eq!( + obsolete("-1000000000000000m"), + Some(Err(ParseError::Overflow)) + ); + assert_eq!( + obsolete("-10000000000000000000000"), + Some(Err(ParseError::Overflow)) + ); + } + #[test] + #[cfg(target_pointer_width = "32")] + fn test_parse_obsolete_overflow_x32() { + assert_eq!(obsolete("-42949672960"), Some(Err(ParseError::Overflow))); + assert_eq!(obsolete("-42949672k"), Some(Err(ParseError::Overflow))); + } +} diff --git a/src/uu/tail/src/tail.rs b/src/uu/tail/src/tail.rs index eaf7bf8bf..d83f02724 100644 --- a/src/uu/tail/src/tail.rs +++ b/src/uu/tail/src/tail.rs @@ -16,17 +16,21 @@ extern crate clap; extern crate uucore; mod chunks; +mod parse; mod platform; use chunks::ReverseChunks; use clap::{App, Arg}; use std::collections::VecDeque; +use std::ffi::OsString; use std::fmt; use std::fs::{File, Metadata}; use std::io::{stdin, stdout, BufRead, BufReader, Read, Seek, SeekFrom, Write}; use std::path::Path; use std::thread::sleep; use std::time::Duration; +use uucore::display::Quotable; +use uucore::error::{UResult, USimpleError}; use uucore::parse_size::{parse_size, ParseSizeError}; use uucore::ringbuffer::RingBuffer; @@ -58,105 +62,122 @@ pub mod options { pub static ARG_FILES: &str = "files"; } +#[derive(Debug)] enum FilterMode { Bytes(usize), Lines(usize, u8), // (number of lines, delimiter) } +impl Default for FilterMode { + fn default() -> Self { + FilterMode::Lines(10, b'\n') + } +} + +#[derive(Debug, Default)] struct Settings { + quiet: bool, + verbose: bool, mode: FilterMode, sleep_msec: u32, beginning: bool, follow: bool, pid: platform::Pid, + files: Vec, } -impl Default for Settings { - fn default() -> Settings { - Settings { - mode: FilterMode::Lines(10, b'\n'), +impl Settings { + pub fn get_from(args: impl uucore::Args) -> Result { + let matches = uu_app().get_matches_from(arg_iterate(args)?); + + let mut settings: Settings = Settings { sleep_msec: 1000, - beginning: false, - follow: false, - pid: 0, + follow: matches.is_present(options::FOLLOW), + ..Default::default() + }; + + if settings.follow { + if let Some(n) = matches.value_of(options::SLEEP_INT) { + let parsed: Option = n.parse().ok(); + if let Some(m) = parsed { + settings.sleep_msec = m * 1000 + } + } } + + if let Some(pid_str) = matches.value_of(options::PID) { + if let Ok(pid) = pid_str.parse() { + settings.pid = pid; + if pid != 0 { + if !settings.follow { + show_warning!("PID ignored; --pid=PID is useful only when following"); + } + + if !platform::supports_pid_checks(pid) { + show_warning!("--pid=PID is not supported on this system"); + settings.pid = 0; + } + } + } + } + + let mode_and_beginning = if let Some(arg) = matches.value_of(options::BYTES) { + match parse_num(arg) { + Ok((n, beginning)) => (FilterMode::Bytes(n), beginning), + Err(e) => return Err(format!("invalid number of bytes: {}", e)), + } + } else if let Some(arg) = matches.value_of(options::LINES) { + match parse_num(arg) { + Ok((n, beginning)) => (FilterMode::Lines(n, b'\n'), beginning), + Err(e) => return Err(format!("invalid number of lines: {}", e)), + } + } else { + (FilterMode::Lines(10, b'\n'), false) + }; + settings.mode = mode_and_beginning.0; + settings.beginning = mode_and_beginning.1; + + if matches.is_present(options::ZERO_TERM) { + if let FilterMode::Lines(count, _) = settings.mode { + settings.mode = FilterMode::Lines(count, 0); + } + } + + settings.verbose = matches.is_present(options::verbosity::VERBOSE); + settings.quiet = matches.is_present(options::verbosity::QUIET); + + settings.files = match matches.values_of(options::ARG_FILES) { + Some(v) => v.map(|s| s.to_owned()).collect(), + None => vec!["-".to_owned()], + }; + + Ok(settings) } } #[allow(clippy::cognitive_complexity)] -pub fn uumain(args: impl uucore::Args) -> i32 { - let mut settings: Settings = Default::default(); - - let app = uu_app(); - - let matches = app.get_matches_from(args); - - settings.follow = matches.is_present(options::FOLLOW); - if settings.follow { - if let Some(n) = matches.value_of(options::SLEEP_INT) { - let parsed: Option = n.parse().ok(); - if let Some(m) = parsed { - settings.sleep_msec = m * 1000 - } +#[uucore_procs::gen_uumain] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let args = match Settings::get_from(args) { + Ok(o) => o, + Err(s) => { + return Err(USimpleError::new(1, s)); } - } - - if let Some(pid_str) = matches.value_of(options::PID) { - if let Ok(pid) = pid_str.parse() { - settings.pid = pid; - if pid != 0 { - if !settings.follow { - show_warning!("PID ignored; --pid=PID is useful only when following"); - } - - if !platform::supports_pid_checks(pid) { - show_warning!("--pid=PID is not supported on this system"); - settings.pid = 0; - } - } - } - } - - let mode_and_beginning = if let Some(arg) = matches.value_of(options::BYTES) { - match parse_num(arg) { - Ok((n, beginning)) => (FilterMode::Bytes(n), beginning), - Err(e) => crash!(1, "invalid number of bytes: {}", e.to_string()), - } - } else if let Some(arg) = matches.value_of(options::LINES) { - match parse_num(arg) { - Ok((n, beginning)) => (FilterMode::Lines(n, b'\n'), beginning), - Err(e) => crash!(1, "invalid number of lines: {}", e.to_string()), - } - } else { - (FilterMode::Lines(10, b'\n'), false) }; - settings.mode = mode_and_beginning.0; - settings.beginning = mode_and_beginning.1; + uu_tail(&args) +} - if matches.is_present(options::ZERO_TERM) { - if let FilterMode::Lines(count, _) = settings.mode { - settings.mode = FilterMode::Lines(count, 0); - } - } - - let verbose = matches.is_present(options::verbosity::VERBOSE); - let quiet = matches.is_present(options::verbosity::QUIET); - - let files: Vec = matches - .values_of(options::ARG_FILES) - .map(|v| v.map(ToString::to_string).collect()) - .unwrap_or_else(|| vec![String::from("-")]); - - let multiple = files.len() > 1; +fn uu_tail(settings: &Settings) -> UResult<()> { + let multiple = settings.files.len() > 1; let mut first_header = true; let mut readers: Vec<(Box, &String)> = Vec::new(); #[cfg(unix)] let stdin_string = String::from("standard input"); - for filename in &files { + for filename in &settings.files { let use_stdin = filename.as_str() == "-"; - if (multiple || verbose) && !quiet { + if (multiple || settings.verbose) && !settings.quiet { if !first_header { println!(); } @@ -170,7 +191,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if use_stdin { let mut reader = BufReader::new(stdin()); - unbounded_tail(&mut reader, &settings); + unbounded_tail(&mut reader, settings); // Don't follow stdin since there are no checks for pipes/FIFOs // @@ -202,14 +223,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let mut file = File::open(&path).unwrap(); let md = file.metadata().unwrap(); if is_seekable(&mut file) && get_block_size(&md) > 0 { - bounded_tail(&mut file, &settings); + bounded_tail(&mut file, settings); if settings.follow { let reader = BufReader::new(file); readers.push((Box::new(reader), filename)); } } else { let mut reader = BufReader::new(file); - unbounded_tail(&mut reader, &settings); + unbounded_tail(&mut reader, settings); if settings.follow { readers.push((Box::new(reader), filename)); } @@ -218,10 +239,36 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } if settings.follow { - follow(&mut readers[..], &settings); + follow(&mut readers[..], settings); } - 0 + Ok(()) +} + +fn arg_iterate<'a>( + mut args: impl uucore::Args + 'a, +) -> Result + 'a>, String> { + // argv[0] is always present + let first = args.next().unwrap(); + if let Some(second) = args.next() { + if let Some(s) = second.to_str() { + match parse::parse_obsolete(s) { + Some(Ok(iter)) => Ok(Box::new(vec![first].into_iter().chain(iter).chain(args))), + Some(Err(e)) => match e { + parse::ParseError::Syntax => Err(format!("bad argument format: {}", s.quote())), + parse::ParseError::Overflow => Err(format!( + "invalid argument: {} Value too large for defined datatype", + s.quote() + )), + }, + None => Ok(Box::new(vec![first, second].into_iter().chain(args))), + } + } else { + Err("bad argument encoding".to_owned()) + } + } else { + Ok(Box::new(vec![first].into_iter())) + } } pub fn uu_app() -> App<'static, 'static> { diff --git a/tests/by-util/test_tail.rs b/tests/by-util/test_tail.rs index 26d8106f0..a020f6235 100644 --- a/tests/by-util/test_tail.rs +++ b/tests/by-util/test_tail.rs @@ -358,6 +358,36 @@ fn test_positive_lines() { .stdout_is("c\nd\ne\n"); } +/// Test for reading all but the first NUM lines: `tail -3`. +#[test] +fn test_obsolete_syntax_positive_lines() { + new_ucmd!() + .args(&["-3"]) + .pipe_in("a\nb\nc\nd\ne\n") + .succeeds() + .stdout_is("c\nd\ne\n"); +} + +/// Test for reading all but the first NUM lines: `tail -n -10`. +#[test] +fn test_small_file() { + new_ucmd!() + .args(&["-n -10"]) + .pipe_in("a\nb\nc\nd\ne\n") + .succeeds() + .stdout_is("a\nb\nc\nd\ne\n"); +} + +/// Test for reading all but the first NUM lines: `tail -10`. +#[test] +fn test_obsolete_syntax_small_file() { + new_ucmd!() + .args(&["-10"]) + .pipe_in("a\nb\nc\nd\ne\n") + .succeeds() + .stdout_is("a\nb\nc\nd\ne\n"); +} + /// Test for reading all lines, specified by `tail -n +0`. #[test] fn test_positive_zero_lines() { From 36c6ccde0d5c22626eeebd49cb7185e4f3b61718 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Tue, 16 Nov 2021 22:22:08 -0600 Subject: [PATCH 112/131] maint/dev ~ prefer less visible tool configuration files --- clippy.toml => .clippy.toml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clippy.toml => .clippy.toml (100%) diff --git a/clippy.toml b/.clippy.toml similarity index 100% rename from clippy.toml rename to .clippy.toml From c951806e70ff579e67e3eb2d617fc5309ac13865 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Wed, 10 Nov 2021 11:33:29 -0600 Subject: [PATCH 113/131] maint/CICD ~ (GHA) fix `cargo clippy` lint - fixes conversion of new `cargo clippy` output style to GHA annotations ## [why] `cargo clippy` output formatting changed, using relative instead of absolute paths. --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index cab39a872..4363582a9 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -140,7 +140,7 @@ jobs: run: | ## `clippy` lint testing # * convert any warnings to GHA UI annotations; ref: - S=$(cargo +nightly clippy --all-targets ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+${PWD//\//\\/}\/(.*):([0-9]+):([0-9]+).*$/::error file=\2,line=\3,col=\4::ERROR: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; exit 1 ; } + S=$(cargo +nightly clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::error file=\2,line=\3,col=\4::ERROR: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; exit 1 ; } code_spellcheck: name: Style/spelling From 37a3c68f3a194b64aab2bfffd4604a8348982efa Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Tue, 16 Nov 2021 21:31:15 -0600 Subject: [PATCH 114/131] maint/CICD ~ (GHA) add style fault configurability (fail vs warn) - add individual job-step control for 'style' step faults (build failure vs only a warning) --- .github/workflows/CICD.yml | 235 +++++++++++++++++++++++-------------- 1 file changed, 145 insertions(+), 90 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 4363582a9..25e1fcf26 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -14,47 +14,17 @@ env: PROJECT_DESC: "Core universal (cross-platform) utilities" PROJECT_AUTH: "uutils" RUST_MIN_SRV: "1.47.0" ## MSRV v1.47.0 + # * style job configuration + STYLE_FAIL_ON_FAULT: true ## fail the build if a style job contains a fault (error or warning); boolean on: [push, pull_request] jobs: - code_deps: - name: Style/dependencies - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { os: ubuntu-latest , features: feat_os_unix } - steps: - - uses: actions/checkout@v2 - - name: Initialize workflow variables - id: vars - shell: bash - run: | - ## VARs setup - outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } - # target-specific options - # * CARGO_FEATURES_OPTION - CARGO_FEATURES_OPTION='' ; - if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi - outputs CARGO_FEATURES_OPTION - - name: Install `rust` toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - default: true - profile: minimal # minimal component installation (ie, no documentation) - - name: "`cargo update` testing" - shell: bash - run: | - ## `cargo update` testing - # * convert any warnings to GHA UI annotations; ref: - cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::'Cargo.lock' file requires update (use \`cargo +${{ env.RUST_MIN_SRV }} update\`)" ; exit 1 ; } - - code_format: + style_format: name: Style/format runs-on: ${{ matrix.job.os }} + # env: + # STYLE_FAIL_ON_FAULT: false # overrides workflow default strategy: fail-fast: false matrix: @@ -68,6 +38,12 @@ jobs: run: | ## VARs setup outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + # failure mode + unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in + ''|0|f|false|n|no|off) FAULT_TYPE=warning ;; + *) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;; + esac; + outputs FAIL_ON_FAULT FAULT_TYPE # target-specific options # * CARGO_FEATURES_OPTION CARGO_FEATURES_OPTION='' ; @@ -80,23 +56,36 @@ jobs: default: true profile: minimal # minimal component installation (ie, no documentation) components: rustfmt - - name: "`fmt` testing" + - name: "`cargo fmt` testing" shell: bash run: | - ## `fmt` testing - # * convert any warnings to GHA UI annotations; ref: - S=$(cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::error file=\1,line=\2::ERROR: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt \"\1\"\`)/p" ; exit 1 ; } - - name: "`fmt` testing of tests" + ## `cargo fmt` testing + unset fault + fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" + fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') + # * convert any errors/warnings to GHA UI annotations; ref: + S=$(cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::${fault_type} file=\1,line=\2::${fault_prefix}: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt -- \"\1\"\`)/p" ; fault=true ; } + if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi + - name: "`cargo fmt` testing of integration tests" if: success() || failure() # run regardless of prior step success/failure shell: bash run: | - ## `fmt` testing of tests - # * convert any warnings to GHA UI annotations; ref: - S=$(find tests -name "*.rs" -print0 | xargs -0 cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::error file=\1,line=\2::ERROR: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt \"\1\"\`)/p" ; exit 1 ; } + ## `cargo fmt` testing of integration tests + unset fault + fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" + fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') + # 'tests' is the standard/usual integration test directory + if [ -d tests ]; then + # * convert any errors/warnings to GHA UI annotations; ref: + S=$(find tests -name "*.rs" -print0 | xargs -0 cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::${fault_type} file=\1,line=\2::${fault_prefix}: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt \"\1\"\`)/p" ; fault=true ; } + fi + if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi - code_lint: + style_lint: name: Style/lint runs-on: ${{ matrix.job.os }} + # env: + # STYLE_FAIL_ON_FAULT: false # overrides workflow default strategy: fail-fast: false matrix: @@ -106,51 +95,75 @@ jobs: - { os: windows-latest , features: feat_os_windows } steps: - uses: actions/checkout@v2 - - name: Install/setup prerequisites - shell: bash - run: | - case '${{ matrix.job.os }}' in - macos-latest) brew install coreutils ;; # needed for show-utils.sh - esac - name: Initialize workflow variables id: vars shell: bash run: | ## VARs setup outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + # failure mode + unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in + ''|0|f|false|n|no|off) FAULT_TYPE=warning ;; + *) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;; + esac; + outputs FAIL_ON_FAULT FAULT_TYPE # target-specific options # * CARGO_FEATURES_OPTION CARGO_FEATURES_OPTION='--all-features' ; - if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features ${{ matrix.job.features }}' ; fi + if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi outputs CARGO_FEATURES_OPTION # * determine sub-crate utility list UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})" echo UTILITY_LIST=${UTILITY_LIST} CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)" outputs CARGO_UTILITY_LIST_OPTIONS + - name: Install/setup prerequisites + shell: bash + run: | + case '${{ matrix.job.os }}' in + macos-latest) brew install coreutils ;; # needed for show-utils.sh + esac - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 with: - toolchain: nightly + toolchain: stable default: true profile: minimal # minimal component installation (ie, no documentation) components: clippy - - name: "`clippy` lint testing" + - name: "`cargo clippy` lint testing" shell: bash run: | - ## `clippy` lint testing + ## `cargo clippy` lint testing + unset fault + fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" + fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') # * convert any warnings to GHA UI annotations; ref: - S=$(cargo +nightly clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::error file=\2,line=\3,col=\4::ERROR: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; exit 1 ; } + S=$(cargo clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; } + if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi - code_spellcheck: + style_spellcheck: name: Style/spelling runs-on: ${{ matrix.job.os }} + # env: + # STYLE_FAIL_ON_FAULT: false # overrides workflow default strategy: matrix: job: - { os: ubuntu-latest } steps: - uses: actions/checkout@v2 + - name: Initialize workflow variables + id: vars + shell: bash + run: | + ## VARs setup + outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + # failure mode + unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in + ''|0|f|false|n|no|off) FAULT_TYPE=warning ;; + *) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;; + esac; + outputs FAIL_ON_FAULT FAULT_TYPE - name: Install/setup prerequisites shell: bash run: | @@ -160,10 +173,19 @@ jobs: shell: bash run: | ## Run `cspell` - cspell --config .vscode/cSpell.json --no-summary --no-progress "**/*" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::error file=\1,line=\2,col=\3::ERROR: \4 (file:'\1', line:\2)/p" + unset fault + fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" + fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') + # * find cspell configuration + cfg_files=($(shopt -s nullglob ; echo {.vscode,.}/{,.}c[sS]pell{.json,.config{.js,.cjs,.json,.yaml,.yml},.yaml,.yml} ;)) + cfg_file=${cfg_files[0]} + unset CSPELL_CFG_OPTION ; if [ -n "$cfg_file" ]; then CSPELL_CFG_OPTION="--config \"$cfg_file\"" ; fi + # * `cspell` + S=$(cspell ${CSPELL_CFG_OPTION} --no-summary --no-progress "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; } + if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi min_version: - name: MinRustV # Minimum supported rust version + name: MinRustV # Minimum supported rust version (aka, MinSRV or MSRV) runs-on: ${{ matrix.job.os }} strategy: matrix: @@ -171,6 +193,17 @@ jobs: - { os: ubuntu-latest , features: feat_os_unix } steps: - uses: actions/checkout@v2 + - name: Initialize workflow variables + id: vars + shell: bash + run: | + ## VARs setup + outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + # target-specific options + # * CARGO_FEATURES_OPTION + unset CARGO_FEATURES_OPTION + if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi + outputs CARGO_FEATURES_OPTION - name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }}) uses: actions-rs/toolchain@v1 with: @@ -208,16 +241,39 @@ jobs: cargo-tree tree -V # dependencies echo "## dependency list" - cargo fetch --locked --quiet ## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors - RUSTUP_TOOLCHAIN=stable cargo-tree tree --locked --all --no-dev-dependencies --no-indent --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique + RUSTUP_TOOLCHAIN=stable cargo fetch --locked --quiet + RUSTUP_TOOLCHAIN=stable cargo-tree tree --all --locked --no-dev-dependencies --no-indent ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} | grep -vE "$PWD" | sort --unique - name: Test uses: actions-rs/cargo@v1 with: command: test - args: --features "feat_os_unix" -p uucore -p coreutils + args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -p uucore -p coreutils env: - RUSTFLAGS: '-Awarnings' + RUSTFLAGS: "-Awarnings" + + deps: + name: Dependencies + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { os: ubuntu-latest , features: feat_os_unix } + steps: + - uses: actions/checkout@v2 + - name: Install `rust` toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + default: true + profile: minimal # minimal component installation (ie, no documentation) + - name: "`cargo update` testing" + shell: bash + run: | + ## `cargo update` testing + # * convert any errors/warnings to GHA UI annotations; ref: + cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::'Cargo.lock' file requires update (use \`cargo +${{ env.RUST_MIN_SRV }} update\`)" ; exit 1 ; } build_makefile: name: Build/Makefile @@ -256,8 +312,8 @@ jobs: fail-fast: false matrix: job: - # { os, target, cargo-options, features, use-cross, toolchain } - - { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf , features: feat_os_unix_gnueabihf , use-cross: use-cross } + # { os , target , cargo-options , features , use-cross , toolchain } + - { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf, features: feat_os_unix_gnueabihf, use-cross: use-cross, } - { os: ubuntu-latest , target: aarch64-unknown-linux-gnu , features: feat_os_unix_gnueabihf , use-cross: use-cross } - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } # - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_selinux , use-cross: use-cross } @@ -270,21 +326,10 @@ jobs: - { os: macos-latest , target: x86_64-apple-darwin , features: feat_os_macos } - { os: windows-latest , target: i686-pc-windows-gnu , features: feat_os_windows } - { os: windows-latest , target: i686-pc-windows-msvc , features: feat_os_windows } - - { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly + - { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly - { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows } steps: - uses: actions/checkout@v2 - - name: Install/setup prerequisites - shell: bash - run: | - ## Install/setup prerequisites - case '${{ matrix.job.target }}' in - arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; - aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; - esac - case '${{ matrix.job.os }}' in - macos-latest) brew install coreutils ;; # needed for testing - esac - name: Initialize workflow variables id: vars shell: bash @@ -402,6 +447,17 @@ jobs: echo UTILITY_LIST=${UTILITY_LIST} CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)" outputs CARGO_UTILITY_LIST_OPTIONS + - name: Install/setup prerequisites + shell: bash + run: | + ## Install/setup prerequisites + case '${{ matrix.job.target }}' in + arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; + aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; + esac + case '${{ matrix.job.os }}' in + macos-latest) brew install coreutils ;; # needed for testing + esac - name: Install `cargo-tree` # for dependency information uses: actions-rs/install@v0.1 with: @@ -576,7 +632,6 @@ jobs: cargo clean EOF - coverage: name: Code Coverage runs-on: ${{ matrix.job.os }} @@ -645,10 +700,10 @@ jobs: command: test args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast -p uucore env: - CARGO_INCREMENTAL: '0' - RUSTC_WRAPPER: '' - RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort' - RUSTDOCFLAGS: '-Cpanic=abort' + CARGO_INCREMENTAL: "0" + RUSTC_WRAPPER: "" + RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" + RUSTDOCFLAGS: "-Cpanic=abort" # RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }} - name: Test uses: actions-rs/cargo@v1 @@ -656,10 +711,10 @@ jobs: command: test args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast env: - CARGO_INCREMENTAL: '0' - RUSTC_WRAPPER: '' - RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort' - RUSTDOCFLAGS: '-Cpanic=abort' + CARGO_INCREMENTAL: "0" + RUSTC_WRAPPER: "" + RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" + RUSTDOCFLAGS: "-Cpanic=abort" # RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }} - name: Test individual utilities uses: actions-rs/cargo@v1 @@ -667,10 +722,10 @@ jobs: command: test args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} env: - CARGO_INCREMENTAL: '0' - RUSTC_WRAPPER: '' - RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort' - RUSTDOCFLAGS: '-Cpanic=abort' + CARGO_INCREMENTAL: "0" + RUSTC_WRAPPER: "" + RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" + RUSTDOCFLAGS: "-Cpanic=abort" # RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }} - name: "`grcov` ~ install" uses: actions-rs/install@v0.1 @@ -711,7 +766,7 @@ jobs: fail-fast: false matrix: job: - - { os: ubuntu-latest , features: feat_os_unix } + - { os: ubuntu-latest , features: feat_os_unix } - { os: macos-latest , features: feat_os_macos } - { os: windows-latest , features: feat_os_windows } steps: From a6635d62c714c2c887bdeb77eef8278f4264b8fe Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Wed, 10 Nov 2021 12:09:46 -0600 Subject: [PATCH 115/131] maint/CICD ~ (GHA) use 'feat_os_unix' consistently for ubuntu jobs --- .github/workflows/CICD.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 25e1fcf26..c1fb7cc58 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -90,7 +90,7 @@ jobs: fail-fast: false matrix: job: - - { os: ubuntu-latest } + - { os: ubuntu-latest , features: feat_os_unix } - { os: macos-latest , features: feat_os_macos } - { os: windows-latest , features: feat_os_windows } steps: @@ -149,7 +149,7 @@ jobs: strategy: matrix: job: - - { os: ubuntu-latest } + - { os: ubuntu-latest , features: feat_os_unix } steps: - uses: actions/checkout@v2 - name: Initialize workflow variables @@ -282,7 +282,7 @@ jobs: fail-fast: false matrix: job: - - { os: ubuntu-latest } + - { os: ubuntu-latest , features: feat_os_unix } steps: - uses: actions/checkout@v2 - name: Install `rust` toolchain From 12419b3ee2cac85977ac676bf16eba98fe2415d7 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Wed, 10 Nov 2021 13:36:23 -0600 Subject: [PATCH 116/131] maint/CICD ~ (GHA) reform 'unused dependency check' (`cargo udeps ...`) into a style warning --- .github/workflows/CICD.yml | 96 +++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index c1fb7cc58..e27080050 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -15,11 +15,73 @@ env: PROJECT_AUTH: "uutils" RUST_MIN_SRV: "1.47.0" ## MSRV v1.47.0 # * style job configuration - STYLE_FAIL_ON_FAULT: true ## fail the build if a style job contains a fault (error or warning); boolean + STYLE_FAIL_ON_FAULT: true ## (bool) fail the build if a style job contains a fault (error or warning); may be overridden on a per-job basis on: [push, pull_request] jobs: + style_deps: + ## ToDO: [2021-11-10; rivy] 'Style/deps' needs more informative output and better integration of results into the GHA dashboard + name: Style/deps + runs-on: ${{ matrix.job.os }} + # env: + # STYLE_FAIL_ON_FAULT: false # overrides workflow default + strategy: + fail-fast: false + matrix: + job: + # note: `cargo-udeps` panics when processing stdbuf/libstdbuf ("uu_stdbuf_libstdbuf"); either b/c of the 'cpp' crate or 'libstdbuf' itself + # ... b/c of the panic, a more limited feature set is tested (though only excluding `stdbuf`) + - { os: ubuntu-latest , features: "feat_Tier1,feat_require_unix,feat_require_unix_utmpx" } + - { os: macos-latest , features: "feat_Tier1,feat_require_unix,feat_require_unix_utmpx" } + - { os: windows-latest , features: feat_os_windows } + steps: + - uses: actions/checkout@v2 + ## note: requires 'nightly' toolchain b/c `cargo-udeps` uses the `rustc` '-Z save-analysis' option + ## * ... ref: + - name: Initialize workflow variables + id: vars + shell: bash + run: | + ## VARs setup + outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } + # failure mode + unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in + ''|0|f|false|n|no|off) FAULT_TYPE=warning ;; + *) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;; + esac; + outputs FAIL_ON_FAULT FAULT_TYPE + # target-specific options + # * CARGO_FEATURES_OPTION + CARGO_FEATURES_OPTION='' ; + if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi + outputs CARGO_FEATURES_OPTION + - name: Install `rust` toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + default: true + profile: minimal + - name: Install `cargo-udeps` + uses: actions-rs/install@v0.1 + with: + crate: cargo-udeps + version: latest + use-tool-cache: true + env: + RUSTUP_TOOLCHAIN: stable + - name: Detect unused dependencies + shell: bash + run: | + ## Detect unused dependencies + unset fault + fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" + fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') + # + cargo +nightly udeps ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --all-targets &> udeps.log || cat udeps.log + grep --ignore-case "all deps seem to have been used" udeps.log || { printf "%s\n" "::${fault_type} ::${fault_prefix}: \`cargo udeps\`: style violation (unused dependency found)" ; fault=true ; } + if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi + style_format: name: Style/format runs-on: ${{ matrix.job.os }} @@ -758,35 +820,3 @@ jobs: flags: ${{ steps.vars.outputs.CODECOV_FLAGS }} name: codecov-umbrella fail_ci_if_error: false - - unused_deps: - name: Unused deps - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { os: ubuntu-latest , features: feat_os_unix } - - { os: macos-latest , features: feat_os_macos } - - { os: windows-latest , features: feat_os_windows } - steps: - - uses: actions/checkout@v2 - - name: Install `rust` toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - default: true - profile: minimal - - name: Install `cargo-udeps` - uses: actions-rs/install@v0.1 - with: - crate: cargo-udeps - version: latest - use-tool-cache: true - env: - RUSTUP_TOOLCHAIN: stable - - name: Confirms there isn't any unused deps - shell: bash - run: | - cargo +nightly udeps --all-targets &> udeps.log || cat udeps.log - grep "seem to have been used" udeps.log From f20aa4982150d002a059af4f9e3fbb3f2798507a Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Fri, 12 Nov 2021 21:40:39 -0600 Subject: [PATCH 117/131] maint/CICD ~ (GHA) fix `cargo-udeps` false positives (add 'ignore' exceptions to sub-crates) --- src/uu/arch/Cargo.toml | 3 +++ src/uu/base32/Cargo.toml | 1 - src/uu/base64/Cargo.toml | 1 - src/uu/basename/Cargo.toml | 1 - src/uu/basenc/Cargo.toml | 1 - src/uu/cat/Cargo.toml | 3 +++ src/uu/chcon/Cargo.toml | 3 +++ src/uu/chgrp/Cargo.toml | 3 +++ src/uu/chmod/Cargo.toml | 3 +++ src/uu/chown/Cargo.toml | 3 +++ src/uu/chroot/Cargo.toml | 3 +++ src/uu/cksum/Cargo.toml | 1 - src/uu/comm/Cargo.toml | 1 - src/uu/cp/Cargo.toml | 1 - src/uu/csplit/Cargo.toml | 1 - src/uu/cut/Cargo.toml | 1 - src/uu/date/Cargo.toml | 1 - src/uu/dd/Cargo.toml | 1 - src/uu/df/Cargo.toml | 3 +++ src/uu/dircolors/Cargo.toml | 1 - src/uu/expand/Cargo.toml | 1 - src/uu/expr/Cargo.toml | 1 - src/uu/factor/Cargo.toml | 1 - src/uu/fmt/Cargo.toml | 1 - src/uu/fold/Cargo.toml | 1 - src/uu/groups/Cargo.toml | 3 +++ src/uu/hashsum/Cargo.toml | 1 - src/uu/head/Cargo.toml | 1 - src/uu/hostname/Cargo.toml | 3 +++ src/uu/join/Cargo.toml | 1 - src/uu/link/Cargo.toml | 1 - src/uu/logname/Cargo.toml | 3 +++ src/uu/ls/Cargo.toml | 1 - src/uu/mkfifo/Cargo.toml | 3 +++ src/uu/mknod/Cargo.toml | 3 +++ src/uu/more/Cargo.toml | 1 - src/uu/mv/Cargo.toml | 1 - src/uu/nice/Cargo.toml | 3 +++ src/uu/nl/Cargo.toml | 3 +-- src/uu/nohup/Cargo.toml | 3 +++ src/uu/nproc/Cargo.toml | 3 +++ src/uu/numfmt/Cargo.toml | 3 +-- src/uu/od/Cargo.toml | 1 - src/uu/paste/Cargo.toml | 1 - src/uu/pathchk/Cargo.toml | 3 +++ src/uu/pinky/Cargo.toml | 3 +++ src/uu/pr/Cargo.toml | 1 - src/uu/printenv/Cargo.toml | 1 - src/uu/printf/Cargo.toml | 1 - src/uu/ptx/Cargo.toml | 1 - src/uu/readlink/Cargo.toml | 1 - src/uu/realpath/Cargo.toml | 1 - src/uu/relpath/Cargo.toml | 1 - src/uu/rm/Cargo.toml | 1 - src/uu/seq/Cargo.toml | 1 - src/uu/shred/Cargo.toml | 1 - src/uu/shuf/Cargo.toml | 1 - src/uu/split/Cargo.toml | 1 - src/uu/stat/Cargo.toml | 3 +++ src/uu/sum/Cargo.toml | 1 - src/uu/sync/Cargo.toml | 3 +++ src/uu/tac/Cargo.toml | 1 - src/uu/tail/Cargo.toml | 2 -- src/uu/tee/Cargo.toml | 1 - src/uu/test/Cargo.toml | 1 - src/uu/timeout/Cargo.toml | 3 +++ src/uu/tr/Cargo.toml | 1 - src/uu/truncate/Cargo.toml | 1 - src/uu/tsort/Cargo.toml | 1 - src/uu/tty/Cargo.toml | 3 +++ src/uu/uname/Cargo.toml | 3 +++ src/uu/unexpand/Cargo.toml | 1 - src/uu/uniq/Cargo.toml | 1 - src/uu/wc/Cargo.toml | 1 - src/uu/who/Cargo.toml | 3 +++ 75 files changed, 74 insertions(+), 54 deletions(-) diff --git a/src/uu/arch/Cargo.toml b/src/uu/arch/Cargo.toml index c7fc4f9f9..16f061aca 100644 --- a/src/uu/arch/Cargo.toml +++ b/src/uu/arch/Cargo.toml @@ -23,3 +23,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "arch" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/base32/Cargo.toml b/src/uu/base32/Cargo.toml index d5fc40024..879051a42 100644 --- a/src/uu/base32/Cargo.toml +++ b/src/uu/base32/Cargo.toml @@ -24,5 +24,4 @@ name = "base32" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/base64/Cargo.toml b/src/uu/base64/Cargo.toml index c8d71b85a..ed5a3e7ae 100644 --- a/src/uu/base64/Cargo.toml +++ b/src/uu/base64/Cargo.toml @@ -25,5 +25,4 @@ name = "base64" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/basename/Cargo.toml b/src/uu/basename/Cargo.toml index b75105790..ed2ff834b 100644 --- a/src/uu/basename/Cargo.toml +++ b/src/uu/basename/Cargo.toml @@ -24,5 +24,4 @@ name = "basename" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/basenc/Cargo.toml b/src/uu/basenc/Cargo.toml index 165d3d3a0..7e177ef6d 100644 --- a/src/uu/basenc/Cargo.toml +++ b/src/uu/basenc/Cargo.toml @@ -25,5 +25,4 @@ name = "basenc" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/cat/Cargo.toml b/src/uu/cat/Cargo.toml index b6b0165ef..c292aade6 100644 --- a/src/uu/cat/Cargo.toml +++ b/src/uu/cat/Cargo.toml @@ -31,3 +31,6 @@ winapi-util = "0.1.5" [[bin]] name = "cat" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/chcon/Cargo.toml b/src/uu/chcon/Cargo.toml index a359f142f..55e698c34 100644 --- a/src/uu/chcon/Cargo.toml +++ b/src/uu/chcon/Cargo.toml @@ -25,3 +25,6 @@ libc = { version = "0.2" } [[bin]] name = "chcon" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/chgrp/Cargo.toml b/src/uu/chgrp/Cargo.toml index acc03472c..1fea17653 100644 --- a/src/uu/chgrp/Cargo.toml +++ b/src/uu/chgrp/Cargo.toml @@ -22,3 +22,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "chgrp" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/chmod/Cargo.toml b/src/uu/chmod/Cargo.toml index 64961e6dd..bc2a94948 100644 --- a/src/uu/chmod/Cargo.toml +++ b/src/uu/chmod/Cargo.toml @@ -24,3 +24,6 @@ walkdir = "2.2" [[bin]] name = "chmod" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/chown/Cargo.toml b/src/uu/chown/Cargo.toml index 20381c660..4bf8af3a6 100644 --- a/src/uu/chown/Cargo.toml +++ b/src/uu/chown/Cargo.toml @@ -22,3 +22,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "chown" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/chroot/Cargo.toml b/src/uu/chroot/Cargo.toml index 362e43b59..e4477f761 100644 --- a/src/uu/chroot/Cargo.toml +++ b/src/uu/chroot/Cargo.toml @@ -22,3 +22,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "chroot" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/cksum/Cargo.toml b/src/uu/cksum/Cargo.toml index b7f81d74b..a231c0fa4 100644 --- a/src/uu/cksum/Cargo.toml +++ b/src/uu/cksum/Cargo.toml @@ -25,5 +25,4 @@ name = "cksum" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/comm/Cargo.toml b/src/uu/comm/Cargo.toml index abcbff57b..b77a91516 100644 --- a/src/uu/comm/Cargo.toml +++ b/src/uu/comm/Cargo.toml @@ -25,5 +25,4 @@ name = "comm" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/cp/Cargo.toml b/src/uu/cp/Cargo.toml index 891bf0244..eb4fa6163 100644 --- a/src/uu/cp/Cargo.toml +++ b/src/uu/cp/Cargo.toml @@ -47,5 +47,4 @@ feat_selinux = ["selinux"] feat_acl = ["exacl"] [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/csplit/Cargo.toml b/src/uu/csplit/Cargo.toml index b76e942ee..3168c8f9a 100644 --- a/src/uu/csplit/Cargo.toml +++ b/src/uu/csplit/Cargo.toml @@ -26,5 +26,4 @@ name = "csplit" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/cut/Cargo.toml b/src/uu/cut/Cargo.toml index 991e3c449..8f868130b 100644 --- a/src/uu/cut/Cargo.toml +++ b/src/uu/cut/Cargo.toml @@ -27,5 +27,4 @@ name = "cut" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/date/Cargo.toml b/src/uu/date/Cargo.toml index b1077fbe1..19f74e4c6 100644 --- a/src/uu/date/Cargo.toml +++ b/src/uu/date/Cargo.toml @@ -31,5 +31,4 @@ name = "date" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index e26c141cb..968996b28 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -33,5 +33,4 @@ name = "dd" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/df/Cargo.toml b/src/uu/df/Cargo.toml index 9486640d0..a2d21dc3a 100644 --- a/src/uu/df/Cargo.toml +++ b/src/uu/df/Cargo.toml @@ -23,3 +23,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "df" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/dircolors/Cargo.toml b/src/uu/dircolors/Cargo.toml index 0bf53d91a..1c158e961 100644 --- a/src/uu/dircolors/Cargo.toml +++ b/src/uu/dircolors/Cargo.toml @@ -25,5 +25,4 @@ name = "dircolors" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/expand/Cargo.toml b/src/uu/expand/Cargo.toml index efd338dab..18f800985 100644 --- a/src/uu/expand/Cargo.toml +++ b/src/uu/expand/Cargo.toml @@ -25,5 +25,4 @@ name = "expand" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/expr/Cargo.toml b/src/uu/expr/Cargo.toml index 90a53f2ce..ee34112bd 100644 --- a/src/uu/expr/Cargo.toml +++ b/src/uu/expr/Cargo.toml @@ -28,5 +28,4 @@ name = "expr" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/factor/Cargo.toml b/src/uu/factor/Cargo.toml index f79afeecf..4e9403bc2 100644 --- a/src/uu/factor/Cargo.toml +++ b/src/uu/factor/Cargo.toml @@ -36,5 +36,4 @@ path = "src/main.rs" path = "src/cli.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/fmt/Cargo.toml b/src/uu/fmt/Cargo.toml index a4700cb5e..7cc6c135e 100644 --- a/src/uu/fmt/Cargo.toml +++ b/src/uu/fmt/Cargo.toml @@ -26,5 +26,4 @@ name = "fmt" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/fold/Cargo.toml b/src/uu/fold/Cargo.toml index ce2aeead4..5942286ad 100644 --- a/src/uu/fold/Cargo.toml +++ b/src/uu/fold/Cargo.toml @@ -24,5 +24,4 @@ name = "fold" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/groups/Cargo.toml b/src/uu/groups/Cargo.toml index 6ac210692..3a86a0024 100644 --- a/src/uu/groups/Cargo.toml +++ b/src/uu/groups/Cargo.toml @@ -22,3 +22,6 @@ clap = { version = "2.33", features = ["wrap_help"] } [[bin]] name = "groups" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/hashsum/Cargo.toml b/src/uu/hashsum/Cargo.toml index 67b0f9e02..191f4e47b 100644 --- a/src/uu/hashsum/Cargo.toml +++ b/src/uu/hashsum/Cargo.toml @@ -35,5 +35,4 @@ name = "hashsum" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/head/Cargo.toml b/src/uu/head/Cargo.toml index aa32a899a..f22fc9afd 100644 --- a/src/uu/head/Cargo.toml +++ b/src/uu/head/Cargo.toml @@ -25,5 +25,4 @@ name = "head" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/hostname/Cargo.toml b/src/uu/hostname/Cargo.toml index daaeb48f7..0f50774f0 100644 --- a/src/uu/hostname/Cargo.toml +++ b/src/uu/hostname/Cargo.toml @@ -25,3 +25,6 @@ winapi = { version="0.3", features=["sysinfoapi", "winsock2"] } [[bin]] name = "hostname" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs", "winapi"] diff --git a/src/uu/join/Cargo.toml b/src/uu/join/Cargo.toml index 1c140f544..7ce287c61 100644 --- a/src/uu/join/Cargo.toml +++ b/src/uu/join/Cargo.toml @@ -24,5 +24,4 @@ name = "join" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/link/Cargo.toml b/src/uu/link/Cargo.toml index 275d3b5cc..6a69b774e 100644 --- a/src/uu/link/Cargo.toml +++ b/src/uu/link/Cargo.toml @@ -25,5 +25,4 @@ name = "link" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/logname/Cargo.toml b/src/uu/logname/Cargo.toml index ed19ff887..b8c23ea9a 100644 --- a/src/uu/logname/Cargo.toml +++ b/src/uu/logname/Cargo.toml @@ -23,3 +23,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "logname" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index b918f6501..4eff804e0 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -40,5 +40,4 @@ path = "src/main.rs" feat_selinux = ["selinux"] [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/mkfifo/Cargo.toml b/src/uu/mkfifo/Cargo.toml index acf38f6bc..fa4c458fc 100644 --- a/src/uu/mkfifo/Cargo.toml +++ b/src/uu/mkfifo/Cargo.toml @@ -23,3 +23,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "mkfifo" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/mknod/Cargo.toml b/src/uu/mknod/Cargo.toml index 046e4b8d0..95d890d6e 100644 --- a/src/uu/mknod/Cargo.toml +++ b/src/uu/mknod/Cargo.toml @@ -24,3 +24,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "mknod" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/more/Cargo.toml b/src/uu/more/Cargo.toml index 0111376f9..41aa469c8 100644 --- a/src/uu/more/Cargo.toml +++ b/src/uu/more/Cargo.toml @@ -35,5 +35,4 @@ name = "more" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/mv/Cargo.toml b/src/uu/mv/Cargo.toml index 6dd129af9..415065182 100644 --- a/src/uu/mv/Cargo.toml +++ b/src/uu/mv/Cargo.toml @@ -25,5 +25,4 @@ name = "mv" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/nice/Cargo.toml b/src/uu/nice/Cargo.toml index ac719a768..b168ed98e 100644 --- a/src/uu/nice/Cargo.toml +++ b/src/uu/nice/Cargo.toml @@ -24,3 +24,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "nice" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/nl/Cargo.toml b/src/uu/nl/Cargo.toml index 781f6502d..f9e2cfc55 100644 --- a/src/uu/nl/Cargo.toml +++ b/src/uu/nl/Cargo.toml @@ -29,5 +29,4 @@ name = "nl" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" -normal = ["uucore_procs"] +normal = ["uucore_procs"] \ No newline at end of file diff --git a/src/uu/nohup/Cargo.toml b/src/uu/nohup/Cargo.toml index c2f3c329e..9fa2f009a 100644 --- a/src/uu/nohup/Cargo.toml +++ b/src/uu/nohup/Cargo.toml @@ -24,3 +24,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "nohup" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/nproc/Cargo.toml b/src/uu/nproc/Cargo.toml index 3c44062bd..49c584237 100644 --- a/src/uu/nproc/Cargo.toml +++ b/src/uu/nproc/Cargo.toml @@ -24,3 +24,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "nproc" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/numfmt/Cargo.toml b/src/uu/numfmt/Cargo.toml index 60bfb3b14..29313350f 100644 --- a/src/uu/numfmt/Cargo.toml +++ b/src/uu/numfmt/Cargo.toml @@ -24,5 +24,4 @@ name = "numfmt" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" -normal = ["uucore_procs"] +normal = ["uucore_procs"] \ No newline at end of file diff --git a/src/uu/od/Cargo.toml b/src/uu/od/Cargo.toml index a6b5bc433..7541eaba1 100644 --- a/src/uu/od/Cargo.toml +++ b/src/uu/od/Cargo.toml @@ -27,5 +27,4 @@ name = "od" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/paste/Cargo.toml b/src/uu/paste/Cargo.toml index aeba6f68e..078d5bcc3 100644 --- a/src/uu/paste/Cargo.toml +++ b/src/uu/paste/Cargo.toml @@ -24,5 +24,4 @@ name = "paste" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/pathchk/Cargo.toml b/src/uu/pathchk/Cargo.toml index 79e0c251b..22a5bf63d 100644 --- a/src/uu/pathchk/Cargo.toml +++ b/src/uu/pathchk/Cargo.toml @@ -23,3 +23,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "pathchk" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/pinky/Cargo.toml b/src/uu/pinky/Cargo.toml index 297d40642..a4915e9f3 100644 --- a/src/uu/pinky/Cargo.toml +++ b/src/uu/pinky/Cargo.toml @@ -22,3 +22,6 @@ clap = { version = "2.33", features = ["wrap_help"] } [[bin]] name = "pinky" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/pr/Cargo.toml b/src/uu/pr/Cargo.toml index 499ee7726..9a8f6de8b 100644 --- a/src/uu/pr/Cargo.toml +++ b/src/uu/pr/Cargo.toml @@ -29,5 +29,4 @@ name = "pr" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/printenv/Cargo.toml b/src/uu/printenv/Cargo.toml index eaf482b21..799e114bc 100644 --- a/src/uu/printenv/Cargo.toml +++ b/src/uu/printenv/Cargo.toml @@ -24,5 +24,4 @@ name = "printenv" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/printf/Cargo.toml b/src/uu/printf/Cargo.toml index 50c454db5..a53f77356 100644 --- a/src/uu/printf/Cargo.toml +++ b/src/uu/printf/Cargo.toml @@ -28,5 +28,4 @@ name = "printf" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/ptx/Cargo.toml b/src/uu/ptx/Cargo.toml index 06eb15ee8..75c8c3fe1 100644 --- a/src/uu/ptx/Cargo.toml +++ b/src/uu/ptx/Cargo.toml @@ -29,5 +29,4 @@ name = "ptx" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/readlink/Cargo.toml b/src/uu/readlink/Cargo.toml index d126a3835..0d22c7f20 100644 --- a/src/uu/readlink/Cargo.toml +++ b/src/uu/readlink/Cargo.toml @@ -25,5 +25,4 @@ name = "readlink" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/realpath/Cargo.toml b/src/uu/realpath/Cargo.toml index 4be39f978..4d2f8341e 100644 --- a/src/uu/realpath/Cargo.toml +++ b/src/uu/realpath/Cargo.toml @@ -24,5 +24,4 @@ name = "realpath" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/relpath/Cargo.toml b/src/uu/relpath/Cargo.toml index f1ddf87dc..d32992aed 100644 --- a/src/uu/relpath/Cargo.toml +++ b/src/uu/relpath/Cargo.toml @@ -24,5 +24,4 @@ name = "relpath" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/rm/Cargo.toml b/src/uu/rm/Cargo.toml index 757fa8b22..59f837739 100644 --- a/src/uu/rm/Cargo.toml +++ b/src/uu/rm/Cargo.toml @@ -29,5 +29,4 @@ name = "rm" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/seq/Cargo.toml b/src/uu/seq/Cargo.toml index 5aeffd3b9..a2d52fca3 100644 --- a/src/uu/seq/Cargo.toml +++ b/src/uu/seq/Cargo.toml @@ -28,5 +28,4 @@ name = "seq" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/shred/Cargo.toml b/src/uu/shred/Cargo.toml index 00356d701..5a2856b20 100644 --- a/src/uu/shred/Cargo.toml +++ b/src/uu/shred/Cargo.toml @@ -26,5 +26,4 @@ name = "shred" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/shuf/Cargo.toml b/src/uu/shuf/Cargo.toml index 35da41145..5ee75a249 100644 --- a/src/uu/shuf/Cargo.toml +++ b/src/uu/shuf/Cargo.toml @@ -25,5 +25,4 @@ name = "shuf" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/split/Cargo.toml b/src/uu/split/Cargo.toml index 44d0350b2..24a24631d 100644 --- a/src/uu/split/Cargo.toml +++ b/src/uu/split/Cargo.toml @@ -24,5 +24,4 @@ name = "split" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/stat/Cargo.toml b/src/uu/stat/Cargo.toml index a1f168b14..54dd0e826 100644 --- a/src/uu/stat/Cargo.toml +++ b/src/uu/stat/Cargo.toml @@ -22,3 +22,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "stat" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/sum/Cargo.toml b/src/uu/sum/Cargo.toml index 273f9dff9..5f5a9d642 100644 --- a/src/uu/sum/Cargo.toml +++ b/src/uu/sum/Cargo.toml @@ -24,5 +24,4 @@ name = "sum" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/sync/Cargo.toml b/src/uu/sync/Cargo.toml index 4bf2c60b3..d0fa6bcdc 100644 --- a/src/uu/sync/Cargo.toml +++ b/src/uu/sync/Cargo.toml @@ -24,3 +24,6 @@ winapi = { version = "0.3", features = ["errhandlingapi", "fileapi", "handleapi" [[bin]] name = "sync" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs", "winapi"] diff --git a/src/uu/tac/Cargo.toml b/src/uu/tac/Cargo.toml index 6840d6bc3..253ab4e2c 100644 --- a/src/uu/tac/Cargo.toml +++ b/src/uu/tac/Cargo.toml @@ -29,5 +29,4 @@ name = "tac" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/tail/Cargo.toml b/src/uu/tail/Cargo.toml index f288846ea..af36585a2 100644 --- a/src/uu/tail/Cargo.toml +++ b/src/uu/tail/Cargo.toml @@ -28,12 +28,10 @@ redox_syscall = "0.2" [target.'cfg(unix)'.dependencies] nix = "0.20" -libc = "0.2" [[bin]] name = "tail" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/tee/Cargo.toml b/src/uu/tee/Cargo.toml index 00a250565..1c263c596 100644 --- a/src/uu/tee/Cargo.toml +++ b/src/uu/tee/Cargo.toml @@ -26,5 +26,4 @@ name = "tee" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/test/Cargo.toml b/src/uu/test/Cargo.toml index 633e0f323..09d61faaf 100644 --- a/src/uu/test/Cargo.toml +++ b/src/uu/test/Cargo.toml @@ -28,5 +28,4 @@ name = "test" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/timeout/Cargo.toml b/src/uu/timeout/Cargo.toml index 13afe8435..36ad3cb75 100644 --- a/src/uu/timeout/Cargo.toml +++ b/src/uu/timeout/Cargo.toml @@ -25,3 +25,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "timeout" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/tr/Cargo.toml b/src/uu/tr/Cargo.toml index 8792938a9..d27426539 100644 --- a/src/uu/tr/Cargo.toml +++ b/src/uu/tr/Cargo.toml @@ -26,5 +26,4 @@ name = "tr" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/truncate/Cargo.toml b/src/uu/truncate/Cargo.toml index 811aa6ef6..ccb735a4d 100644 --- a/src/uu/truncate/Cargo.toml +++ b/src/uu/truncate/Cargo.toml @@ -24,5 +24,4 @@ name = "truncate" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/tsort/Cargo.toml b/src/uu/tsort/Cargo.toml index bcc2dd757..e0a9634f6 100644 --- a/src/uu/tsort/Cargo.toml +++ b/src/uu/tsort/Cargo.toml @@ -24,5 +24,4 @@ name = "tsort" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/tty/Cargo.toml b/src/uu/tty/Cargo.toml index 5b2e18cd2..b5d36f304 100644 --- a/src/uu/tty/Cargo.toml +++ b/src/uu/tty/Cargo.toml @@ -24,3 +24,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "tty" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/uname/Cargo.toml b/src/uu/uname/Cargo.toml index 90a103075..0c75f9ad2 100644 --- a/src/uu/uname/Cargo.toml +++ b/src/uu/uname/Cargo.toml @@ -23,3 +23,6 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [[bin]] name = "uname" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] diff --git a/src/uu/unexpand/Cargo.toml b/src/uu/unexpand/Cargo.toml index 026a12872..3345e64c2 100644 --- a/src/uu/unexpand/Cargo.toml +++ b/src/uu/unexpand/Cargo.toml @@ -25,5 +25,4 @@ name = "unexpand" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/uniq/Cargo.toml b/src/uu/uniq/Cargo.toml index a6fcebc92..03cecad87 100644 --- a/src/uu/uniq/Cargo.toml +++ b/src/uu/uniq/Cargo.toml @@ -26,5 +26,4 @@ name = "uniq" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/wc/Cargo.toml b/src/uu/wc/Cargo.toml index d76e0b176..aca35d7d6 100644 --- a/src/uu/wc/Cargo.toml +++ b/src/uu/wc/Cargo.toml @@ -31,5 +31,4 @@ name = "wc" path = "src/main.rs" [package.metadata.cargo-udeps.ignore] -# Necessary for "make all" normal = ["uucore_procs"] diff --git a/src/uu/who/Cargo.toml b/src/uu/who/Cargo.toml index c660be59d..588306927 100644 --- a/src/uu/who/Cargo.toml +++ b/src/uu/who/Cargo.toml @@ -22,3 +22,6 @@ clap = { version = "2.33", features = ["wrap_help"] } [[bin]] name = "who" path = "src/main.rs" + +[package.metadata.cargo-udeps.ignore] +normal = ["uucore_procs"] From aba1c8f596245cc0d0d64dfbe353205267e5fb90 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Wed, 10 Nov 2021 13:49:43 -0600 Subject: [PATCH 118/131] maint/CICD ~ (GHA) disable tool cache use for 'action-rs/install' # [why] The tool cache is currently failing and seems to be getting further behind current versions. The [actions-rs/install#12] issue addresses this but seems to be languishing without any proposed solution. [ref]: --- .github/workflows/CICD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index e27080050..72e08d121 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -67,7 +67,7 @@ jobs: with: crate: cargo-udeps version: latest - use-tool-cache: true + use-tool-cache: false env: RUSTUP_TOOLCHAIN: stable - name: Detect unused dependencies From 0b10e69f56ba55f87e2fe77044dfb89177f8cdb5 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Wed, 10 Nov 2021 19:28:28 -0600 Subject: [PATCH 119/131] maint/CICD ~ (GHA) repair broken `cspell` by pinning it to version v4.2.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## [why] `cspell` in CI started mysteriously failing mid-2021. Tracking down the error took some time as it was not obvious from `cspell` feedback where the issue lay. Ultimately, it was discovered that `cspell` had deprecated use on NodeJS versions < v12 for `cspell` v5+. `cspell` is now pinned to v4.2.8, with a maintenance note to allow an upgrade to the `cspell` version when a version of NodeJS >= v12 is being used in the CI. An issue requesting better tool feedback for similar situations was also opened on the `cspell` repo.[*] [*]: [🙏🏻 Add warning (or error) when used on deprecated/outdated JS platform versions](https://github.com/streetsidesoftware/cspell/issues/1984) --- .github/workflows/CICD.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 72e08d121..615fe0831 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -2,10 +2,10 @@ name: CICD # spell-checker:ignore (acronyms) CICD MSVC musl # spell-checker:ignore (env/flags) Awarnings Ccodegen Coverflow Cpanic RUSTDOCFLAGS RUSTFLAGS Zpanic -# spell-checker:ignore (jargon) SHAs deps softprops toolchain +# spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain # spell-checker:ignore (names) CodeCOV MacOS MinGW Peltoche rivy # spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rustc rustfmt rustup shopt xargs -# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend runtest tempfile testsuite uutils +# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend pell runtest tempfile testsuite uutils # ToDO: [2021-06; rivy] change from `cargo-tree` to `cargo tree` once MSRV is >= 1.45 @@ -230,7 +230,9 @@ jobs: shell: bash run: | ## Install/setup prerequisites - sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell -g ; + # * pin installed cspell to v4.2.8 (cspell v5+ is broken for NodeJS < v12) + ## maint: [2021-11-10; rivy] `cspell` version may be advanced to v5 when used with NodeJS >= v12 + sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell@4.2.8 -g ; - name: Run `cspell` shell: bash run: | @@ -238,12 +240,14 @@ jobs: unset fault fault_type="${{ steps.vars.outputs.FAULT_TYPE }}" fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]') - # * find cspell configuration + # * find cspell configuration ; note: avoid quotes around ${cfg_file} b/c `cspell` (v4) doesn't correctly dequote the config argument (or perhaps a subshell expansion issue?) cfg_files=($(shopt -s nullglob ; echo {.vscode,.}/{,.}c[sS]pell{.json,.config{.js,.cjs,.json,.yaml,.yml},.yaml,.yml} ;)) cfg_file=${cfg_files[0]} - unset CSPELL_CFG_OPTION ; if [ -n "$cfg_file" ]; then CSPELL_CFG_OPTION="--config \"$cfg_file\"" ; fi + unset CSPELL_CFG_OPTION ; if [ -n "$cfg_file" ]; then CSPELL_CFG_OPTION="--config $cfg_file" ; fi # * `cspell` - S=$(cspell ${CSPELL_CFG_OPTION} --no-summary --no-progress "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; } + ## maint: [2021-11-10; rivy] the `--no-progress` option for `cspell` is a `cspell` v5+ option + # S=$(cspell ${CSPELL_CFG_OPTION} --no-summary --no-progress "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; } + S=$(cspell ${CSPELL_CFG_OPTION} --no-summary "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; } if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi min_version: From 047c17dd312a6e3f0cb2e1274b4ff6a2870c804d Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sun, 14 Nov 2021 19:23:48 -0600 Subject: [PATCH 120/131] maint/CICD - (GHA) standardize job step ordering (install, vars, prereq, toolchain, ...) --- .github/workflows/CICD.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 615fe0831..7126cc088 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -484,6 +484,17 @@ jobs: *-pc-windows-msvc) STRIP="" ;; esac; outputs STRIP + - name: Install/setup prerequisites + shell: bash + run: | + ## Install/setup prerequisites + case '${{ matrix.job.target }}' in + arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; + aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; + esac + case '${{ matrix.job.os }}' in + macos-latest) brew install coreutils ;; # needed for testing + esac - name: Create all needed build/work directories shell: bash run: | @@ -493,10 +504,10 @@ jobs: mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg' - name: rust toolchain ~ install uses: actions-rs/toolchain@v1 - env: - # Override auto-detection of RAM for Rustc install. - # https://github.com/rust-lang/rustup/issues/2229#issuecomment-585855925 - RUSTUP_UNPACK_RAM: "21474836480" + # env: + # # Override auto-detection of RAM for Rustc install. + # # https://github.com/rust-lang/rustup/issues/2229#issuecomment-585855925 + # RUSTUP_UNPACK_RAM: "21474836480" with: toolchain: ${{ steps.vars.outputs.TOOLCHAIN }} target: ${{ matrix.job.target }} @@ -513,17 +524,6 @@ jobs: echo UTILITY_LIST=${UTILITY_LIST} CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)" outputs CARGO_UTILITY_LIST_OPTIONS - - name: Install/setup prerequisites - shell: bash - run: | - ## Install/setup prerequisites - case '${{ matrix.job.target }}' in - arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; - aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; - esac - case '${{ matrix.job.os }}' in - macos-latest) brew install coreutils ;; # needed for testing - esac - name: Install `cargo-tree` # for dependency information uses: actions-rs/install@v0.1 with: From 7133efd0a5f711a948264793d3664423862772dd Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Wed, 10 Nov 2021 15:59:41 -0600 Subject: [PATCH 121/131] tests ~ fix `cargo clippy` complaint (clippy::needless_return) --- tests/common/util.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/common/util.rs b/tests/common/util.rs index cfde5f229..ffca7d9b2 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -1166,13 +1166,13 @@ pub fn host_name_for(util_name: &str) -> Cow { { // make call to `host_name_for` idempotent if util_name.starts_with('g') && util_name != "groups" { - return util_name.into(); + util_name.into() } else { - return format!("g{}", util_name).into(); + format!("g{}", util_name).into() } } #[cfg(target_os = "linux")] - return util_name.into(); + util_name.into() } // GNU coreutils version 8.32 is the reference version since it is the latest version and the From f07a1749a1772166c4412d131bbb6e3b07668b8a Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Fri, 12 Nov 2021 23:23:57 -0600 Subject: [PATCH 122/131] fix spell-check errors --- src/uu/chcon/src/chcon.rs | 2 +- src/uu/dd/src/parseargs.rs | 8 ++++---- src/uu/env/src/env.rs | 4 ++-- src/uu/pwd/src/pwd.rs | 2 +- src/uu/uniq/src/uniq.rs | 2 +- tests/by-util/test_dd.rs | 2 +- tests/by-util/test_tr.rs | 2 +- tests/by-util/test_yes.rs | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/uu/chcon/src/chcon.rs b/src/uu/chcon/src/chcon.rs index 9664f69f5..e47312045 100644 --- a/src/uu/chcon/src/chcon.rs +++ b/src/uu/chcon/src/chcon.rs @@ -707,7 +707,7 @@ fn root_dev_ino_warn(dir_name: &Path) { // When a program like chgrp performs a recursive traversal that requires traversing symbolic links, // it is *not* a problem. // However, when invoked with "-P -R", it deserves a warning. -// The fts_options parameter records the options that control this aspect of fts's behavior, +// The fts_options parameter records the options that control this aspect of fts behavior, // so test that. fn cycle_warning_required(fts_options: c_int, entry: &fts::EntryRef) -> bool { // When dereferencing no symlinks, or when dereferencing only those listed on the command line diff --git a/src/uu/dd/src/parseargs.rs b/src/uu/dd/src/parseargs.rs index 3683023ac..a21e9567f 100644 --- a/src/uu/dd/src/parseargs.rs +++ b/src/uu/dd/src/parseargs.rs @@ -20,7 +20,7 @@ pub enum ParseError { MultipleFmtTable, MultipleUCaseLCase, MultipleBlockUnblock, - MultipleExclNoCreat, + MultipleExclNoCreate, FlagNoMatch(String), ConvFlagNoMatch(String), MultiplierStringParseFailure(String), @@ -45,7 +45,7 @@ impl std::fmt::Display for ParseError { Self::MultipleBlockUnblock => { write!(f, "Only one of conv=block or conv=unblock may be specified") } - Self::MultipleExclNoCreat => { + Self::MultipleExclNoCreate => { write!(f, "Only one ov conv=excl or conv=nocreat may be specified") } Self::FlagNoMatch(arg) => { @@ -523,14 +523,14 @@ pub fn parse_conv_flag_output(matches: &Matches) -> Result { if !oconvflags.excl { oconvflags.nocreat = true; } else { - return Err(ParseError::MultipleExclNoCreat); + return Err(ParseError::MultipleExclNoCreate); } } ConvFlag::NoTrunc => oconvflags.notrunc = true, diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 346bf4c8e..55dfce625 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -8,7 +8,7 @@ /* last synced with: env (GNU coreutils) 8.13 */ -// spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets setenv putenv posix_spawnp +// spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets setenv putenv spawnp #[macro_use] extern crate clap; @@ -280,7 +280,7 @@ fn run_env(args: impl uucore::Args) -> UResult<()> { * exec*'s or in the description of env in the "Shell & Utilities" volume). * * It also doesn't specify any checks for putenv before modifying the environ variable, which - * is likely why glibc doesn't do so. However, setenv's first argument cannot point to + * is likely why glibc doesn't do so. However, the first set_var argument cannot point to * an empty string or a string containing '='. * * There is no benefit in replicating GNU's env behavior, since it will only modify the diff --git a/src/uu/pwd/src/pwd.rs b/src/uu/pwd/src/pwd.rs index 8beb65dbd..4b4819ed5 100644 --- a/src/uu/pwd/src/pwd.rs +++ b/src/uu/pwd/src/pwd.rs @@ -18,7 +18,7 @@ static OPT_LOGICAL: &str = "logical"; static OPT_PHYSICAL: &str = "physical"; fn physical_path() -> io::Result { - // std::env::current_dir() is a thin wrapper around libc's getcwd(). + // std::env::current_dir() is a thin wrapper around libc::getcwd(). // On Unix, getcwd() must return the physical path: // https://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html diff --git a/src/uu/uniq/src/uniq.rs b/src/uu/uniq/src/uniq.rs index c6c6a444d..bea64cc53 100644 --- a/src/uu/uniq/src/uniq.rs +++ b/src/uu/uniq/src/uniq.rs @@ -396,7 +396,7 @@ fn get_delimiter(matches: &ArgMatches) -> Delimiters { .value_of(options::ALL_REPEATED) .or_else(|| matches.value_of(options::GROUP)); if let Some(delimiter_arg) = value { - Delimiters::from_str(delimiter_arg).unwrap() // All possible values for ALL_REPEATED are &str's of Delimiters + Delimiters::from_str(delimiter_arg).unwrap() // All possible values for ALL_REPEATED are Delimiters (of type `&str`) } else if matches.is_present(options::GROUP) { Delimiters::Separate } else { diff --git a/tests/by-util/test_dd.rs b/tests/by-util/test_dd.rs index 8340c7059..96f57a885 100644 --- a/tests/by-util/test_dd.rs +++ b/tests/by-util/test_dd.rs @@ -357,7 +357,7 @@ fn test_fullblock() { of!(&tmp_fn), "bs=128M", // Note: In order for this test to actually test iflag=fullblock, the bs=VALUE - // must be big enough to 'overwhelm' urandom's store of bytes. + // must be big enough to 'overwhelm' the urandom store of bytes. // Try executing 'dd if=/dev/urandom bs=128M count=1' (i.e without iflag=fullblock). // The stats should contain the line: '0+1 records in' indicating a partial read. // Since my system only copies 32 MiB without fullblock, I expect 128 MiB to be diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs index bd022b1c2..49dcb813c 100644 --- a/tests/by-util/test_tr.rs +++ b/tests/by-util/test_tr.rs @@ -290,7 +290,7 @@ fn test_interpret_backslash_at_eol_literally() { #[cfg(not(target_os = "freebsd"))] fn test_more_than_2_sets() { new_ucmd!() - .args(&["'abcdefgh'", "'a", "'b'"]) + .args(&["'abcdef'", "'a'", "'b'"]) .pipe_in("hello world") .fails(); } diff --git a/tests/by-util/test_yes.rs b/tests/by-util/test_yes.rs index 7e950e1ea..38165e92b 100644 --- a/tests/by-util/test_yes.rs +++ b/tests/by-util/test_yes.rs @@ -45,7 +45,7 @@ fn test_long_input() { // try something long. #[cfg(windows)] const TIMES: usize = 500; - let arg = "abcdefg".repeat(TIMES) + "\n"; + let arg = "abcdef".repeat(TIMES) + "\n"; let expected_out = arg.repeat(30); run(&[&arg[..arg.len() - 1]], expected_out.as_bytes()); } From 39a6e6c75b53222306b747de7d99fd257573b03b Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sun, 14 Nov 2021 16:24:18 -0600 Subject: [PATCH 123/131] maint/CICD ~ (GHA) normalize setup sub-step ordering --- .github/workflows/CICD.yml | 103 ++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 35 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 7126cc088..3c9be1319 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -37,8 +37,6 @@ jobs: - { os: windows-latest , features: feat_os_windows } steps: - uses: actions/checkout@v2 - ## note: requires 'nightly' toolchain b/c `cargo-udeps` uses the `rustc` '-Z save-analysis' option - ## * ... ref: - name: Initialize workflow variables id: vars shell: bash @@ -56,6 +54,8 @@ jobs: CARGO_FEATURES_OPTION='' ; if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi outputs CARGO_FEATURES_OPTION + ## note: requires 'nightly' toolchain b/c `cargo-udeps` uses the `rustc` '-Z save-analysis' option + ## * ... ref: - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 with: @@ -351,17 +351,17 @@ jobs: - { os: ubuntu-latest , features: feat_os_unix } steps: - uses: actions/checkout@v2 + - name: Install/setup prerequisites + shell: bash + run: | + ## Install/setup prerequisites + sudo apt-get -y update ; sudo apt-get -y install python3-sphinx ; - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 with: toolchain: stable default: true profile: minimal # minimal component installation (ie, no documentation) - - name: Install/setup prerequisites - shell: bash - run: | - ## Install/setup prerequisites - sudo apt-get -y update ; sudo apt-get -y install python3-sphinx ; - name: "`make build`" shell: bash run: | @@ -502,6 +502,17 @@ jobs: mkdir -p '${{ steps.vars.outputs.STAGING }}' mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}' mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg' + - name: Install/setup prerequisites + shell: bash + run: | + ## Install/setup prerequisites + case '${{ matrix.job.target }}' in + arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; + aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; + esac + case '${{ matrix.job.os }}' in + macos-latest) brew install coreutils ;; # needed for testing + esac - name: rust toolchain ~ install uses: actions-rs/toolchain@v1 # env: @@ -632,16 +643,17 @@ jobs: - { os: ubuntu-latest } steps: - uses: actions/checkout@v2 + - name: Install/setup prerequisites + shell: bash + run: | + ## Install/setup prerequisites + make prepare-busytest - name: Install `rust` toolchain uses: actions-rs/toolchain@v1 with: toolchain: stable default: true profile: minimal # minimal component installation (ie, no documentation) - - name: Install/setup prerequisites - shell: bash - run: | - make prepare-busytest - name: "Run BusyBox test suite" shell: bash run: | @@ -654,48 +666,69 @@ jobs: if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi test_freebsd: - runs-on: macos-10.15 name: Tests/FreeBSD test suite + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { os: macos-10.15 , features: unix } ## GHA MacOS-11.0 VM won't have VirtualBox; refs: , env: mem: 2048 steps: - uses: actions/checkout@v2 - name: Prepare, build and test - id: test + ## spell-checker:ignore (ToDO) sshfs usesh vmactions uses: vmactions/freebsd-vm@v0.1.5 with: usesh: true + # sync: sshfs prepare: pkg install -y curl gmake sudo run: | - # Need to be run in the same block. Otherwise, we are back on the mac host. + ## Prepare, build, and test + # implementation modelled after ref: + # * NOTE: All steps need to be run in this block, otherwise, we are operating back on the mac host set -e - pw adduser -n cuuser -d /root/ -g wheel -c "Coreutils user to build" -w random - chown -R cuuser:wheel /root/ /Users/runner/work/coreutils/ + # + TEST_USER=tester + REPO_NAME=${GITHUB_WORKSPACE##*/} + WORKSPACE_PARENT="/Users/runner/work/${REPO_NAME}" + WORKSPACE="${WORKSPACE_PARENT}/${REPO_NAME}" + # + pw adduser -n ${TEST_USER} -d /root/ -g wheel -c "Coreutils user to build" -w random + # chown -R ${TEST_USER}:wheel /root/ "${WORKSPACE_PARENT}"/ + chown -R ${TEST_USER}:wheel /root/ "/Users/runner/work/${REPO_NAME}"/ whoami - - # Needs to be done in a sudo as we are changing users - sudo -i -u cuuser sh << EOF + # + # Further work needs to be done in a sudo as we are changing users + sudo -i -u ${TEST_USER} sh << EOF set -e whoami curl https://sh.rustup.rs -sSf --output rustup.sh sh rustup.sh -y --profile=minimal + . $HOME/.cargo/env ## Info # environment echo "## environment" echo "CI='${CI}'" - # tooling info display - echo "## tooling" - . $HOME/.cargo/env + echo "REPO_NAME='${REPO_NAME}'" + echo "TEST_USER='${TEST_USER}'" + echo "WORKSPACE_PARENT='${WORKSPACE_PARENT}'" + echo "WORKSPACE='${WORKSPACE}'" + env | sort + # tooling info + echo "## tooling info" cargo -V rustc -V - env - - # where the files are resynced - cd /Users/runner/work/coreutils/coreutils/ - cargo build - cargo test --features feat_os_unix -p uucore -p coreutils + # + cd "${WORKSPACE}" + unset FAULT + cargo build || FAULT=1 + cargo test --features "${{ matrix.job.features }}" || FAULT=1 + cargo test --features "${{ matrix.job.features }}" -p uucore || FAULT=1 # Clean to avoid to rsync back the files cargo clean + if (test -n "$FAULT"); then exit 1 ; fi EOF coverage: @@ -711,13 +744,6 @@ jobs: - { os: windows-latest , features: windows } steps: - uses: actions/checkout@v2 - - name: Install/setup prerequisites - shell: bash - run: | - ## Install/setup prerequisites - case '${{ matrix.job.os }}' in - macos-latest) brew install coreutils ;; # needed for testing - esac # - name: Reattach HEAD ## may be needed for accurate code coverage info # run: git checkout ${{ github.head_ref }} - name: Initialize workflow variables @@ -744,6 +770,13 @@ jobs: # * CODECOV_FLAGS CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' ) outputs CODECOV_FLAGS + - name: Install/setup prerequisites + shell: bash + run: | + ## Install/setup prerequisites + case '${{ matrix.job.os }}' in + macos-latest) brew install coreutils ;; # needed for testing + esac - name: rust toolchain ~ install uses: actions-rs/toolchain@v1 with: From 8b7f2b44f6b2c4d40fa106615f835e9706d3b653 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sun, 14 Nov 2021 16:29:23 -0600 Subject: [PATCH 124/131] change ~ use patched/vendor'ed 'nix' crate until fixed at source - a PR has been submitted to 'nix'; ref: --- Cargo.toml | 9 +- vendor/nix-v0.23.1-patched/.cirrus.yml | 274 ++ vendor/nix-v0.23.1-patched/.gitattributes | 1 + vendor/nix-v0.23.1-patched/.gitignore | 10 + vendor/nix-v0.23.1-patched/CHANGELOG.md | 1227 +++++++ vendor/nix-v0.23.1-patched/CONTRIBUTING.md | 114 + vendor/nix-v0.23.1-patched/CONVENTIONS.md | 86 + vendor/nix-v0.23.1-patched/Cargo.toml | 74 + vendor/nix-v0.23.1-patched/Cross.toml | 5 + vendor/nix-v0.23.1-patched/LICENSE | 21 + vendor/nix-v0.23.1-patched/README.md | 102 + .../nix-v0.23.1-patched/RELEASE_PROCEDURE.md | 18 + vendor/nix-v0.23.1-patched/bors.toml | 49 + vendor/nix-v0.23.1-patched/release.toml | 5 + vendor/nix-v0.23.1-patched/src/dir.rs | 246 ++ vendor/nix-v0.23.1-patched/src/env.rs | 66 + vendor/nix-v0.23.1-patched/src/errno.rs | 2723 +++++++++++++++ vendor/nix-v0.23.1-patched/src/fcntl.rs | 696 ++++ vendor/nix-v0.23.1-patched/src/features.rs | 121 + vendor/nix-v0.23.1-patched/src/ifaddrs.rs | 147 + vendor/nix-v0.23.1-patched/src/kmod.rs | 122 + vendor/nix-v0.23.1-patched/src/lib.rs | 227 ++ vendor/nix-v0.23.1-patched/src/macros.rs | 311 ++ vendor/nix-v0.23.1-patched/src/mount/bsd.rs | 426 +++ vendor/nix-v0.23.1-patched/src/mount/linux.rs | 111 + vendor/nix-v0.23.1-patched/src/mount/mod.rs | 21 + vendor/nix-v0.23.1-patched/src/mqueue.rs | 178 + vendor/nix-v0.23.1-patched/src/net/if_.rs | 411 +++ vendor/nix-v0.23.1-patched/src/net/mod.rs | 4 + vendor/nix-v0.23.1-patched/src/poll.rs | 163 + vendor/nix-v0.23.1-patched/src/pty.rs | 348 ++ vendor/nix-v0.23.1-patched/src/sched.rs | 282 ++ vendor/nix-v0.23.1-patched/src/sys/aio.rs | 1122 ++++++ vendor/nix-v0.23.1-patched/src/sys/epoll.rs | 109 + vendor/nix-v0.23.1-patched/src/sys/event.rs | 348 ++ vendor/nix-v0.23.1-patched/src/sys/eventfd.rs | 17 + vendor/nix-v0.23.1-patched/src/sys/inotify.rs | 233 ++ .../nix-v0.23.1-patched/src/sys/ioctl/bsd.rs | 109 + .../src/sys/ioctl/linux.rs | 141 + .../nix-v0.23.1-patched/src/sys/ioctl/mod.rs | 778 +++++ vendor/nix-v0.23.1-patched/src/sys/memfd.rs | 19 + vendor/nix-v0.23.1-patched/src/sys/mman.rs | 464 +++ vendor/nix-v0.23.1-patched/src/sys/mod.rs | 131 + .../src/sys/personality.rs | 70 + vendor/nix-v0.23.1-patched/src/sys/pthread.rs | 38 + .../nix-v0.23.1-patched/src/sys/ptrace/bsd.rs | 176 + .../src/sys/ptrace/linux.rs | 479 +++ .../nix-v0.23.1-patched/src/sys/ptrace/mod.rs | 22 + vendor/nix-v0.23.1-patched/src/sys/quota.rs | 277 ++ vendor/nix-v0.23.1-patched/src/sys/reboot.rs | 45 + .../nix-v0.23.1-patched/src/sys/resource.rs | 233 ++ vendor/nix-v0.23.1-patched/src/sys/select.rs | 430 +++ .../nix-v0.23.1-patched/src/sys/sendfile.rs | 231 ++ vendor/nix-v0.23.1-patched/src/sys/signal.rs | 1234 +++++++ .../nix-v0.23.1-patched/src/sys/signalfd.rs | 169 + .../src/sys/socket/addr.rs | 1447 ++++++++ .../nix-v0.23.1-patched/src/sys/socket/mod.rs | 1916 +++++++++++ .../src/sys/socket/sockopt.rs | 930 +++++ vendor/nix-v0.23.1-patched/src/sys/stat.rs | 315 ++ vendor/nix-v0.23.1-patched/src/sys/statfs.rs | 622 ++++ vendor/nix-v0.23.1-patched/src/sys/statvfs.rs | 161 + vendor/nix-v0.23.1-patched/src/sys/sysinfo.rs | 79 + vendor/nix-v0.23.1-patched/src/sys/termios.rs | 1016 ++++++ vendor/nix-v0.23.1-patched/src/sys/time.rs | 609 ++++ vendor/nix-v0.23.1-patched/src/sys/timerfd.rs | 281 ++ vendor/nix-v0.23.1-patched/src/sys/uio.rs | 223 ++ vendor/nix-v0.23.1-patched/src/sys/utsname.rs | 75 + vendor/nix-v0.23.1-patched/src/sys/wait.rs | 262 ++ vendor/nix-v0.23.1-patched/src/time.rs | 260 ++ vendor/nix-v0.23.1-patched/src/ucontext.rs | 43 + vendor/nix-v0.23.1-patched/src/unistd.rs | 2994 +++++++++++++++++ vendor/nix-v0.23.1-patched/test/common/mod.rs | 141 + vendor/nix-v0.23.1-patched/test/sys/mod.rs | 47 + .../nix-v0.23.1-patched/test/sys/test_aio.rs | 620 ++++ .../test/sys/test_aio_drop.rs | 29 + .../test/sys/test_epoll.rs | 23 + .../test/sys/test_inotify.rs | 63 + .../test/sys/test_ioctl.rs | 337 ++ .../test/sys/test_lio_listio_resubmit.rs | 106 + .../nix-v0.23.1-patched/test/sys/test_mman.rs | 92 + .../test/sys/test_pthread.rs | 22 + .../test/sys/test_ptrace.rs | 219 ++ .../test/sys/test_select.rs | 82 + .../test/sys/test_signal.rs | 121 + .../test/sys/test_signalfd.rs | 27 + .../test/sys/test_socket.rs | 1941 +++++++++++ .../test/sys/test_sockopt.rs | 199 ++ .../test/sys/test_sysinfo.rs | 18 + .../test/sys/test_termios.rs | 130 + .../test/sys/test_timerfd.rs | 61 + .../nix-v0.23.1-patched/test/sys/test_uio.rs | 255 ++ .../nix-v0.23.1-patched/test/sys/test_wait.rs | 107 + vendor/nix-v0.23.1-patched/test/test.rs | 103 + .../nix-v0.23.1-patched/test/test_clearenv.rs | 9 + vendor/nix-v0.23.1-patched/test/test_dir.rs | 56 + vendor/nix-v0.23.1-patched/test/test_fcntl.rs | 540 +++ .../test/test_kmod/hello_mod/Makefile | 7 + .../test/test_kmod/hello_mod/hello.c | 26 + .../nix-v0.23.1-patched/test/test_kmod/mod.rs | 166 + vendor/nix-v0.23.1-patched/test/test_mount.rs | 236 ++ vendor/nix-v0.23.1-patched/test/test_mq.rs | 157 + vendor/nix-v0.23.1-patched/test/test_net.rs | 12 + .../nix-v0.23.1-patched/test/test_nix_path.rs | 0 .../nix-v0.23.1-patched/test/test_nmount.rs | 51 + vendor/nix-v0.23.1-patched/test/test_poll.rs | 82 + vendor/nix-v0.23.1-patched/test/test_pty.rs | 301 ++ .../test/test_ptymaster_drop.rs | 20 + .../nix-v0.23.1-patched/test/test_resource.rs | 23 + vendor/nix-v0.23.1-patched/test/test_sched.rs | 32 + .../nix-v0.23.1-patched/test/test_sendfile.rs | 151 + vendor/nix-v0.23.1-patched/test/test_stat.rs | 358 ++ vendor/nix-v0.23.1-patched/test/test_time.rs | 58 + .../nix-v0.23.1-patched/test/test_unistd.rs | 1150 +++++++ 113 files changed, 34881 insertions(+), 3 deletions(-) create mode 100644 vendor/nix-v0.23.1-patched/.cirrus.yml create mode 100644 vendor/nix-v0.23.1-patched/.gitattributes create mode 100644 vendor/nix-v0.23.1-patched/.gitignore create mode 100644 vendor/nix-v0.23.1-patched/CHANGELOG.md create mode 100644 vendor/nix-v0.23.1-patched/CONTRIBUTING.md create mode 100644 vendor/nix-v0.23.1-patched/CONVENTIONS.md create mode 100644 vendor/nix-v0.23.1-patched/Cargo.toml create mode 100644 vendor/nix-v0.23.1-patched/Cross.toml create mode 100644 vendor/nix-v0.23.1-patched/LICENSE create mode 100644 vendor/nix-v0.23.1-patched/README.md create mode 100644 vendor/nix-v0.23.1-patched/RELEASE_PROCEDURE.md create mode 100644 vendor/nix-v0.23.1-patched/bors.toml create mode 100644 vendor/nix-v0.23.1-patched/release.toml create mode 100644 vendor/nix-v0.23.1-patched/src/dir.rs create mode 100644 vendor/nix-v0.23.1-patched/src/env.rs create mode 100644 vendor/nix-v0.23.1-patched/src/errno.rs create mode 100644 vendor/nix-v0.23.1-patched/src/fcntl.rs create mode 100644 vendor/nix-v0.23.1-patched/src/features.rs create mode 100644 vendor/nix-v0.23.1-patched/src/ifaddrs.rs create mode 100644 vendor/nix-v0.23.1-patched/src/kmod.rs create mode 100644 vendor/nix-v0.23.1-patched/src/lib.rs create mode 100644 vendor/nix-v0.23.1-patched/src/macros.rs create mode 100644 vendor/nix-v0.23.1-patched/src/mount/bsd.rs create mode 100644 vendor/nix-v0.23.1-patched/src/mount/linux.rs create mode 100644 vendor/nix-v0.23.1-patched/src/mount/mod.rs create mode 100644 vendor/nix-v0.23.1-patched/src/mqueue.rs create mode 100644 vendor/nix-v0.23.1-patched/src/net/if_.rs create mode 100644 vendor/nix-v0.23.1-patched/src/net/mod.rs create mode 100644 vendor/nix-v0.23.1-patched/src/poll.rs create mode 100644 vendor/nix-v0.23.1-patched/src/pty.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sched.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/aio.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/epoll.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/event.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/eventfd.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/inotify.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/ioctl/bsd.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/ioctl/linux.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/ioctl/mod.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/memfd.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/mman.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/mod.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/personality.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/pthread.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/ptrace/bsd.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/ptrace/linux.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/ptrace/mod.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/quota.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/reboot.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/resource.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/select.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/sendfile.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/signal.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/signalfd.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/socket/addr.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/socket/mod.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/socket/sockopt.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/stat.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/statfs.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/statvfs.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/sysinfo.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/termios.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/time.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/timerfd.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/uio.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/utsname.rs create mode 100644 vendor/nix-v0.23.1-patched/src/sys/wait.rs create mode 100644 vendor/nix-v0.23.1-patched/src/time.rs create mode 100644 vendor/nix-v0.23.1-patched/src/ucontext.rs create mode 100644 vendor/nix-v0.23.1-patched/src/unistd.rs create mode 100644 vendor/nix-v0.23.1-patched/test/common/mod.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/mod.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_aio.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_aio_drop.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_epoll.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_inotify.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_ioctl.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_lio_listio_resubmit.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_mman.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_pthread.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_ptrace.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_select.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_signal.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_signalfd.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_socket.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_sockopt.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_sysinfo.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_termios.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_timerfd.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_uio.rs create mode 100644 vendor/nix-v0.23.1-patched/test/sys/test_wait.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_clearenv.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_dir.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_fcntl.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_kmod/hello_mod/Makefile create mode 100644 vendor/nix-v0.23.1-patched/test/test_kmod/hello_mod/hello.c create mode 100644 vendor/nix-v0.23.1-patched/test/test_kmod/mod.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_mount.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_mq.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_net.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_nix_path.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_nmount.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_poll.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_pty.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_ptymaster_drop.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_resource.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_sched.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_sendfile.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_stat.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_time.rs create mode 100644 vendor/nix-v0.23.1-patched/test/test_unistd.rs diff --git a/Cargo.toml b/Cargo.toml index 9ecea79c2..1ce218fb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -381,12 +381,15 @@ atty = "0.2" rlimit = "0.4.0" [target.'cfg(unix)'.dev-dependencies] -nix = "0.20.0" - +nix = "=0.23.1" rust-users = { version="0.10", package="users" } unix_socket = "0.5.0" - [[bin]] name = "coreutils" path = "src/bin/coreutils.rs" + +[patch.crates-io] +# FixME: [2021-11-16; rivy] remove 'nix' patch when MacOS compatibility is restored; ref: +# nix = { git = "https://github.com/rivy-t/nix" } +nix = { path = "vendor/nix-v0.23.1-patched" } diff --git a/vendor/nix-v0.23.1-patched/.cirrus.yml b/vendor/nix-v0.23.1-patched/.cirrus.yml new file mode 100644 index 000000000..3848f9206 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/.cirrus.yml @@ -0,0 +1,274 @@ +cargo_cache: + folder: $CARGO_HOME/registry + fingerprint_script: cat Cargo.lock || echo "" + +env: + # Build by default; don't just check + BUILD: build + CLIPPYFLAGS: -D warnings + RUSTFLAGS: -D warnings + RUSTDOCFLAGS: -D warnings + TOOL: cargo + # The MSRV + TOOLCHAIN: 1.46.0 + ZFLAGS: + +# Tests that don't require executing the build binaries +build: &BUILD + build_script: + - . $HOME/.cargo/env || true + - $TOOL +$TOOLCHAIN $BUILD $ZFLAGS --target $TARGET --all-targets + - $TOOL +$TOOLCHAIN doc $ZFLAGS --no-deps --target $TARGET + - $TOOL +$TOOLCHAIN clippy $ZFLAGS --target $TARGET -- $CLIPPYFLAGS + +# Tests that do require executing the binaries +test: &TEST + << : *BUILD + test_script: + - . $HOME/.cargo/env || true + - $TOOL +$TOOLCHAIN test --target $TARGET + +# Test FreeBSD in a full VM. Test the i686 target too, in the +# same VM. The binary will be built in 32-bit mode, but will execute on a +# 64-bit kernel and in a 64-bit environment. Our tests don't execute any of +# the system's binaries, so the environment shouldn't matter. +task: + name: FreeBSD amd64 & i686 + env: + TARGET: x86_64-unknown-freebsd + freebsd_instance: + image: freebsd-12-2-release-amd64 + setup_script: + - fetch https://sh.rustup.rs -o rustup.sh + - sh rustup.sh -y --profile=minimal --default-toolchain $TOOLCHAIN + - . $HOME/.cargo/env + - rustup target add i686-unknown-freebsd + - rustup component add --toolchain $TOOLCHAIN clippy + << : *TEST + i386_test_script: + - . $HOME/.cargo/env + - cargo build --target i686-unknown-freebsd + - cargo doc --no-deps --target i686-unknown-freebsd + - cargo test --target i686-unknown-freebsd + before_cache_script: rm -rf $CARGO_HOME/registry/index + +# Test OSX in a full VM +task: + matrix: + - name: OSX x86_64 + env: + TARGET: x86_64-apple-darwin + osx_instance: + image: catalina-xcode + setup_script: + - curl --proto '=https' --tlsv1.2 -sSf -o rustup.sh https://sh.rustup.rs + - sh rustup.sh -y --profile=minimal --default-toolchain $TOOLCHAIN + - . $HOME/.cargo/env + - rustup component add --toolchain $TOOLCHAIN clippy + << : *TEST + before_cache_script: rm -rf $CARGO_HOME/registry/index + +# Use cross for QEMU-based testing +# cross needs to execute Docker, so we must use Cirrus's Docker Builder task. +task: + env: + RUST_TEST_THREADS: 1 # QEMU works best with 1 thread + HOME: /tmp/home + PATH: $HOME/.cargo/bin:$PATH + RUSTFLAGS: --cfg qemu -D warnings + TOOL: cross + matrix: + - name: Linux arm gnueabi + env: + TARGET: arm-unknown-linux-gnueabi + - name: Linux armv7 gnueabihf + env: + TARGET: armv7-unknown-linux-gnueabihf + - name: Linux i686 + env: + TARGET: i686-unknown-linux-gnu + - name: Linux i686 musl + env: + TARGET: i686-unknown-linux-musl + - name: Linux MIPS + env: + TARGET: mips-unknown-linux-gnu + - name: Linux MIPS64 + env: + TARGET: mips64-unknown-linux-gnuabi64 + - name: Linux MIPS64 el + env: + TARGET: mips64el-unknown-linux-gnuabi64 + - name: Linux mipsel + env: + TARGET: mipsel-unknown-linux-gnu + - name: Linux powerpc64le + env: + TARGET: powerpc64le-unknown-linux-gnu + compute_engine_instance: + image_project: cirrus-images + image: family/docker-builder + platform: linux + cpu: 1 # Since QEMU will only use 1 thread + memory: 4G + setup_script: + - mkdir /tmp/home + - curl --proto '=https' --tlsv1.2 -sSf -o rustup.sh https://sh.rustup.rs + - sh rustup.sh -y --profile=minimal --default-toolchain $TOOLCHAIN + - . $HOME/.cargo/env + - cargo install cross + << : *TEST + before_cache_script: rm -rf $CARGO_HOME/registry/index + +# Tasks for Linux native builds +task: + matrix: + - name: Rust Stable + container: + image: rust:latest + env: + TARGET: x86_64-unknown-linux-gnu + TOOLCHAIN: + - name: Linux aarch64 + arm_container: + image: rust:1.46 + env: + RUSTFLAGS: --cfg graviton -D warnings + TARGET: aarch64-unknown-linux-gnu + - name: Linux x86_64 + container: + image: rust:1.46 + env: + TARGET: x86_64-unknown-linux-gnu + - name: Linux x86_64 musl + container: + image: rust:1.46 + env: + TARGET: x86_64-unknown-linux-musl + setup_script: + - rustup target add $TARGET + - rustup component add clippy + << : *TEST + before_cache_script: rm -rf $CARGO_HOME/registry/index + +# Tasks for cross-compiling, but no testing +task: + container: + image: rust:1.46 + env: + BUILD: check + matrix: + # Cross claims to support Android, but when it tries to run Nix's tests it + # reports undefined symbol references. + - name: Android aarch64 + env: + TARGET: aarch64-linux-android + - name: Android arm + env: + TARGET: arm-linux-androideabi + - name: Android armv7 + env: + TARGET: armv7-linux-androideabi + - name: Android i686 + env: + TARGET: i686-linux-android + - name: Android x86_64 + env: + TARGET: x86_64-linux-android + - name: Linux arm-musleabi + env: + TARGET: arm-unknown-linux-musleabi + - name: Fuchsia x86_64 + env: + TARGET: x86_64-fuchsia + - name: Illumos + env: + TARGET: x86_64-unknown-illumos + # illumos toolchain isn't available via rustup until 1.50 + TOOLCHAIN: 1.50.0 + container: + image: rust:1.50 + # Cross claims to support running tests on iOS, but it actually doesn't. + # https://github.com/rust-embedded/cross/issues/535 + - name: iOS aarch64 + env: + TARGET: aarch64-apple-ios + # Rustup only supports cross-building from arbitrary hosts for iOS at + # 1.49.0 and above. Below that it's possible to cross-build from an OSX + # host, but OSX VMs are more expensive than Linux VMs. + TOOLCHAIN: 1.49.0 + - name: iOS x86_64 + env: + TARGET: x86_64-apple-ios + TOOLCHAIN: 1.49.0 + # Cross testing on powerpc fails with "undefined reference to renameat2". + # Perhaps cross is using too-old a version? + - name: Linux powerpc + env: + TARGET: powerpc-unknown-linux-gnu + # Cross claims to support Linux powerpc64, but it really doesn't. + # https://github.com/rust-embedded/cross/issues/441 + - name: Linux powerpc64 + env: + TARGET: powerpc64-unknown-linux-gnu + - name: Linux s390x + env: + TARGET: s390x-unknown-linux-gnu + - name: Linux x32 + env: + TARGET: x86_64-unknown-linux-gnux32 + - name: NetBSD x86_64 + env: + TARGET: x86_64-unknown-netbsd + - name: Redox x86_64 + env: + TARGET: x86_64-unknown-redox + # Redox requires a nightly compiler. + # If stuff breaks, change nightly to the date in the toolchain_* + # directory at https://static.redox-os.org + TOOLCHAIN: nightly-2020-08-04 + setup_script: + - rustup target add $TARGET + - rustup toolchain install $TOOLCHAIN --profile minimal --target $TARGET + - rustup component add --toolchain $TOOLCHAIN clippy + << : *BUILD + before_cache_script: rm -rf $CARGO_HOME/registry/index + +# Rust Tier 3 targets can't use Rustup +task: + container: + image: rustlang/rust:nightly + env: + BUILD: check + # Must allow here rather than in lib.rs because this lint doesn't exist + # prior to Rust 1.57.0 + # https://github.com/rust-lang/rust-clippy/issues/7718 + CLIPPYFLAGS: -D warnings -A clippy::if_then_panic + TOOLCHAIN: nightly + ZFLAGS: -Zbuild-std + matrix: + - name: DragonFly BSD x86_64 + env: + TARGET: x86_64-unknown-dragonfly + - name: OpenBSD x86_64 + env: + TARGET: x86_64-unknown-openbsd + setup_script: + - rustup component add rust-src + << : *BUILD + before_cache_script: rm -rf $CARGO_HOME/registry/index + +# Test that we can build with the lowest version of all dependencies. +# "cargo test" doesn't work because some of our dev-dependencies, like +# rand, can't build with their own minimal dependencies. +task: + name: Minver + env: + TOOLCHAIN: nightly + container: + image: rustlang/rust:nightly + setup_script: + - cargo update -Zminimal-versions + script: + - cargo check + before_cache_script: rm -rf $CARGO_HOME/registry/index diff --git a/vendor/nix-v0.23.1-patched/.gitattributes b/vendor/nix-v0.23.1-patched/.gitattributes new file mode 100644 index 000000000..3d432e03f --- /dev/null +++ b/vendor/nix-v0.23.1-patched/.gitattributes @@ -0,0 +1 @@ +/CHANGELOG.md merge=union diff --git a/vendor/nix-v0.23.1-patched/.gitignore b/vendor/nix-v0.23.1-patched/.gitignore new file mode 100644 index 000000000..87f1a1476 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/.gitignore @@ -0,0 +1,10 @@ +syntax: glob +Cargo.lock +target +*.diff +*.rej +*.orig +.*.swn +.*.swo +.*.swp +*.a diff --git a/vendor/nix-v0.23.1-patched/CHANGELOG.md b/vendor/nix-v0.23.1-patched/CHANGELOG.md new file mode 100644 index 000000000..d2db7d2d6 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/CHANGELOG.md @@ -0,0 +1,1227 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](https://semver.org/). + +## [Unreleased] - ReleaseDate + +### Added +### Changed +### Fixed + +- Fixed soundness issues in `FdSet::insert`, `FdSet::remove`, and + `FdSet::contains` involving file descriptors outside of the range + `0..FD_SETSIZE`. + (#[1575](https://github.com/nix-rust/nix/pull/1575)) + +### Removed + +## [0.23.0] - 2021-09-28 +### Added + +- Added the `LocalPeerCred` sockopt. + (#[1482](https://github.com/nix-rust/nix/pull/1482)) +- Added `TimeSpec::from_duration` and `TimeSpec::from_timespec` + (#[1465](https://github.com/nix-rust/nix/pull/1465)) +- Added `IPV6_V6ONLY` sockopt. + (#[1470](https://github.com/nix-rust/nix/pull/1470)) +- Added `impl From for libc::passwd` trait implementation to convert a `User` + into a `libc::passwd`. Consumes the `User` struct to give ownership over + the member pointers. + (#[1471](https://github.com/nix-rust/nix/pull/1471)) +- Added `pthread_kill`. + (#[1472](https://github.com/nix-rust/nix/pull/1472)) +- Added `mknodat`. + (#[1473](https://github.com/nix-rust/nix/pull/1473)) +- Added `setrlimit` and `getrlimit`. + (#[1302](https://github.com/nix-rust/nix/pull/1302)) +- Added `ptrace::interrupt` method for platforms that support `PTRACE_INTERRUPT` + (#[1422](https://github.com/nix-rust/nix/pull/1422)) +- Added `IP6T_SO_ORIGINAL_DST` sockopt. + (#[1490](https://github.com/nix-rust/nix/pull/1490)) +- Added the `PTRACE_EVENT_STOP` variant to the `sys::ptrace::Event` enum + (#[1335](https://github.com/nix-rust/nix/pull/1335)) +- Exposed `SockAddr::from_raw_sockaddr` + (#[1447](https://github.com/nix-rust/nix/pull/1447)) +- Added `TcpRepair` + (#[1503](https://github.com/nix-rust/nix/pull/1503)) +- Enabled `pwritev` and `preadv` for more operating systems. + (#[1511](https://github.com/nix-rust/nix/pull/1511)) +- Added support for `TCP_MAXSEG` TCP Maximum Segment Size socket options + (#[1292](https://github.com/nix-rust/nix/pull/1292)) +- Added `Ipv4RecvErr` and `Ipv6RecvErr` sockopts and associated control messages. + (#[1514](https://github.com/nix-rust/nix/pull/1514)) +- Added `AsRawFd` implementation on `PollFd`. + (#[1516](https://github.com/nix-rust/nix/pull/1516)) +- Added `Ipv4Ttl` and `Ipv6Ttl` sockopts. + (#[1515](https://github.com/nix-rust/nix/pull/1515)) +- Added `MAP_EXCL`, `MAP_ALIGNED_SUPER`, and `MAP_CONCEAL` mmap flags, and + exposed `MAP_ANONYMOUS` for all operating systems. + (#[1522](https://github.com/nix-rust/nix/pull/1522)) + (#[1525](https://github.com/nix-rust/nix/pull/1525)) + (#[1531](https://github.com/nix-rust/nix/pull/1531)) + (#[1534](https://github.com/nix-rust/nix/pull/1534)) +- Added read/write accessors for 'events' on `PollFd`. + (#[1517](https://github.com/nix-rust/nix/pull/1517)) + +### Changed + +- `FdSet::{contains, highest, fds}` no longer require a mutable reference. + (#[1464](https://github.com/nix-rust/nix/pull/1464)) +- `User::gecos` and corresponding `libc::passwd::pw_gecos` are supported on + 64-bit Android, change conditional compilation to include the field in + 64-bit Android builds + (#[1471](https://github.com/nix-rust/nix/pull/1471)) +- `eventfd`s are supported on Android, change conditional compilation to + include `sys::eventfd::eventfd` and `sys::eventfd::EfdFlags`for Android + builds. + (#[1481](https://github.com/nix-rust/nix/pull/1481)) +- Most enums that come from C, for example `Errno`, are now marked as + `#[non_exhaustive]`. + (#[1474](https://github.com/nix-rust/nix/pull/1474)) +- Many more functions, mostly contructors, are now `const`. + (#[1476](https://github.com/nix-rust/nix/pull/1476)) + (#[1492](https://github.com/nix-rust/nix/pull/1492)) +- `sys::event::KEvent::filter` now returns a `Result` instead of being + infalliable. The only cases where it will now return an error are cases + where it previously would've had undefined behavior. + (#[1484](https://github.com/nix-rust/nix/pull/1484)) +- Minimum supported Rust version is now 1.46.0. + ([#1492](https://github.com/nix-rust/nix/pull/1492)) +- Rework `UnixAddr` to encapsulate internals better in order to fix soundness + issues. No longer allows creating a `UnixAddr` from a raw `sockaddr_un`. + ([#1496](https://github.com/nix-rust/nix/pull/1496)) +- Raised bitflags to 1.3.0 and the MSRV to 1.46.0. + ([#1492](https://github.com/nix-rust/nix/pull/1492)) + +### Fixed + +- `posix_fadvise` now returns errors in the conventional way, rather than as a + non-zero value in `Ok()`. + (#[1538](https://github.com/nix-rust/nix/pull/1538)) +- Added more errno definitions for better backwards compatibility with + Nix 0.21.0. + (#[1467](https://github.com/nix-rust/nix/pull/1467)) +- Fixed potential undefined behavior in `Signal::try_from` on some platforms. + (#[1484](https://github.com/nix-rust/nix/pull/1484)) +- Fixed buffer overflow in `unistd::getgrouplist`. + (#[1545](https://github.com/nix-rust/nix/pull/1545)) + + +### Removed + +- Removed a couple of termios constants on redox that were never actually + supported. + (#[1483](https://github.com/nix-rust/nix/pull/1483)) +- Removed `nix::sys::signal::NSIG`. It was of dubious utility, and not correct + for all platforms. + (#[1484](https://github.com/nix-rust/nix/pull/1484)) +- Removed support for 32-bit Apple targets, since they've been dropped by both + Rustc and Xcode. + (#[1492](https://github.com/nix-rust/nix/pull/1492)) +- Deprecated `SockAddr/InetAddr::to_str` in favor of `ToString::to_string` + (#[1495](https://github.com/nix-rust/nix/pull/1495)) +- Removed `SigevNotify` on OpenBSD and Redox. + (#[1511](https://github.com/nix-rust/nix/pull/1511)) + +## [0.22.0] - 9 July 2021 +### Added +- Added `if_nameindex` (#[1445](https://github.com/nix-rust/nix/pull/1445)) +- Added `nmount` for FreeBSD. + (#[1453](https://github.com/nix-rust/nix/pull/1453)) +- Added `IpFreebind` socket option (sockopt) on Linux, Fuchsia and Android. + (#[1456](https://github.com/nix-rust/nix/pull/1456)) +- Added `TcpUserTimeout` socket option (sockopt) on Linux and Fuchsia. + (#[1457](https://github.com/nix-rust/nix/pull/1457)) +- Added `renameat2` for Linux + (#[1458](https://github.com/nix-rust/nix/pull/1458)) +- Added `RxqOvfl` support on Linux, Fuchsia and Android. + (#[1455](https://github.com/nix-rust/nix/pull/1455)) + +### Changed +- `ptsname_r` now returns a lossily-converted string in the event of bad UTF, + just like `ptsname`. + ([#1446](https://github.com/nix-rust/nix/pull/1446)) +- Nix's error type is now a simple wrapper around the platform's Errno. This + means it is now `Into`. It's also `Clone`, `Copy`, `Eq`, and + has a small fixed size. It also requires less typing. For example, the old + enum variant `nix::Error::Sys(nix::errno::Errno::EINVAL)` is now simply + `nix::Error::EINVAL`. + ([#1446](https://github.com/nix-rust/nix/pull/1446)) + +### Fixed +### Removed + +## [0.21.0] - 31 May 2021 +### Added +- Added `getresuid` and `getresgid` + (#[1430](https://github.com/nix-rust/nix/pull/1430)) +- Added TIMESTAMPNS support for linux + (#[1402](https://github.com/nix-rust/nix/pull/1402)) +- Added `sendfile64` (#[1439](https://github.com/nix-rust/nix/pull/1439)) +- Added `MS_LAZYTIME` to `MsFlags` + (#[1437](https://github.com/nix-rust/nix/pull/1437)) + +### Changed +- Made `forkpty` unsafe, like `fork` + (#[1390](https://github.com/nix-rust/nix/pull/1390)) +- Made `Uid`, `Gid` and `Pid` methods `from_raw` and `as_raw` a `const fn` + (#[1429](https://github.com/nix-rust/nix/pull/1429)) +- Made `Uid::is_root` a `const fn` + (#[1429](https://github.com/nix-rust/nix/pull/1429)) +- `AioCb` is now always pinned. Once a `libc::aiocb` gets sent to the kernel, + its address in memory must not change. Nix now enforces that by using + `std::pin`. Most users won't need to change anything, except when using + `aio_suspend`. See that method's documentation for the new usage. + (#[1440](https://github.com/nix-rust/nix/pull/1440)) +- `LioCb` is now constructed using a distinct `LioCbBuilder` struct. This + avoids a soundness issue with the old `LioCb`. Usage is similar but + construction now uses the builder pattern. See the documentation for + details. + (#[1440](https://github.com/nix-rust/nix/pull/1440)) +- Minimum supported Rust version is now 1.41.0. + ([#1440](https://github.com/nix-rust/nix/pull/1440)) +- Errno aliases are now associated consts on `Errno`, instead of consts in the + `errno` module. + (#[1452](https://github.com/nix-rust/nix/pull/1452)) + +### Fixed +- Allow `sockaddr_ll` size, as reported by the Linux kernel, to be smaller then it's definition + (#[1395](https://github.com/nix-rust/nix/pull/1395)) +- Fix spurious errors using `sendmmsg` with multiple cmsgs + (#[1414](https://github.com/nix-rust/nix/pull/1414)) +- Added `Errno::EOPNOTSUPP` to FreeBSD, where it was missing. + (#[1452](https://github.com/nix-rust/nix/pull/1452)) + +### Removed + +- Removed `sys::socket::accept4` from Android arm because libc removed it in + version 0.2.87. + ([#1399](https://github.com/nix-rust/nix/pull/1399)) +- `AioCb::from_boxed_slice` and `AioCb::from_boxed_mut_slice` have been + removed. They were useful with earlier versions of Rust, but should no + longer be needed now that async/await are available. `AioCb`s now work + exclusively with borrowed buffers, not owned ones. + (#[1440](https://github.com/nix-rust/nix/pull/1440)) +- Removed some Errno values from platforms where they aren't actually defined. + (#[1452](https://github.com/nix-rust/nix/pull/1452)) + +## [0.20.0] - 20 February 2021 +### Added + +- Added a `passwd` field to `Group` (#[1338](https://github.com/nix-rust/nix/pull/1338)) +- Added `mremap` (#[1306](https://github.com/nix-rust/nix/pull/1306)) +- Added `personality` (#[1331](https://github.com/nix-rust/nix/pull/1331)) +- Added limited Fuchsia support (#[1285](https://github.com/nix-rust/nix/pull/1285)) +- Added `getpeereid` (#[1342](https://github.com/nix-rust/nix/pull/1342)) +- Implemented `IntoIterator` for `Dir` + (#[1333](https://github.com/nix-rust/nix/pull/1333)). + +### Changed + +- Minimum supported Rust version is now 1.40.0. + ([#1356](https://github.com/nix-rust/nix/pull/1356)) +- i686-apple-darwin has been demoted to Tier 2 support, because it's deprecated + by Xcode. + (#[1350](https://github.com/nix-rust/nix/pull/1350)) +- Fixed calling `recvfrom` on an `AddrFamily::Packet` socket + (#[1344](https://github.com/nix-rust/nix/pull/1344)) + +### Fixed +- `TimerFd` now closes the underlying fd on drop. + ([#1381](https://github.com/nix-rust/nix/pull/1381)) +- Define `*_MAGIC` filesystem constants on Linux s390x + (#[1372](https://github.com/nix-rust/nix/pull/1372)) +- mqueue, sysinfo, timespec, statfs, test_ptrace_syscall() on x32 + (#[1366](https://github.com/nix-rust/nix/pull/1366)) + +### Removed + +- `Dir`, `SignalFd`, and `PtyMaster` are no longer `Clone`. + (#[1382](https://github.com/nix-rust/nix/pull/1382)) +- Removed `SockLevel`, which hasn't been used for a few years + (#[1362](https://github.com/nix-rust/nix/pull/1362)) +- Removed both `Copy` and `Clone` from `TimerFd`. + ([#1381](https://github.com/nix-rust/nix/pull/1381)) + +## [0.19.1] - 28 November 2020 +### Fixed +- Fixed bugs in `recvmmsg`. + (#[1341](https://github.com/nix-rust/nix/pull/1341)) + +## [0.19.0] - 6 October 2020 +### Added +- Added Netlink protocol families to the `SockProtocol` enum + (#[1289](https://github.com/nix-rust/nix/pull/1289)) +- Added `clock_gettime`, `clock_settime`, `clock_getres`, + `clock_getcpuclockid` functions and `ClockId` struct. + (#[1281](https://github.com/nix-rust/nix/pull/1281)) +- Added wrapper functions for `PTRACE_SYSEMU` and `PTRACE_SYSEMU_SINGLESTEP`. + (#[1300](https://github.com/nix-rust/nix/pull/1300)) +- Add support for Vsock on Android rather than just Linux. + (#[1301](https://github.com/nix-rust/nix/pull/1301)) +- Added `TCP_KEEPCNT` and `TCP_KEEPINTVL` TCP keepalive options. + (#[1283](https://github.com/nix-rust/nix/pull/1283)) +### Changed +- Expose `SeekData` and `SeekHole` on all Linux targets + (#[1284](https://github.com/nix-rust/nix/pull/1284)) +- Changed unistd::{execv,execve,execvp,execvpe,fexecve,execveat} to take both `&[&CStr]` and `&[CString]` as its list argument(s). + (#[1278](https://github.com/nix-rust/nix/pull/1278)) +- Made `unistd::fork` an unsafe funtion, bringing it in line with [libstd's decision](https://github.com/rust-lang/rust/pull/58059). + (#[1293](https://github.com/nix-rust/nix/pull/1293)) +### Fixed +### Removed + +## [0.18.0] - 26 July 2020 +### Added +- Added `fchown(2)` wrapper. + (#[1257](https://github.com/nix-rust/nix/pull/1257)) +- Added support on linux systems for `MAP_HUGE_`_`SIZE`_ family of flags. + (#[1211](https://github.com/nix-rust/nix/pull/1211)) +- Added support for `F_OFD_*` `fcntl` commands on Linux and Android. + (#[1195](https://github.com/nix-rust/nix/pull/1195)) +- Added `env::clearenv()`: calls `libc::clearenv` on platforms + where it's available, and clears the environment of all variables + via `std::env::vars` and `std::env::remove_var` on others. + (#[1185](https://github.com/nix-rust/nix/pull/1185)) +- `FsType` inner value made public. + (#[1187](https://github.com/nix-rust/nix/pull/1187)) +- Added `unistd::setfsuid` and `unistd::setfsgid` to set the user or group + identity for filesystem checks per-thread. + (#[1163](https://github.com/nix-rust/nix/pull/1163)) +- Derived `Ord`, `PartialOrd` for `unistd::Pid` (#[1189](https://github.com/nix-rust/nix/pull/1189)) +- Added `select::FdSet::fds` method to iterate over file descriptors in a set. + ([#1207](https://github.com/nix-rust/nix/pull/1207)) +- Added support for UDP generic segmentation offload (GSO) and generic + receive offload (GRO) ([#1209](https://github.com/nix-rust/nix/pull/1209)) +- Added support for `sendmmsg` and `recvmmsg` calls + (#[1208](https://github.com/nix-rust/nix/pull/1208)) +- Added support for `SCM_CREDS` messages (`UnixCredentials`) on FreeBSD/DragonFly + (#[1216](https://github.com/nix-rust/nix/pull/1216)) +- Added `BindToDevice` socket option (sockopt) on Linux + (#[1233](https://github.com/nix-rust/nix/pull/1233)) +- Added `EventFilter` bitflags for `EV_DISPATCH` and `EV_RECEIPT` on OpenBSD. + (#[1252](https://github.com/nix-rust/nix/pull/1252)) +- Added support for `Ipv4PacketInfo` and `Ipv6PacketInfo` to `ControlMessage`. + (#[1222](https://github.com/nix-rust/nix/pull/1222)) +- `CpuSet` and `UnixCredentials` now implement `Default`. + (#[1244](https://github.com/nix-rust/nix/pull/1244)) +- Added `unistd::ttyname` + (#[1259](https://github.com/nix-rust/nix/pull/1259)) +- Added support for `Ipv4PacketInfo` and `Ipv6PacketInfo` to `ControlMessage` for iOS and Android. + (#[1265](https://github.com/nix-rust/nix/pull/1265)) +- Added support for `TimerFd`. + (#[1261](https://github.com/nix-rust/nix/pull/1261)) + +### Changed +- Changed `fallocate` return type from `c_int` to `()` (#[1201](https://github.com/nix-rust/nix/pull/1201)) +- Enabled `sys::ptrace::setregs` and `sys::ptrace::getregs` on x86_64-unknown-linux-musl target + (#[1198](https://github.com/nix-rust/nix/pull/1198)) +- On Linux, `ptrace::write` is now an `unsafe` function. Caveat programmer. + (#[1245](https://github.com/nix-rust/nix/pull/1245)) +- `execv`, `execve`, `execvp` and `execveat` in `::nix::unistd` and `reboot` in + `::nix::sys::reboot` now return `Result` instead of `Result` (#[1239](https://github.com/nix-rust/nix/pull/1239)) +- `sys::socket::sockaddr_storage_to_addr` is no longer `unsafe`. So is + `offset_of!`. +- `sys::socket::sockaddr_storage_to_addr`, `offset_of!`, and `Errno::clear` are + no longer `unsafe`. +- `SockAddr::as_ffi_pair`,`sys::socket::sockaddr_storage_to_addr`, `offset_of!`, + and `Errno::clear` are no longer `unsafe`. + (#[1244](https://github.com/nix-rust/nix/pull/1244)) +- Several `Inotify` methods now take `self` by value instead of by reference + (#[1244](https://github.com/nix-rust/nix/pull/1244)) +- `nix::poll::ppoll`: `timeout` parameter is now optional, None is equivalent for infinite timeout. + +### Fixed + +- Fixed `getsockopt`. The old code produced UB which triggers a panic with + Rust 1.44.0. + (#[1214](https://github.com/nix-rust/nix/pull/1214)) + +- Fixed a bug in nix::unistd that would result in an infinite loop + when a group or user lookup required a buffer larger than + 16KB. (#[1198](https://github.com/nix-rust/nix/pull/1198)) +- Fixed unaligned casting of `cmsg_data` to `af_alg_iv` (#[1206](https://github.com/nix-rust/nix/pull/1206)) +- Fixed `readlink`/`readlinkat` when reading symlinks longer than `PATH_MAX` (#[1231](https://github.com/nix-rust/nix/pull/1231)) +- `PollFd`, `EpollEvent`, `IpMembershipRequest`, `Ipv6MembershipRequest`, + `TimeVal`, and `IoVec` are now `repr(transparent)`. This is required for + correctness's sake across all architectures and compilers, though now bugs + have been reported so far. + (#[1243](https://github.com/nix-rust/nix/pull/1243)) +- Fixed unaligned pointer read in `Inotify::read_events`. + (#[1244](https://github.com/nix-rust/nix/pull/1244)) + +### Removed + +- Removed `sys::socket::addr::from_libc_sockaddr` from the public API. + (#[1215](https://github.com/nix-rust/nix/pull/1215)) +- Removed `sys::termios::{get_libc_termios, get_libc_termios_mut, update_wrapper` + from the public API. These were previously hidden in the docs but still usable + by downstream. + (#[1235](https://github.com/nix-rust/nix/pull/1235)) + +- Nix no longer implements `NixPath` for `Option

where P: NixPath`. Most + Nix functions that accept `NixPath` arguments can't do anything useful with + `None`. The exceptions (`mount` and `quotactl_sync`) already take explicitly + optional arguments. + (#[1242](https://github.com/nix-rust/nix/pull/1242)) + +- Removed `unistd::daemon` and `unistd::pipe2` on OSX and ios + (#[1255](https://github.com/nix-rust/nix/pull/1255)) + +- Removed `sys::event::FilterFlag::NOTE_EXIT_REPARENTED` and + `sys::event::FilterFlag::NOTE_REAP` on OSX and ios. + (#[1255](https://github.com/nix-rust/nix/pull/1255)) + +- Removed `sys::ptrace::ptrace` on Android and Linux. + (#[1255](https://github.com/nix-rust/nix/pull/1255)) + +- Dropped support for powerpc64-unknown-linux-gnu + (#[1266](https://github.com/nix-rust/nix/pull/1268)) + +## [0.17.0] - 3 February 2020 +### Added +- Add `CLK_TCK` to `SysconfVar` + (#[1177](https://github.com/nix-rust/nix/pull/1177)) +### Changed +### Fixed +### Removed +- Removed deprecated Error::description from error types + (#[1175](https://github.com/nix-rust/nix/pull/1175)) + +## [0.16.1] - 23 December 2019 +### Added +### Changed +### Fixed + +- Fixed the build for OpenBSD + (#[1168](https://github.com/nix-rust/nix/pull/1168)) + +### Removed + +## [0.16.0] - 1 December 2019 +### Added +- Added `ptrace::seize()`: similar to `attach()` on Linux + but with better-defined semantics. + (#[1154](https://github.com/nix-rust/nix/pull/1154)) + +- Added `Signal::as_str()`: returns signal name as `&'static str` + (#[1138](https://github.com/nix-rust/nix/pull/1138)) + +- Added `posix_fallocate`. + ([#1105](https://github.com/nix-rust/nix/pull/1105)) + +- Implemented `Default` for `FdSet` + ([#1107](https://github.com/nix-rust/nix/pull/1107)) + +- Added `NixPath::is_empty`. + ([#1107](https://github.com/nix-rust/nix/pull/1107)) + +- Added `mkfifoat` + ([#1133](https://github.com/nix-rust/nix/pull/1133)) + +- Added `User::from_uid`, `User::from_name`, `User::from_gid` and + `Group::from_name`, + ([#1139](https://github.com/nix-rust/nix/pull/1139)) + +- Added `linkat` + ([#1101](https://github.com/nix-rust/nix/pull/1101)) + +- Added `sched_getaffinity`. + ([#1148](https://github.com/nix-rust/nix/pull/1148)) + +- Added optional `Signal` argument to `ptrace::{detach, syscall}` for signal + injection. ([#1083](https://github.com/nix-rust/nix/pull/1083)) + +### Changed +- `sys::termios::BaudRate` now implements `TryFrom` instead of + `From`. The old `From` implementation would panic on failure. + ([#1159](https://github.com/nix-rust/nix/pull/1159)) + +- `sys::socket::ControlMessage::ScmCredentials` and + `sys::socket::ControlMessageOwned::ScmCredentials` now wrap `UnixCredentials` + rather than `libc::ucred`. + ([#1160](https://github.com/nix-rust/nix/pull/1160)) + +- `sys::socket::recvmsg` now takes a plain `Vec` instead of a `CmsgBuffer` + implementor. If you were already using `cmsg_space!`, then you needn't worry. + ([#1156](https://github.com/nix-rust/nix/pull/1156)) + +- `sys::socket::recvfrom` now returns + `Result<(usize, Option)>` instead of `Result<(usize, SockAddr)>`. + ([#1145](https://github.com/nix-rust/nix/pull/1145)) + +- `Signal::from_c_int` has been replaced by `Signal::try_from` + ([#1113](https://github.com/nix-rust/nix/pull/1113)) + +- Changed `readlink` and `readlinkat` to return `OsString` + ([#1109](https://github.com/nix-rust/nix/pull/1109)) + + ```rust + # use nix::fcntl::{readlink, readlinkat}; + // the buffer argument of `readlink` and `readlinkat` has been removed, + // and the return value is now an owned type (`OsString`). + // Existing code can be updated by removing the buffer argument + // and removing any clone or similar operation on the output + + // old code `readlink(&path, &mut buf)` can be replaced with the following + let _: OsString = readlink(&path); + + // old code `readlinkat(dirfd, &path, &mut buf)` can be replaced with the following + let _: OsString = readlinkat(dirfd, &path); + ``` + +- Minimum supported Rust version is now 1.36.0. + ([#1108](https://github.com/nix-rust/nix/pull/1108)) + +- `Ipv4Addr::octets`, `Ipv4Addr::to_std`, `Error::as_errno`, + `ForkResult::is_child`, `ForkResult::is_parent`, `Gid::as_raw`, + `Uid::is_root`, `Uid::as_raw`, `Pid::as_raw`, and `PollFd::revents` now take + `self` by value. + ([#1107](https://github.com/nix-rust/nix/pull/1107)) + +- Type `&CString` for parameters of `exec(v|ve|vp|vpe|veat)` are changed to `&CStr`. + ([#1121](https://github.com/nix-rust/nix/pull/1121)) + +### Fixed +- Fix length of abstract socket addresses + ([#1120](https://github.com/nix-rust/nix/pull/1120)) + +- Fix initialization of msghdr in recvmsg/sendmsg when built with musl + ([#1136](https://github.com/nix-rust/nix/pull/1136)) + +### Removed +- Remove the deprecated `CmsgSpace`. + ([#1156](https://github.com/nix-rust/nix/pull/1156)) + +## [0.15.0] - 10 August 2019 +### Added +- Added `MSG_WAITALL` to `MsgFlags` in `sys::socket`. + ([#1079](https://github.com/nix-rust/nix/pull/1079)) +- Implemented `Clone`, `Copy`, `Debug`, `Eq`, `Hash`, and `PartialEq` for most + types that support them. ([#1035](https://github.com/nix-rust/nix/pull/1035)) +- Added `copy_file_range` wrapper + ([#1069](https://github.com/nix-rust/nix/pull/1069)) +- Add `mkdirat`. + ([#1084](https://github.com/nix-rust/nix/pull/1084)) +- Add `posix_fadvise`. + ([#1089](https://github.com/nix-rust/nix/pull/1089)) +- Added `AF_VSOCK` to `AddressFamily`. + ([#1091](https://github.com/nix-rust/nix/pull/1091)) +- Add `unlinkat` + ([#1058](https://github.com/nix-rust/nix/pull/1058)) +- Add `renameat`. + ([#1097](https://github.com/nix-rust/nix/pull/1097)) + +### Changed +- Support for `ifaddrs` now present when building for Android. + ([#1077](https://github.com/nix-rust/nix/pull/1077)) +- Minimum supported Rust version is now 1.31.0 + ([#1035](https://github.com/nix-rust/nix/pull/1035)) + ([#1095](https://github.com/nix-rust/nix/pull/1095)) +- Now functions `statfs()` and `fstatfs()` return result with `Statfs` wrapper + ([#928](https://github.com/nix-rust/nix/pull/928)) + +### Fixed +- Enabled `sched_yield` for all nix hosts. + ([#1090](https://github.com/nix-rust/nix/pull/1090)) + +### Removed + +## [0.14.1] - 2019-06-06 +### Added +- Macros exported by `nix` may now be imported via `use` on the Rust 2018 + edition without importing helper macros on Linux targets. + ([#1066](https://github.com/nix-rust/nix/pull/1066)) + + For example, in Rust 2018, the `ioctl_read_bad!` macro can now be imported + without importing the `convert_ioctl_res!` macro. + + ```rust + use nix::ioctl_read_bad; + + ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios); + ``` + +### Changed +- Changed some public types from reexports of libc types like `uint32_t` to the + native equivalents like `u32.` + ([#1072](https://github.com/nix-rust/nix/pull/1072/commits)) + +### Fixed +- Fix the build on Android and Linux/mips with recent versions of libc. + ([#1072](https://github.com/nix-rust/nix/pull/1072/commits)) + +### Removed + +## [0.14.0] - 2019-05-21 +### Added +- Add IP_RECVIF & IP_RECVDSTADDR. Enable IP_PKTINFO and IP6_PKTINFO on netbsd/openbsd. + ([#1002](https://github.com/nix-rust/nix/pull/1002)) +- Added `inotify_init1`, `inotify_add_watch` and `inotify_rm_watch` wrappers for + Android and Linux. ([#1016](https://github.com/nix-rust/nix/pull/1016)) +- Add `ALG_SET_IV`, `ALG_SET_OP` and `ALG_SET_AEAD_ASSOCLEN` control messages and `AF_ALG` + socket types on Linux and Android ([#1031](https://github.com/nix-rust/nix/pull/1031)) +- Add killpg + ([#1034](https://github.com/nix-rust/nix/pull/1034)) +- Added ENOTSUP errno support for Linux and Android. + ([#969](https://github.com/nix-rust/nix/pull/969)) +- Add several errno constants from OpenBSD 6.2 + ([#1036](https://github.com/nix-rust/nix/pull/1036)) +- Added `from_std` and `to_std` methods for `sys::socket::IpAddr` + ([#1043](https://github.com/nix-rust/nix/pull/1043)) +- Added `nix::unistd:seteuid` and `nix::unistd::setegid` for those platforms that do + not support `setresuid` nor `setresgid` respectively. + ([#1044](https://github.com/nix-rust/nix/pull/1044)) +- Added a `access` wrapper + ([#1045](https://github.com/nix-rust/nix/pull/1045)) +- Add `forkpty` + ([#1042](https://github.com/nix-rust/nix/pull/1042)) +- Add `sched_yield` + ([#1050](https://github.com/nix-rust/nix/pull/1050)) + +### Changed +- `PollFd` event flags renamed to `PollFlags` ([#1024](https://github.com/nix-rust/nix/pull/1024/)) +- `recvmsg` now returns an Iterator over `ControlMessageOwned` objects rather + than `ControlMessage` objects. This is sadly not backwards-compatible. Fix + code like this: + ```rust + if let ControlMessage::ScmRights(&fds) = cmsg { + ``` + + By replacing it with code like this: + ```rust + if let ControlMessageOwned::ScmRights(fds) = cmsg { + ``` + ([#1020](https://github.com/nix-rust/nix/pull/1020)) +- Replaced `CmsgSpace` with the `cmsg_space` macro. + ([#1020](https://github.com/nix-rust/nix/pull/1020)) + +### Fixed +- Fixed multiple bugs when using `sendmsg` and `recvmsg` with ancillary control messages + ([#1020](https://github.com/nix-rust/nix/pull/1020)) +- Macros exported by `nix` may now be imported via `use` on the Rust 2018 + edition without importing helper macros for BSD targets. + ([#1041](https://github.com/nix-rust/nix/pull/1041)) + + For example, in Rust 2018, the `ioctl_read_bad!` macro can now be imported + without importing the `convert_ioctl_res!` macro. + + ```rust + use nix::ioctl_read_bad; + + ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios); + ``` + +### Removed +- `Daemon`, `NOTE_REAP`, and `NOTE_EXIT_REPARENTED` are now deprecated on OSX + and iOS. + ([#1033](https://github.com/nix-rust/nix/pull/1033)) +- `PTRACE_GETREGS`, `PTRACE_SETREGS`, `PTRACE_GETFPREGS`, and + `PTRACE_SETFPREGS` have been removed from some platforms where they never + should've been defined in the first place. + ([#1055](https://github.com/nix-rust/nix/pull/1055)) + +## [0.13.0] - 2019-01-15 +### Added +- Added PKTINFO(V4) & V6PKTINFO cmsg support - Android/FreeBSD/iOS/Linux/MacOS. + ([#990](https://github.com/nix-rust/nix/pull/990)) +- Added support of CString type in `setsockopt`. + ([#972](https://github.com/nix-rust/nix/pull/972)) +- Added option `TCP_CONGESTION` in `setsockopt`. + ([#972](https://github.com/nix-rust/nix/pull/972)) +- Added `symlinkat` wrapper. + ([#997](https://github.com/nix-rust/nix/pull/997)) +- Added `ptrace::{getregs, setregs}`. + ([#1010](https://github.com/nix-rust/nix/pull/1010)) +- Added `nix::sys::signal::signal`. + ([#817](https://github.com/nix-rust/nix/pull/817)) +- Added an `mprotect` wrapper. + ([#991](https://github.com/nix-rust/nix/pull/991)) + +### Changed +### Fixed +- `lutimes` never worked on OpenBSD as it is not implemented on OpenBSD. It has + been removed. ([#1000](https://github.com/nix-rust/nix/pull/1000)) +- `fexecve` never worked on NetBSD or on OpenBSD as it is not implemented on + either OS. It has been removed. ([#1000](https://github.com/nix-rust/nix/pull/1000)) + +### Removed + +## [0.12.0] 2018-11-28 + +### Added +- Added `FromStr` and `Display` impls for `nix::sys::Signal` + ([#884](https://github.com/nix-rust/nix/pull/884)) +- Added a `sync` wrapper. + ([#961](https://github.com/nix-rust/nix/pull/961)) +- Added a `sysinfo` wrapper. + ([#922](https://github.com/nix-rust/nix/pull/922)) +- Support the `SO_PEERCRED` socket option and the `UnixCredentials` type on all Linux and Android targets. + ([#921](https://github.com/nix-rust/nix/pull/921)) +- Added support for `SCM_CREDENTIALS`, allowing to send process credentials over Unix sockets. + ([#923](https://github.com/nix-rust/nix/pull/923)) +- Added a `dir` module for reading directories (wraps `fdopendir`, `readdir`, and `rewinddir`). + ([#916](https://github.com/nix-rust/nix/pull/916)) +- Added `kmod` module that allows loading and unloading kernel modules on Linux. + ([#930](https://github.com/nix-rust/nix/pull/930)) +- Added `futimens` and `utimesat` wrappers ([#944](https://github.com/nix-rust/nix/pull/944)), + an `lutimes` wrapper ([#967](https://github.com/nix-rust/nix/pull/967)), + and a `utimes` wrapper ([#946](https://github.com/nix-rust/nix/pull/946)). +- Added `AF_UNSPEC` wrapper to `AddressFamily` ([#948](https://github.com/nix-rust/nix/pull/948)) +- Added the `mode_t` public alias within `sys::stat`. + ([#954](https://github.com/nix-rust/nix/pull/954)) +- Added a `truncate` wrapper. + ([#956](https://github.com/nix-rust/nix/pull/956)) +- Added a `fchownat` wrapper. + ([#955](https://github.com/nix-rust/nix/pull/955)) +- Added support for `ptrace` on BSD operating systems ([#949](https://github.com/nix-rust/nix/pull/949)) +- Added `ptrace` functions for reads and writes to tracee memory and ptrace kill + ([#949](https://github.com/nix-rust/nix/pull/949)) ([#958](https://github.com/nix-rust/nix/pull/958)) +- Added a `acct` wrapper module for enabling and disabling process accounting + ([#952](https://github.com/nix-rust/nix/pull/952)) +- Added the `time_t` and `suseconds_t` public aliases within `sys::time`. + ([#968](https://github.com/nix-rust/nix/pull/968)) +- Added `unistd::execvpe` for Haiku, Linux and OpenBSD + ([#975](https://github.com/nix-rust/nix/pull/975)) +- Added `Error::as_errno`. + ([#977](https://github.com/nix-rust/nix/pull/977)) + +### Changed +- Increased required Rust version to 1.24.1 + ([#900](https://github.com/nix-rust/nix/pull/900)) + ([#966](https://github.com/nix-rust/nix/pull/966)) + +### Fixed +- Made `preadv` take immutable slice of IoVec. + ([#914](https://github.com/nix-rust/nix/pull/914)) +- Fixed passing multiple file descriptors over Unix Sockets. + ([#918](https://github.com/nix-rust/nix/pull/918)) + +### Removed + +## [0.11.0] 2018-06-01 + +### Added +- Added `sendfile` on FreeBSD and Darwin. + ([#901](https://github.com/nix-rust/nix/pull/901)) +- Added `pselect` + ([#894](https://github.com/nix-rust/nix/pull/894)) +- Exposed `preadv` and `pwritev` on the BSDs. + ([#883](https://github.com/nix-rust/nix/pull/883)) +- Added `mlockall` and `munlockall` + ([#876](https://github.com/nix-rust/nix/pull/876)) +- Added `SO_MARK` on Linux. + ([#873](https://github.com/nix-rust/nix/pull/873)) +- Added safe support for nearly any buffer type in the `sys::aio` module. + ([#872](https://github.com/nix-rust/nix/pull/872)) +- Added `sys::aio::LioCb` as a wrapper for `libc::lio_listio`. + ([#872](https://github.com/nix-rust/nix/pull/872)) +- Added `unistd::getsid` + ([#850](https://github.com/nix-rust/nix/pull/850)) +- Added `alarm`. ([#830](https://github.com/nix-rust/nix/pull/830)) +- Added interface flags `IFF_NO_PI, IFF_TUN, IFF_TAP` on linux-like systems. + ([#853](https://github.com/nix-rust/nix/pull/853)) +- Added `statvfs` module to all MacOS and Linux architectures. + ([#832](https://github.com/nix-rust/nix/pull/832)) +- Added `EVFILT_EMPTY`, `EVFILT_PROCDESC`, and `EVFILT_SENDFILE` on FreeBSD. + ([#825](https://github.com/nix-rust/nix/pull/825)) +- Exposed `termios::cfmakesane` on FreeBSD. + ([#825](https://github.com/nix-rust/nix/pull/825)) +- Exposed `MSG_CMSG_CLOEXEC` on *BSD. + ([#825](https://github.com/nix-rust/nix/pull/825)) +- Added `fchmod`, `fchmodat`. + ([#857](https://github.com/nix-rust/nix/pull/857)) +- Added `request_code_write_int!` on FreeBSD/DragonFlyBSD + ([#833](https://github.com/nix-rust/nix/pull/833)) + +### Changed +- `Display` and `Debug` for `SysControlAddr` now includes all fields. + ([#837](https://github.com/nix-rust/nix/pull/837)) +- `ioctl!` has been replaced with a family of `ioctl_*!` macros. + ([#833](https://github.com/nix-rust/nix/pull/833)) +- `io!`, `ior!`, `iow!`, and `iorw!` has been renamed to `request_code_none!`, `request_code_read!`, + `request_code_write!`, and `request_code_readwrite!` respectively. These have also now been exposed + in the documentation. + ([#833](https://github.com/nix-rust/nix/pull/833)) +- Enabled more `ptrace::Request` definitions for uncommon Linux platforms + ([#892](https://github.com/nix-rust/nix/pull/892)) +- Emulation of `FD_CLOEXEC` and `O_NONBLOCK` was removed from `socket()`, `accept4()`, and + `socketpair()`. + ([#907](https://github.com/nix-rust/nix/pull/907)) + +### Fixed +- Fixed possible panics when using `SigAction::flags` on Linux + ([#869](https://github.com/nix-rust/nix/pull/869)) +- Properly exposed 460800 and 921600 baud rates on NetBSD + ([#837](https://github.com/nix-rust/nix/pull/837)) +- Fixed `ioctl_write_int!` on FreeBSD/DragonFlyBSD + ([#833](https://github.com/nix-rust/nix/pull/833)) +- `ioctl_write_int!` now properly supports passing a `c_ulong` as the parameter on Linux non-musl targets + ([#833](https://github.com/nix-rust/nix/pull/833)) + +### Removed +- Removed explicit support for the `bytes` crate from the `sys::aio` module. + See `sys::aio::AioCb::from_boxed_slice` examples for alternatives. + ([#872](https://github.com/nix-rust/nix/pull/872)) +- Removed `sys::aio::lio_listio`. Use `sys::aio::LioCb::listio` instead. + ([#872](https://github.com/nix-rust/nix/pull/872)) +- Removed emulated `accept4()` from macos, ios, and netbsd targets + ([#907](https://github.com/nix-rust/nix/pull/907)) +- Removed `IFF_NOTRAILERS` on OpenBSD, as it has been removed in OpenBSD 6.3 + ([#893](https://github.com/nix-rust/nix/pull/893)) + +## [0.10.0] 2018-01-26 + +### Added +- Added specialized wrapper: `sys::ptrace::step` + ([#852](https://github.com/nix-rust/nix/pull/852)) +- Added `AioCb::from_ptr` and `AioCb::from_mut_ptr` + ([#820](https://github.com/nix-rust/nix/pull/820)) +- Added specialized wrappers: `sys::ptrace::{traceme, syscall, cont, attach}`. Using the matching routines + with `sys::ptrace::ptrace` is now deprecated. +- Added `nix::poll` module for all platforms + ([#672](https://github.com/nix-rust/nix/pull/672)) +- Added `nix::ppoll` function for FreeBSD and DragonFly + ([#672](https://github.com/nix-rust/nix/pull/672)) +- Added protocol families in `AddressFamily` enum. + ([#647](https://github.com/nix-rust/nix/pull/647)) +- Added the `pid()` method to `WaitStatus` for extracting the PID. + ([#722](https://github.com/nix-rust/nix/pull/722)) +- Added `nix::unistd:fexecve`. + ([#727](https://github.com/nix-rust/nix/pull/727)) +- Expose `uname()` on all platforms. + ([#739](https://github.com/nix-rust/nix/pull/739)) +- Expose `signalfd` module on Android as well. + ([#739](https://github.com/nix-rust/nix/pull/739)) +- Added `nix::sys::ptrace::detach`. + ([#749](https://github.com/nix-rust/nix/pull/749)) +- Added timestamp socket control message variant: + `nix::sys::socket::ControlMessage::ScmTimestamp` + ([#663](https://github.com/nix-rust/nix/pull/663)) +- Added socket option variant that enables the timestamp socket + control message: `nix::sys::socket::sockopt::ReceiveTimestamp` + ([#663](https://github.com/nix-rust/nix/pull/663)) +- Added more accessor methods for `AioCb` + ([#773](https://github.com/nix-rust/nix/pull/773)) +- Add `nix::sys::fallocate` + ([#768](https:://github.com/nix-rust/nix/pull/768)) +- Added `nix::unistd::mkfifo`. + ([#602](https://github.com/nix-rust/nix/pull/774)) +- Added `ptrace::Options::PTRACE_O_EXITKILL` on Linux and Android. + ([#771](https://github.com/nix-rust/nix/pull/771)) +- Added `nix::sys::uio::{process_vm_readv, process_vm_writev}` on Linux + ([#568](https://github.com/nix-rust/nix/pull/568)) +- Added `nix::unistd::{getgroups, setgroups, getgrouplist, initgroups}`. ([#733](https://github.com/nix-rust/nix/pull/733)) +- Added `nix::sys::socket::UnixAddr::as_abstract` on Linux and Android. + ([#785](https://github.com/nix-rust/nix/pull/785)) +- Added `nix::unistd::execveat` on Linux and Android. + ([#800](https://github.com/nix-rust/nix/pull/800)) +- Added the `from_raw()` method to `WaitStatus` for converting raw status values + to `WaitStatus` independent of syscalls. + ([#741](https://github.com/nix-rust/nix/pull/741)) +- Added more standard trait implementations for various types. + ([#814](https://github.com/nix-rust/nix/pull/814)) +- Added `sigprocmask` to the signal module. + ([#826](https://github.com/nix-rust/nix/pull/826)) +- Added `nix::sys::socket::LinkAddr` on Linux and all bsdlike system. + ([#813](https://github.com/nix-rust/nix/pull/813)) +- Add socket options for `IP_TRANSPARENT` / `BIND_ANY`. + ([#835](https://github.com/nix-rust/nix/pull/835)) + +### Changed +- Exposed the `mqueue` module for all supported operating systems. + ([#834](https://github.com/nix-rust/nix/pull/834)) +- Use native `pipe2` on all BSD targets. Users should notice no difference. + ([#777](https://github.com/nix-rust/nix/pull/777)) +- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692)) +- Marked `sys::ptrace::ptrace` as `unsafe`. +- Changed function signature of `socket()` and `socketpair()`. The `protocol` argument + has changed type from `c_int` to `SockProtocol`. + It accepts a `None` value for default protocol that was specified with zero using `c_int`. + ([#647](https://github.com/nix-rust/nix/pull/647)) +- Made `select` easier to use, adding the ability to automatically calculate the `nfds` parameter using the new + `FdSet::highest` ([#701](https://github.com/nix-rust/nix/pull/701)) +- Exposed `unistd::setresuid` and `unistd::setresgid` on FreeBSD and OpenBSD + ([#721](https://github.com/nix-rust/nix/pull/721)) +- Refactored the `statvfs` module removing extraneous API functions and the + `statvfs::vfs` module. Additionally `(f)statvfs()` now return the struct + directly. And the returned `Statvfs` struct now exposes its data through + accessor methods. ([#729](https://github.com/nix-rust/nix/pull/729)) +- The `addr` argument to `madvise` and `msync` is now `*mut` to better match the + libc API. ([#731](https://github.com/nix-rust/nix/pull/731)) +- `shm_open` and `shm_unlink` are no longer exposed on Android targets, where + they are not officially supported. ([#731](https://github.com/nix-rust/nix/pull/731)) +- `MapFlags`, `MmapAdvise`, and `MsFlags` expose some more variants and only + officially-supported variants are provided for each target. + ([#731](https://github.com/nix-rust/nix/pull/731)) +- Marked `pty::ptsname` function as `unsafe` + ([#744](https://github.com/nix-rust/nix/pull/744)) +- Moved constants ptrace request, event and options to enums and updated ptrace functions and argument types accordingly. + ([#749](https://github.com/nix-rust/nix/pull/749)) +- `AioCb::Drop` will now panic if the `AioCb` is still in-progress ([#715](https://github.com/nix-rust/nix/pull/715)) +- Restricted `nix::sys::socket::UnixAddr::new_abstract` to Linux and Android only. + ([#785](https://github.com/nix-rust/nix/pull/785)) +- The `ucred` struct has been removed in favor of a `UserCredentials` struct that + contains only getters for its fields. + ([#814](https://github.com/nix-rust/nix/pull/814)) +- Both `ip_mreq` and `ipv6_mreq` have been replaced with `IpMembershipRequest` and + `Ipv6MembershipRequest`. + ([#814](https://github.com/nix-rust/nix/pull/814)) +- Removed return type from `pause`. + ([#829](https://github.com/nix-rust/nix/pull/829)) +- Changed the termios APIs to allow for using a `u32` instead of the `BaudRate` + enum on BSD platforms to support arbitrary baud rates. See the module docs for + `nix::sys::termios` for more details. + ([#843](https://github.com/nix-rust/nix/pull/843)) + +### Fixed +- Fix compilation and tests for OpenBSD targets + ([#688](https://github.com/nix-rust/nix/pull/688)) +- Fixed error handling in `AioCb::fsync`, `AioCb::read`, and `AioCb::write`. + It is no longer an error to drop an `AioCb` that failed to enqueue in the OS. + ([#715](https://github.com/nix-rust/nix/pull/715)) +- Fix potential memory corruption on non-Linux platforms when using + `sendmsg`/`recvmsg`, caused by mismatched `msghdr` definition. + ([#648](https://github.com/nix-rust/nix/pull/648)) + +### Removed +- `AioCb::from_boxed_slice` has been removed. It was never actually safe. Use + `from_bytes` or `from_bytes_mut` instead. + ([#820](https://github.com/nix-rust/nix/pull/820)) +- The syscall module has been removed. This only exposed enough functionality for + `memfd_create()` and `pivot_root()`, which are still exposed as separate functions. + ([#747](https://github.com/nix-rust/nix/pull/747)) +- The `Errno` variants are no longer reexported from the `errno` module. `Errno` itself is no longer reexported from the + crate root and instead must be accessed using the `errno` module. ([#696](https://github.com/nix-rust/nix/pull/696)) +- Removed `MS_VERBOSE`, `MS_NOSEC`, and `MS_BORN` from `MsFlags`. These + are internal kernel flags and should never have been exposed. + ([#814](https://github.com/nix-rust/nix/pull/814)) + + +## [0.9.0] 2017-07-23 + +### Added +- Added `sysconf`, `pathconf`, and `fpathconf` + ([#630](https://github.com/nix-rust/nix/pull/630) +- Added `sys::signal::SigAction::{ flags, mask, handler}` + ([#611](https://github.com/nix-rust/nix/pull/609) +- Added `nix::sys::pthread::pthread_self` + ([#591](https://github.com/nix-rust/nix/pull/591) +- Added `AioCb::from_boxed_slice` + ([#582](https://github.com/nix-rust/nix/pull/582) +- Added `nix::unistd::{openat, fstatat, readlink, readlinkat}` + ([#551](https://github.com/nix-rust/nix/pull/551)) +- Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` + ([#556](https://github.com/nix-rust/nix/pull/556) +- Added `nix::ptr::openpty` + ([#456](https://github.com/nix-rust/nix/pull/456)) +- Added `nix::ptrace::{ptrace_get_data, ptrace_getsiginfo, ptrace_setsiginfo + and nix::Error::UnsupportedOperation}` + ([#614](https://github.com/nix-rust/nix/pull/614)) +- Added `cfmakeraw`, `cfsetspeed`, and `tcgetsid`. ([#527](https://github.com/nix-rust/nix/pull/527)) +- Added "bad none", "bad write_ptr", "bad write_int", and "bad readwrite" variants to the `ioctl!` + macro. ([#670](https://github.com/nix-rust/nix/pull/670)) +- On Linux and Android, added support for receiving `PTRACE_O_TRACESYSGOOD` + events from `wait` and `waitpid` using `WaitStatus::PtraceSyscall` + ([#566](https://github.com/nix-rust/nix/pull/566)). + +### Changed +- The `ioctl!` macro and its variants now allow the generated functions to have + doccomments. ([#661](https://github.com/nix-rust/nix/pull/661)) +- Changed `ioctl!(write ...)` into `ioctl!(write_ptr ...)` and `ioctl!(write_int ..)` variants + to more clearly separate those use cases. ([#670](https://github.com/nix-rust/nix/pull/670)) +- Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe. + ([#559](https://github.com/nix-rust/nix/pull/559)) +- Minimum supported Rust version is now 1.13. +- Removed `revents` argument from `PollFd::new()` as it's an output argument and + will be overwritten regardless of value. + ([#542](https://github.com/nix-rust/nix/pull/542)) +- Changed type signature of `sys::select::FdSet::contains` to make `self` + immutable ([#564](https://github.com/nix-rust/nix/pull/564)) +- Introduced wrapper types for `gid_t`, `pid_t`, and `uid_t` as `Gid`, `Pid`, and `Uid` + respectively. Various functions have been changed to use these new types as + arguments. ([#629](https://github.com/nix-rust/nix/pull/629)) +- Fixed compilation on all Android and iOS targets ([#527](https://github.com/nix-rust/nix/pull/527)) + and promoted them to Tier 2 support. +- `nix::sys::statfs::{statfs,fstatfs}` uses statfs definition from `libc::statfs` instead of own linux specific type `nix::sys::Statfs`. + Also file system type constants like `nix::sys::statfs::ADFS_SUPER_MAGIC` were removed in favor of the libc equivalent. + ([#561](https://github.com/nix-rust/nix/pull/561)) +- Revised the termios API including additional tests and documentation and exposed it on iOS. ([#527](https://github.com/nix-rust/nix/pull/527)) +- `eventfd`, `signalfd`, and `pwritev`/`preadv` functionality is now included by default for all + supported platforms. ([#681](https://github.com/nix-rust/nix/pull/561)) +- The `ioctl!` macro's plain variants has been replaced with "bad read" to be consistent with + other variants. The generated functions also have more strict types for their arguments. The + "*_buf" variants also now calculate total array size and take slice references for improved type + safety. The documentation has also been dramatically improved. + ([#670](https://github.com/nix-rust/nix/pull/670)) + +### Removed +- Removed `io::Error` from `nix::Error` and the conversion from `nix::Error` to `Errno` + ([#614](https://github.com/nix-rust/nix/pull/614)) +- All feature flags have been removed in favor of conditional compilation on supported platforms. + `execvpe` is no longer supported, but this was already broken and will be added back in the next + release. ([#681](https://github.com/nix-rust/nix/pull/561)) +- Removed `ioc_*` functions and many helper constants and macros within the `ioctl` module. These + should always have been private and only the `ioctl!` should be used in public code. + ([#670](https://github.com/nix-rust/nix/pull/670)) + +### Fixed +- Fixed multiple issues compiling under different archetectures and OSes. + Now compiles on Linux/MIPS ([#538](https://github.com/nix-rust/nix/pull/538)), + `Linux/PPC` ([#553](https://github.com/nix-rust/nix/pull/553)), + `MacOS/x86_64,i686` ([#553](https://github.com/nix-rust/nix/pull/553)), + `NetBSD/x64_64` ([#538](https://github.com/nix-rust/nix/pull/538)), + `FreeBSD/x86_64,i686` ([#536](https://github.com/nix-rust/nix/pull/536)), and + `Android` ([#631](https://github.com/nix-rust/nix/pull/631)). +- `bind` and `errno_location` now work correctly on `Android` + ([#631](https://github.com/nix-rust/nix/pull/631)) +- Added `nix::ptrace` on all Linux-kernel-based platforms + [#624](https://github.com/nix-rust/nix/pull/624). Previously it was + only available on x86, x86-64, and ARM, and also not on Android. +- Fixed `sys::socket::sendmsg` with zero entry `cmsgs` parameter. + ([#623](https://github.com/nix-rust/nix/pull/623)) +- Multiple constants related to the termios API have now been properly defined for + all supported platforms. ([#527](https://github.com/nix-rust/nix/pull/527)) +- `ioctl!` macro now supports working with non-int datatypes and properly supports all platforms. + ([#670](https://github.com/nix-rust/nix/pull/670)) + +## [0.8.1] 2017-04-16 + +### Fixed +- Fixed build on FreeBSD. (Cherry-picked + [a859ee3c](https://github.com/nix-rust/nix/commit/a859ee3c9396dfdb118fcc2c8ecc697e2d303467)) + +## [0.8.0] 2017-03-02 + +### Added +- Added `::nix::sys::termios::BaudRate` enum to provide portable baudrate + values. ([#518](https://github.com/nix-rust/nix/pull/518)) +- Added a new `WaitStatus::PtraceEvent` to support ptrace events on Linux + and Android ([#438](https://github.com/nix-rust/nix/pull/438)) +- Added support for POSIX AIO + ([#483](https://github.com/nix-rust/nix/pull/483)) + ([#506](https://github.com/nix-rust/nix/pull/506)) +- Added support for XNU system control sockets + ([#478](https://github.com/nix-rust/nix/pull/478)) +- Added support for `ioctl` calls on BSD platforms + ([#478](https://github.com/nix-rust/nix/pull/478)) +- Added struct `TimeSpec` + ([#475](https://github.com/nix-rust/nix/pull/475)) + ([#483](https://github.com/nix-rust/nix/pull/483)) +- Added complete definitions for all kqueue-related constants on all supported + OSes + ([#415](https://github.com/nix-rust/nix/pull/415)) +- Added function `epoll_create1` and bitflags `EpollCreateFlags` in + `::nix::sys::epoll` in order to support `::libc::epoll_create1`. + ([#410](https://github.com/nix-rust/nix/pull/410)) +- Added `setresuid` and `setresgid` for Linux in `::nix::unistd` + ([#448](https://github.com/nix-rust/nix/pull/448)) +- Added `getpgid` in `::nix::unistd` + ([#433](https://github.com/nix-rust/nix/pull/433)) +- Added `tcgetpgrp` and `tcsetpgrp` in `::nix::unistd` + ([#451](https://github.com/nix-rust/nix/pull/451)) +- Added `CLONE_NEWCGROUP` in `::nix::sched` + ([#457](https://github.com/nix-rust/nix/pull/457)) +- Added `getpgrp` in `::nix::unistd` + ([#491](https://github.com/nix-rust/nix/pull/491)) +- Added `fchdir` in `::nix::unistd` + ([#497](https://github.com/nix-rust/nix/pull/497)) +- Added `major` and `minor` in `::nix::sys::stat` for decomposing `dev_t` + ([#508](https://github.com/nix-rust/nix/pull/508)) +- Fixed the style of many bitflags and use `libc` in more places. + ([#503](https://github.com/nix-rust/nix/pull/503)) +- Added `ppoll` in `::nix::poll` + ([#520](https://github.com/nix-rust/nix/pull/520)) +- Added support for getting and setting pipe size with fcntl(2) on Linux + ([#540](https://github.com/nix-rust/nix/pull/540)) + +### Changed +- `::nix::sys::termios::{cfgetispeed, cfsetispeed, cfgetospeed, cfsetospeed}` + switched to use `BaudRate` enum from `speed_t`. + ([#518](https://github.com/nix-rust/nix/pull/518)) +- `epoll_ctl` now could accept None as argument `event` + when op is `EpollOp::EpollCtlDel`. + ([#480](https://github.com/nix-rust/nix/pull/480)) +- Removed the `bad` keyword from the `ioctl!` macro + ([#478](https://github.com/nix-rust/nix/pull/478)) +- Changed `TimeVal` into an opaque Newtype + ([#475](https://github.com/nix-rust/nix/pull/475)) +- `kill`'s signature, defined in `::nix::sys::signal`, changed, so that the + signal parameter has type `T: Into>`. `None` as an argument + for that parameter will result in a 0 passed to libc's `kill`, while a + `Some`-argument will result in the previous behavior for the contained + `Signal`. + ([#445](https://github.com/nix-rust/nix/pull/445)) +- The minimum supported version of rustc is now 1.7.0. + ([#444](https://github.com/nix-rust/nix/pull/444)) +- Changed `KEvent` to an opaque structure that may only be modified by its + constructor and the `ev_set` method. + ([#415](https://github.com/nix-rust/nix/pull/415)) + ([#442](https://github.com/nix-rust/nix/pull/442)) + ([#463](https://github.com/nix-rust/nix/pull/463)) +- `pipe2` now calls `libc::pipe2` where available. Previously it was emulated + using `pipe`, which meant that setting `O_CLOEXEC` was not atomic. + ([#427](https://github.com/nix-rust/nix/pull/427)) +- Renamed `EpollEventKind` to `EpollFlags` in `::nix::sys::epoll` in order for + it to conform with our conventions. + ([#410](https://github.com/nix-rust/nix/pull/410)) +- `EpollEvent` in `::nix::sys::epoll` is now an opaque proxy for + `::libc::epoll_event`. The formerly public field `events` is now be read-only + accessible with the new method `events()` of `EpollEvent`. Instances of + `EpollEvent` can be constructed using the new method `new()` of EpollEvent. + ([#410](https://github.com/nix-rust/nix/pull/410)) +- `SigFlags` in `::nix::sys::signal` has be renamed to `SigmaskHow` and its type + has changed from `bitflags` to `enum` in order to conform to our conventions. + ([#460](https://github.com/nix-rust/nix/pull/460)) +- `sethostname` now takes a `&str` instead of a `&[u8]` as this provides an API + that makes more sense in normal, correct usage of the API. +- `gethostname` previously did not expose the actual length of the hostname + written from the underlying system call at all. This has been updated to + return a `&CStr` within the provided buffer that is always properly + NUL-terminated (this is not guaranteed by the call with all platforms/libc + implementations). +- Exposed all fcntl(2) operations at the module level, so they can be + imported direclty instead of via `FcntlArg` enum. + ([#541](https://github.com/nix-rust/nix/pull/541)) + +### Fixed +- Fixed multiple issues with Unix domain sockets on non-Linux OSes + ([#474](https://github.com/nix-rust/nix/pull/415)) +- Fixed using kqueue with `EVFILT_USER` on FreeBSD + ([#415](https://github.com/nix-rust/nix/pull/415)) +- Fixed the build on FreeBSD, and fixed the getsockopt, sendmsg, and recvmsg + functions on that same OS. + ([#397](https://github.com/nix-rust/nix/pull/397)) +- Fixed an off-by-one bug in `UnixAddr::new_abstract` in `::nix::sys::socket`. + ([#429](https://github.com/nix-rust/nix/pull/429)) +- Fixed clone passing a potentially unaligned stack. + ([#490](https://github.com/nix-rust/nix/pull/490)) +- Fixed mkdev not creating a `dev_t` the same way as libc. + ([#508](https://github.com/nix-rust/nix/pull/508)) + +## [0.7.0] 2016-09-09 + +### Added +- Added `lseek` and `lseek64` in `::nix::unistd` + ([#377](https://github.com/nix-rust/nix/pull/377)) +- Added `mkdir` and `getcwd` in `::nix::unistd` + ([#416](https://github.com/nix-rust/nix/pull/416)) +- Added accessors `sigmask_mut` and `sigmask` to `UContext` in + `::nix::ucontext`. + ([#370](https://github.com/nix-rust/nix/pull/370)) +- Added `WUNTRACED` to `WaitPidFlag` in `::nix::sys::wait` for non-_linux_ + targets. + ([#379](https://github.com/nix-rust/nix/pull/379)) +- Added new module `::nix::sys::reboot` with enumeration `RebootMode` and + functions `reboot` and `set_cad_enabled`. Currently for _linux_ only. + ([#386](https://github.com/nix-rust/nix/pull/386)) +- `FdSet` in `::nix::sys::select` now also implements `Clone`. + ([#405](https://github.com/nix-rust/nix/pull/405)) +- Added `F_FULLFSYNC` to `FcntlArg` in `::nix::fcntl` for _apple_ targets. + ([#407](https://github.com/nix-rust/nix/pull/407)) +- Added `CpuSet::unset` in `::nix::sched`. + ([#402](https://github.com/nix-rust/nix/pull/402)) +- Added constructor method `new()` to `PollFd` in `::nix::poll`, in order to + allow creation of objects, after removing public access to members. + ([#399](https://github.com/nix-rust/nix/pull/399)) +- Added method `revents()` to `PollFd` in `::nix::poll`, in order to provide + read access to formerly public member `revents`. + ([#399](https://github.com/nix-rust/nix/pull/399)) +- Added `MSG_CMSG_CLOEXEC` to `MsgFlags` in `::nix::sys::socket` for _linux_ only. + ([#422](https://github.com/nix-rust/nix/pull/422)) + +### Changed +- Replaced the reexported integer constants for signals by the enumeration + `Signal` in `::nix::sys::signal`. + ([#362](https://github.com/nix-rust/nix/pull/362)) +- Renamed `EventFdFlag` to `EfdFlags` in `::nix::sys::eventfd`. + ([#383](https://github.com/nix-rust/nix/pull/383)) +- Changed the result types of `CpuSet::is_set` and `CpuSet::set` in + `::nix::sched` to `Result` and `Result<()>`, respectively. They now + return `EINVAL`, if an invalid argument for the `field` parameter is passed. + ([#402](https://github.com/nix-rust/nix/pull/402)) +- `MqAttr` in `::nix::mqueue` is now an opaque proxy for `::libc::mq_attr`, + which has the same structure as the old `MqAttr`. The field `mq_flags` of + `::libc::mq_attr` is readable using the new method `flags()` of `MqAttr`. + `MqAttr` also no longer implements `Debug`. + ([#392](https://github.com/nix-rust/nix/pull/392)) +- The parameter `msq_prio` of `mq_receive` with type `u32` in `::nix::mqueue` + was replaced by a parameter named `msg_prio` with type `&mut u32`, so that + the message priority can be obtained by the caller. + ([#392](https://github.com/nix-rust/nix/pull/392)) +- The type alias `MQd` in `::nix::queue` was replaced by the type alias + `libc::mqd_t`, both of which are aliases for the same type. + ([#392](https://github.com/nix-rust/nix/pull/392)) + +### Removed +- Type alias `SigNum` from `::nix::sys::signal`. + ([#362](https://github.com/nix-rust/nix/pull/362)) +- Type alias `CpuMask` from `::nix::shed`. + ([#402](https://github.com/nix-rust/nix/pull/402)) +- Removed public fields from `PollFd` in `::nix::poll`. (See also added method + `revents()`. + ([#399](https://github.com/nix-rust/nix/pull/399)) + +### Fixed +- Fixed the build problem for NetBSD (Note, that we currently do not support + it, so it might already be broken again). + ([#389](https://github.com/nix-rust/nix/pull/389)) +- Fixed the build on FreeBSD, and fixed the getsockopt, sendmsg, and recvmsg + functions on that same OS. + ([#397](https://github.com/nix-rust/nix/pull/397)) + +## [0.6.0] 2016-06-10 + +### Added +- Added `gettid` in `::nix::unistd` for _linux_ and _android_. + ([#293](https://github.com/nix-rust/nix/pull/293)) +- Some _mips_ support in `::nix::sched` and `::nix::sys::syscall`. + ([#301](https://github.com/nix-rust/nix/pull/301)) +- Added `SIGNALFD_SIGINFO_SIZE` in `::nix::sys::signalfd`. + ([#309](https://github.com/nix-rust/nix/pull/309)) +- Added new module `::nix::ucontext` with struct `UContext`. Currently for + _linux_ only. + ([#311](https://github.com/nix-rust/nix/pull/311)) +- Added `EPOLLEXCLUSIVE` to `EpollEventKind` in `::nix::sys::epoll`. + ([#330](https://github.com/nix-rust/nix/pull/330)) +- Added `pause` to `::nix::unistd`. + ([#336](https://github.com/nix-rust/nix/pull/336)) +- Added `sleep` to `::nix::unistd`. + ([#351](https://github.com/nix-rust/nix/pull/351)) +- Added `S_IFDIR`, `S_IFLNK`, `S_IFMT` to `SFlag` in `::nix::sys::stat`. + ([#359](https://github.com/nix-rust/nix/pull/359)) +- Added `clear` and `extend` functions to `SigSet`'s implementation in + `::nix::sys::signal`. + ([#347](https://github.com/nix-rust/nix/pull/347)) +- `sockaddr_storage_to_addr` in `::nix::sys::socket` now supports `sockaddr_nl` + on _linux_ and _android_. + ([#366](https://github.com/nix-rust/nix/pull/366)) +- Added support for `SO_ORIGINAL_DST` in `::nix::sys::socket` on _linux_. + ([#367](https://github.com/nix-rust/nix/pull/367)) +- Added `SIGINFO` in `::nix::sys::signal` for the _macos_ target as well as + `SIGPWR` and `SIGSTKFLT` in `::nix::sys::signal` for non-_macos_ targets. + ([#361](https://github.com/nix-rust/nix/pull/361)) + +### Changed +- Changed the structure `IoVec` in `::nix::sys::uio`. + ([#304](https://github.com/nix-rust/nix/pull/304)) +- Replaced `CREATE_NEW_FD` by `SIGNALFD_NEW` in `::nix::sys::signalfd`. + ([#309](https://github.com/nix-rust/nix/pull/309)) +- Renamed `SaFlag` to `SaFlags` and `SigFlag` to `SigFlags` in + `::nix::sys::signal`. + ([#314](https://github.com/nix-rust/nix/pull/314)) +- Renamed `Fork` to `ForkResult` and changed its fields in `::nix::unistd`. + ([#332](https://github.com/nix-rust/nix/pull/332)) +- Added the `signal` parameter to `clone`'s signature in `::nix::sched`. + ([#344](https://github.com/nix-rust/nix/pull/344)) +- `execv`, `execve`, and `execvp` now return `Result` instead of + `Result<()>` in `::nix::unistd`. + ([#357](https://github.com/nix-rust/nix/pull/357)) + +### Fixed +- Improved the conversion from `std::net::SocketAddr` to `InetAddr` in + `::nix::sys::socket::addr`. + ([#335](https://github.com/nix-rust/nix/pull/335)) + +## [0.5.0] 2016-03-01 diff --git a/vendor/nix-v0.23.1-patched/CONTRIBUTING.md b/vendor/nix-v0.23.1-patched/CONTRIBUTING.md new file mode 100644 index 000000000..00221157b --- /dev/null +++ b/vendor/nix-v0.23.1-patched/CONTRIBUTING.md @@ -0,0 +1,114 @@ +# Contributing to nix + +We're really glad you're interested in contributing to nix! This +document has a few pointers and guidelines to help get you started. + +To have a welcoming and inclusive project, nix uses the Rust project's +[Code of Conduct][conduct]. All contributors are expected to follow it. + +[conduct]: https://www.rust-lang.org/conduct.html + + +# Issues + +We use GitHub's [issue tracker][issues]. + +[issues]: https://github.com/nix-rust/nix/issues + + +## Bug reports + +Before submitting a new bug report, please [search existing +issues][issue-search] to see if there's something related. If not, just +[open a new issue][new-issue]! + +As a reminder, the more information you can give in your issue, the +easier it is to figure out how to fix it. For nix, this will likely +include the OS and version, and the architecture. + +[issue-search]: https://github.com/nix-rust/nix/search?utf8=%E2%9C%93&q=is%3Aissue&type=Issues +[new-issue]: https://github.com/nix-rust/nix/issues/new + + +## Feature / API requests + +If you'd like a new API or feature added, please [open a new +issue][new-issue] requesting it. As with reporting a bug, the more +information you can provide, the better. + + +## Labels + +We use labels to help manage issues. The structure is modeled after +[Rust's issue labeling scheme][rust-labels]: +- **A-** prefixed labels state which area of the project the issue + relates to +- **E-** prefixed labels explain the level of experience necessary to fix the + issue +- **O-** prefixed labels specify the OS for issues that are OS-specific +- **R-** prefixed labels specify the architecture for issues that are + architecture-specific + +[rust-labels]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#issue-triage + + +# Pull requests + +GitHub pull requests are the primary mechanism we use to change nix. GitHub itself has +some [great documentation][pr-docs] on using the Pull Request feature. We use the 'fork and +pull' model described there. + +Please make pull requests against the `master` branch. + +If you change the API by way of adding, removing or changing something or if +you fix a bug, please add an appropriate note to the [change log][cl]. We +follow the conventions of [Keep A CHANGELOG][kacl]. + +[cl]: https://github.com/nix-rust/nix/blob/master/CHANGELOG.md +[kacl]: https://github.com/olivierlacan/keep-a-changelog/tree/18adb5f5be7a898d046f6a4acb93e39dcf40c4ad +[pr-docs]: https://help.github.com/articles/using-pull-requests/ + +## Testing + +nix has a test suite that you can run with `cargo test`. Ideally, we'd like pull +requests to include tests where they make sense. For example, when fixing a bug, +add a test that would have failed without the fix. + +After you've made your change, make sure the tests pass in your development +environment. We also have [continuous integration set up on +Cirrus-CI][cirrus-ci], which might find some issues on other platforms. The CI +will run once you open a pull request. + +There is also infrastructure for running tests for other targets +locally. More information is available in the [CI Readme][ci-readme]. + +[cirrus-ci]: https://cirrus-ci.com/github/nix-rust/nix +[ci-readme]: ci/README.md + +### Disabling a test in the CI environment + +Sometimes there are features that cannot be tested in the CI environment. +To stop a test from running under CI, add `skip_if_cirrus!()` to it. Please +describe the reason it shouldn't run under CI, and a link to an issue if +possible! + +## bors, the bot who merges all the PRs + +All pull requests are merged via [bors], an integration bot. After the +pull request has been reviewed, the reviewer will leave a comment like + +> bors r+ + +to let bors know that it was approved. Then bors will check that it passes +tests when merged with the latest changes in the `master` branch, and +merge if the tests succeed. + +[bors]: https://bors-ng.github.io/ + + +## API conventions + +If you're adding a new API, we have a [document with +conventions][conventions] to use throughout the nix project. + +[conventions]: https://github.com/nix-rust/nix/blob/master/CONVENTIONS.md diff --git a/vendor/nix-v0.23.1-patched/CONVENTIONS.md b/vendor/nix-v0.23.1-patched/CONVENTIONS.md new file mode 100644 index 000000000..2461085eb --- /dev/null +++ b/vendor/nix-v0.23.1-patched/CONVENTIONS.md @@ -0,0 +1,86 @@ +# Conventions + +In order to achieve our goal of wrapping [libc][libc] code in idiomatic rust +constructs with minimal performance overhead, we follow the following +conventions. + +Note that, thus far, not all the code follows these conventions and not all +conventions we try to follow have been documented here. If you find an instance +of either, feel free to remedy the flaw by opening a pull request with +appropriate changes or additions. + +## Change Log + +We follow the conventions laid out in [Keep A CHANGELOG][kacl]. + +[kacl]: https://github.com/olivierlacan/keep-a-changelog/tree/18adb5f5be7a898d046f6a4acb93e39dcf40c4ad + +## libc constants, functions and structs + +We do not define integer constants ourselves, but use or reexport them from the +[libc crate][libc]. + +We use the functions exported from [libc][libc] instead of writing our own +`extern` declarations. + +We use the `struct` definitions from [libc][libc] internally instead of writing +our own. If we want to add methods to a libc type, we use the newtype pattern. +For example, + +```rust +pub struct SigSet(libc::sigset_t); + +impl SigSet { + ... +} +``` + +When creating newtypes, we use Rust's `CamelCase` type naming convention. + +## Bitflags + +Many C functions have flags parameters that are combined from constants using +bitwise operations. We represent the types of these parameters by types defined +using our `libc_bitflags!` macro, which is a convenience wrapper around the +`bitflags!` macro from the [bitflags crate][bitflags] that brings in the +constant value from `libc`. + +We name the type for a set of constants whose element's names start with `FOO_` +`FooFlags`. + +For example, + +```rust +libc_bitflags!{ + pub struct ProtFlags: libc::c_int { + PROT_NONE; + PROT_READ; + PROT_WRITE; + PROT_EXEC; + #[cfg(any(target_os = "linux", target_os = "android"))] + PROT_GROWSDOWN; + #[cfg(any(target_os = "linux", target_os = "android"))] + PROT_GROWSUP; + } +} +``` + + +## Enumerations + +We represent sets of constants that are intended as mutually exclusive arguments +to parameters of functions by [enumerations][enum]. + + +## Structures Initialized by libc Functions + +Whenever we need to use a [libc][libc] function to properly initialize a +variable and said function allows us to use uninitialized memory, we use +[`std::mem::MaybeUninit`][std_MaybeUninit] when defining the variable. This +allows us to avoid the overhead incurred by zeroing or otherwise initializing +the variable. + +[bitflags]: https://crates.io/crates/bitflags/ +[enum]: https://doc.rust-lang.org/reference.html#enumerations +[libc]: https://crates.io/crates/libc/ +[std_MaybeUninit]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html diff --git a/vendor/nix-v0.23.1-patched/Cargo.toml b/vendor/nix-v0.23.1-patched/Cargo.toml new file mode 100644 index 000000000..6309c12cc --- /dev/null +++ b/vendor/nix-v0.23.1-patched/Cargo.toml @@ -0,0 +1,74 @@ +[package] +name = "nix" +description = "Rust friendly bindings to *nix APIs" +edition = "2018" +version = "0.23.1" +authors = ["The nix-rust Project Developers"] +repository = "https://github.com/nix-rust/nix" +license = "MIT" +categories = ["os::unix-apis"] +include = ["src/**/*", "LICENSE", "README.md", "CHANGELOG.md"] + +[package.metadata.docs.rs] +targets = [ + "x86_64-unknown-linux-gnu", + "aarch64-linux-android", + "x86_64-apple-darwin", + "aarch64-apple-ios", + "x86_64-unknown-freebsd", + "x86_64-unknown-openbsd", + "x86_64-unknown-netbsd", + "x86_64-unknown-dragonfly", + "x86_64-fuchsia", + "x86_64-unknown-redox", + "x86_64-unknown-illumos" +] + +[dependencies] +libc = { version = "0.2.102", features = [ "extra_traits" ] } +bitflags = "1.3.1" +cfg-if = "1.0" + +[target.'cfg(not(target_os = "redox"))'.dependencies] +memoffset = "0.6.3" + +[target.'cfg(target_os = "dragonfly")'.build-dependencies] +cc = "1" + +[dev-dependencies] +assert-impl = "0.1" +lazy_static = "1.2" +rand = "0.8" +tempfile = "3.2.0" +semver = "1.0.0" + +[target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies] +caps = "0.5.1" + +[target.'cfg(target_os = "freebsd")'.dev-dependencies] +sysctl = "0.1" + +[[test]] +name = "test" +path = "test/test.rs" + +[[test]] +name = "test-aio-drop" +path = "test/sys/test_aio_drop.rs" + +[[test]] +name = "test-clearenv" +path = "test/test_clearenv.rs" + +[[test]] +name = "test-lio-listio-resubmit" +path = "test/sys/test_lio_listio_resubmit.rs" + +[[test]] +name = "test-mount" +path = "test/test_mount.rs" +harness = false + +[[test]] +name = "test-ptymaster-drop" +path = "test/test_ptymaster_drop.rs" diff --git a/vendor/nix-v0.23.1-patched/Cross.toml b/vendor/nix-v0.23.1-patched/Cross.toml new file mode 100644 index 000000000..acd94f308 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/Cross.toml @@ -0,0 +1,5 @@ +[build.env] +passthrough = [ + "RUSTFLAGS", + "RUST_TEST_THREADS" +] diff --git a/vendor/nix-v0.23.1-patched/LICENSE b/vendor/nix-v0.23.1-patched/LICENSE new file mode 100644 index 000000000..aff9096fd --- /dev/null +++ b/vendor/nix-v0.23.1-patched/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Carl Lerche + nix-rust Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/nix-v0.23.1-patched/README.md b/vendor/nix-v0.23.1-patched/README.md new file mode 100644 index 000000000..a8759f1ce --- /dev/null +++ b/vendor/nix-v0.23.1-patched/README.md @@ -0,0 +1,102 @@ +# Rust bindings to *nix APIs + +[![Cirrus Build Status](https://api.cirrus-ci.com/github/nix-rust/nix.svg)](https://cirrus-ci.com/github/nix-rust/nix) +[![crates.io](https://img.shields.io/crates/v/nix.svg)](https://crates.io/crates/nix) + +[Documentation (Releases)](https://docs.rs/nix/) + +Nix seeks to provide friendly bindings to various *nix platform APIs (Linux, Darwin, +...). The goal is to not provide a 100% unified interface, but to unify +what can be while still providing platform specific APIs. + +For many system APIs, Nix provides a safe alternative to the unsafe APIs +exposed by the [libc crate](https://github.com/rust-lang/libc). This is done by +wrapping the libc functionality with types/abstractions that enforce legal/safe +usage. + + +As an example of what Nix provides, examine the differences between what is +exposed by libc and nix for the +[gethostname](https://man7.org/linux/man-pages/man2/gethostname.2.html) system +call: + +```rust,ignore +// libc api (unsafe, requires handling return code/errno) +pub unsafe extern fn gethostname(name: *mut c_char, len: size_t) -> c_int; + +// nix api (returns a nix::Result) +pub fn gethostname<'a>(buffer: &'a mut [u8]) -> Result<&'a CStr>; +``` + +## Supported Platforms + +nix target support consists of two tiers. While nix attempts to support all +platforms supported by [libc](https://github.com/rust-lang/libc), only some +platforms are actively supported due to either technical or manpower +limitations. Support for platforms is split into three tiers: + + * Tier 1 - Builds and tests for this target are run in CI. Failures of either + block the inclusion of new code. + * Tier 2 - Builds for this target are run in CI. Failures during the build + blocks the inclusion of new code. Tests may be run, but failures + in tests don't block the inclusion of new code. + * Tier 3 - Builds for this target are run in CI. Failures during the build + *do not* block the inclusion of new code. Testing may be run, but + failures in tests don't block the inclusion of new code. + +The following targets are supported by `nix`: + +Tier 1: + * aarch64-unknown-linux-gnu + * arm-unknown-linux-gnueabi + * armv7-unknown-linux-gnueabihf + * i686-unknown-freebsd + * i686-unknown-linux-gnu + * i686-unknown-linux-musl + * mips-unknown-linux-gnu + * mips64-unknown-linux-gnuabi64 + * mips64el-unknown-linux-gnuabi64 + * mipsel-unknown-linux-gnu + * powerpc64le-unknown-linux-gnu + * x86_64-apple-darwin + * x86_64-unknown-freebsd + * x86_64-unknown-linux-gnu + * x86_64-unknown-linux-musl + +Tier 2: + * aarch64-apple-ios + * aarch64-linux-android + * arm-linux-androideabi + * arm-unknown-linux-musleabi + * armv7-linux-androideabi + * i686-linux-android + * powerpc-unknown-linux-gnu + * s390x-unknown-linux-gnu + * x86_64-apple-ios + * x86_64-linux-android + * x86_64-unknown-illumos + * x86_64-unknown-netbsd + +Tier 3: + * x86_64-fuchsia + * x86_64-unknown-dragonfly + * x86_64-unknown-linux-gnux32 + * x86_64-unknown-openbsd + * x86_64-unknown-redox + +## Minimum Supported Rust Version (MSRV) + +nix is supported on Rust 1.46.0 and higher. It's MSRV will not be +changed in the future without bumping the major or minor version. + +## Contributing + +Contributions are very welcome. Please See [CONTRIBUTING](CONTRIBUTING.md) for +additional details. + +Feel free to join us in [the nix-rust/nix](https://gitter.im/nix-rust/nix) channel on Gitter to +discuss `nix` development. + +## License + +Nix is licensed under the MIT license. See [LICENSE](LICENSE) for more details. diff --git a/vendor/nix-v0.23.1-patched/RELEASE_PROCEDURE.md b/vendor/nix-v0.23.1-patched/RELEASE_PROCEDURE.md new file mode 100644 index 000000000..b8cfcd81d --- /dev/null +++ b/vendor/nix-v0.23.1-patched/RELEASE_PROCEDURE.md @@ -0,0 +1,18 @@ +This document lists the steps that lead to a successful release of the Nix +library. + +# Before Release + +Nix uses [cargo release](https://github.com/crate-ci/cargo-release) to automate +the release process. Based on changes since the last release, pick a new +version number following semver conventions. For nix, a change that drops +support for some Rust versions counts as a breaking change, and requires a +major bump. + +The release is prepared as follows: + +- Ask for a new libc version if, necessary. It usually is. Then update the + dependency in Cargo.toml accordingly. +- Confirm that everything's ready for a release by running + `cargo release --dry-run ` +- Create the release with `cargo release ` diff --git a/vendor/nix-v0.23.1-patched/bors.toml b/vendor/nix-v0.23.1-patched/bors.toml new file mode 100644 index 000000000..03810b7e8 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/bors.toml @@ -0,0 +1,49 @@ +status = [ + "Android aarch64", + "Android arm", + "Android armv7", + "Android i686", + "Android x86_64", + "DragonFly BSD x86_64", + "FreeBSD amd64 & i686", + "Fuchsia x86_64", + "Linux MIPS", + "Linux MIPS64 el", + "Linux MIPS64", + "Linux aarch64", + "Linux arm gnueabi", + "Linux arm-musleabi", + "Linux armv7 gnueabihf", + "Linux i686 musl", + "Linux i686", + "Linux mipsel", + "Linux powerpc", + "Linux powerpc64", + "Linux powerpc64le", + "Linux s390x", + "Linux x32", + "Linux x86_64 musl", + "Linux x86_64", + "Minver", + "NetBSD x86_64", + "OpenBSD x86_64", + "OSX x86_64", + "Redox x86_64", + "Rust Stable", + "iOS aarch64", + "iOS x86_64", + "Illumos", +] + +# Set bors's timeout to 1 hour +# +# bors's timeout should always be at least twice as long as the test suite +# takes. This is to allow the CI provider to fast-fail a test; if one of the +# builders immediately reports a failure, then bors will move on to the next +# batch, leaving the slower builders to work through the already-doomed run and +# the next one. +# +# At the time this was written, nix's test suite took about twenty minutes to +# run. The timeout was raised to one hour to give nix room to grow and time +# for delays on Cirrus's end. +timeout_sec = 3600 diff --git a/vendor/nix-v0.23.1-patched/release.toml b/vendor/nix-v0.23.1-patched/release.toml new file mode 100644 index 000000000..df2c9da45 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/release.toml @@ -0,0 +1,5 @@ +no-dev-version = true +pre-release-replacements = [ + { file="CHANGELOG.md", search="Unreleased", replace="{{version}}" }, + { file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}" } +] diff --git a/vendor/nix-v0.23.1-patched/src/dir.rs b/vendor/nix-v0.23.1-patched/src/dir.rs new file mode 100644 index 000000000..ed70a458a --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/dir.rs @@ -0,0 +1,246 @@ +use crate::{Error, NixPath, Result}; +use crate::errno::Errno; +use crate::fcntl::{self, OFlag}; +use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; +use std::ptr; +use std::ffi; +use crate::sys; + +#[cfg(target_os = "linux")] +use libc::{dirent64 as dirent, readdir64_r as readdir_r}; + +#[cfg(not(target_os = "linux"))] +use libc::{dirent, readdir_r}; + +/// An open directory. +/// +/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences: +/// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing +/// if the path represents a file or directory). +/// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc. +/// The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd` +/// after the `Dir` is dropped. +/// * can be iterated through multiple times without closing and reopening the file +/// descriptor. Each iteration rewinds when finished. +/// * returns entries for `.` (current directory) and `..` (parent directory). +/// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc +/// does). +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct Dir( + ptr::NonNull +); + +impl Dir { + /// Opens the given path as with `fcntl::open`. + pub fn open(path: &P, oflag: OFlag, + mode: sys::stat::Mode) -> Result { + let fd = fcntl::open(path, oflag, mode)?; + Dir::from_fd(fd) + } + + /// Opens the given path as with `fcntl::openat`. + pub fn openat(dirfd: RawFd, path: &P, oflag: OFlag, + mode: sys::stat::Mode) -> Result { + let fd = fcntl::openat(dirfd, path, oflag, mode)?; + Dir::from_fd(fd) + } + + /// Converts from a descriptor-based object, closing the descriptor on success or failure. + #[inline] + pub fn from(fd: F) -> Result { + Dir::from_fd(fd.into_raw_fd()) + } + + /// Converts from a file descriptor, closing it on success or failure. + pub fn from_fd(fd: RawFd) -> Result { + let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(|| { + let e = Error::last(); + unsafe { libc::close(fd) }; + e + })?; + Ok(Dir(d)) + } + + /// Returns an iterator of `Result` which rewinds when finished. + pub fn iter(&mut self) -> Iter { + Iter(self) + } +} + +// `Dir` is not `Sync`. With the current implementation, it could be, but according to +// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html, +// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to +// call `readdir` simultaneously from multiple threads. +// +// `Dir` is safe to pass from one thread to another, as it's not reference-counted. +unsafe impl Send for Dir {} + +impl AsRawFd for Dir { + fn as_raw_fd(&self) -> RawFd { + unsafe { libc::dirfd(self.0.as_ptr()) } + } +} + +impl Drop for Dir { + fn drop(&mut self) { + let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) }); + if !std::thread::panicking() && e == Err(Errno::EBADF) { + panic!("Closing an invalid file descriptor!"); + }; + } +} + +fn next(dir: &mut Dir) -> Option> { + unsafe { + // Note: POSIX specifies that portable applications should dynamically allocate a + // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1 + // for the NUL byte. It doesn't look like the std library does this; it just uses + // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate). + // Probably fine here too then. + let mut ent = std::mem::MaybeUninit::::uninit(); + let mut result = ptr::null_mut(); + if let Err(e) = Errno::result( + readdir_r(dir.0.as_ptr(), ent.as_mut_ptr(), &mut result)) + { + return Some(Err(e)); + } + if result.is_null() { + return None; + } + assert_eq!(result, ent.as_mut_ptr()); + Some(Ok(Entry(ent.assume_init()))) + } +} + +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct Iter<'d>(&'d mut Dir); + +impl<'d> Iterator for Iter<'d> { + type Item = Result; + + fn next(&mut self) -> Option { + next(self.0) + } +} + +impl<'d> Drop for Iter<'d> { + fn drop(&mut self) { + unsafe { libc::rewinddir((self.0).0.as_ptr()) } + } +} + +/// The return type of [Dir::into_iter] +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct OwningIter(Dir); + +impl Iterator for OwningIter { + type Item = Result; + + fn next(&mut self) -> Option { + next(&mut self.0) + } +} + +impl IntoIterator for Dir { + type Item = Result; + type IntoIter = OwningIter; + + /// Creates a owning iterator, that is, one that takes ownership of the + /// `Dir`. The `Dir` cannot be used after calling this. This can be useful + /// when you have a function that both creates a `Dir` instance and returns + /// an `Iterator`. + /// + /// Example: + /// + /// ``` + /// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode}; + /// use std::{iter::Iterator, string::String}; + /// + /// fn ls_upper(dirname: &str) -> impl Iterator { + /// let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap(); + /// d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase()) + /// } + /// ``` + fn into_iter(self) -> Self::IntoIter { + OwningIter(self) + } +} + +/// A directory entry, similar to `std::fs::DirEntry`. +/// +/// Note that unlike the std version, this may represent the `.` or `..` entries. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[repr(transparent)] +pub struct Entry(dirent); + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum Type { + Fifo, + CharacterDevice, + Directory, + BlockDevice, + File, + Symlink, + Socket, +} + +impl Entry { + /// Returns the inode number (`d_ino`) of the underlying `dirent`. + #[cfg(any(target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "ios", + target_os = "l4re", + target_os = "linux", + target_os = "macos", + target_os = "solaris"))] + pub fn ino(&self) -> u64 { + self.0.d_ino as u64 + } + + /// Returns the inode number (`d_fileno`) of the underlying `dirent`. + #[cfg(not(any(target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "ios", + target_os = "l4re", + target_os = "linux", + target_os = "macos", + target_os = "solaris")))] + #[allow(clippy::useless_conversion)] // Not useless on all OSes + pub fn ino(&self) -> u64 { + u64::from(self.0.d_fileno) + } + + /// Returns the bare file name of this directory entry without any other leading path component. + pub fn file_name(&self) -> &ffi::CStr { + unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) } + } + + /// Returns the type of this directory entry, if known. + /// + /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known; + /// notably, some Linux filesystems don't implement this. The caller should use `stat` or + /// `fstat` if this returns `None`. + pub fn file_type(&self) -> Option { + #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] + match self.0.d_type { + libc::DT_FIFO => Some(Type::Fifo), + libc::DT_CHR => Some(Type::CharacterDevice), + libc::DT_DIR => Some(Type::Directory), + libc::DT_BLK => Some(Type::BlockDevice), + libc::DT_REG => Some(Type::File), + libc::DT_LNK => Some(Type::Symlink), + libc::DT_SOCK => Some(Type::Socket), + /* libc::DT_UNKNOWN | */ _ => None, + } + + // illumos and Solaris systems do not have the d_type member at all: + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + None + } +} diff --git a/vendor/nix-v0.23.1-patched/src/env.rs b/vendor/nix-v0.23.1-patched/src/env.rs new file mode 100644 index 000000000..54d759596 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/env.rs @@ -0,0 +1,66 @@ +//! Environment variables +use cfg_if::cfg_if; +use std::fmt; + +/// Indicates that [`clearenv`] failed for some unknown reason +#[derive(Clone, Copy, Debug)] +pub struct ClearEnvError; + +impl fmt::Display for ClearEnvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "clearenv failed") + } +} + +impl std::error::Error for ClearEnvError {} + +/// Clear the environment of all name-value pairs. +/// +/// On platforms where libc provides `clearenv()`, it will be used. libc's +/// `clearenv()` is documented to return an error code but not set errno; if the +/// return value indicates a failure, this function will return +/// [`ClearEnvError`]. +/// +/// On platforms where libc does not provide `clearenv()`, a fallback +/// implementation will be used that iterates over all environment variables and +/// removes them one-by-one. +/// +/// # Safety +/// +/// This function is not threadsafe and can cause undefined behavior in +/// combination with `std::env` or other program components that access the +/// environment. See, for example, the discussion on `std::env::remove_var`; this +/// function is a case of an "inherently unsafe non-threadsafe API" dealing with +/// the environment. +/// +/// The caller must ensure no other threads access the process environment while +/// this function executes and that no raw pointers to an element of libc's +/// `environ` is currently held. The latter is not an issue if the only other +/// environment access in the program is via `std::env`, but the requirement on +/// thread safety must still be upheld. +pub unsafe fn clearenv() -> std::result::Result<(), ClearEnvError> { + let ret; + cfg_if! { + if #[cfg(any(target_os = "fuchsia", + target_os = "wasi", + target_env = "wasi", + target_env = "uclibc", + target_os = "linux", + target_os = "android", + target_os = "emscripten"))] { + ret = libc::clearenv(); + } else { + use std::env; + for (name, _) in env::vars_os() { + env::remove_var(name); + } + ret = 0; + } + } + + if ret == 0 { + Ok(()) + } else { + Err(ClearEnvError) + } +} diff --git a/vendor/nix-v0.23.1-patched/src/errno.rs b/vendor/nix-v0.23.1-patched/src/errno.rs new file mode 100644 index 000000000..3da246e82 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/errno.rs @@ -0,0 +1,2723 @@ +use cfg_if::cfg_if; +use libc::{c_int, c_void}; +use std::convert::TryFrom; +use std::{fmt, io, error}; +use crate::{Error, Result}; + +pub use self::consts::*; + +cfg_if! { + if #[cfg(any(target_os = "freebsd", + target_os = "ios", + target_os = "macos"))] { + unsafe fn errno_location() -> *mut c_int { + libc::__error() + } + } else if #[cfg(any(target_os = "android", + target_os = "netbsd", + target_os = "openbsd"))] { + unsafe fn errno_location() -> *mut c_int { + libc::__errno() + } + } else if #[cfg(any(target_os = "linux", + target_os = "redox", + target_os = "dragonfly", + target_os = "fuchsia"))] { + unsafe fn errno_location() -> *mut c_int { + libc::__errno_location() + } + } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { + unsafe fn errno_location() -> *mut c_int { + libc::___errno() + } + } +} + +/// Sets the platform-specific errno to no-error +fn clear() { + // Safe because errno is a thread-local variable + unsafe { + *errno_location() = 0; + } +} + +/// Returns the platform-specific value of errno +pub fn errno() -> i32 { + unsafe { + (*errno_location()) as i32 + } +} + +impl Errno { + /// Convert this `Error` to an [`Errno`](enum.Errno.html). + /// + /// # Example + /// + /// ``` + /// # use nix::Error; + /// # use nix::errno::Errno; + /// let e = Error::from(Errno::EPERM); + /// assert_eq!(Some(Errno::EPERM), e.as_errno()); + /// ``` + #[deprecated( + since = "0.22.0", + note = "It's a no-op now; just delete it." + )] + pub const fn as_errno(self) -> Option { + Some(self) + } + + /// Create a nix Error from a given errno + #[deprecated( + since = "0.22.0", + note = "It's a no-op now; just delete it." + )] + #[allow(clippy::wrong_self_convention)] // False positive + pub fn from_errno(errno: Errno) -> Error { + errno + } + + /// Create a new invalid argument error (`EINVAL`) + #[deprecated( + since = "0.22.0", + note = "Use Errno::EINVAL instead" + )] + pub const fn invalid_argument() -> Error { + Errno::EINVAL + } + + pub fn last() -> Self { + last() + } + + pub fn desc(self) -> &'static str { + desc(self) + } + + pub const fn from_i32(err: i32) -> Errno { + from_i32(err) + } + + pub fn clear() { + clear() + } + + /// Returns `Ok(value)` if it does not contain the sentinel value. This + /// should not be used when `-1` is not the errno sentinel value. + #[inline] + pub fn result>(value: S) -> Result { + if value == S::sentinel() { + Err(Self::last()) + } else { + Ok(value) + } + } + + /// Backwards compatibility hack for Nix <= 0.21.0 users + /// + /// In older versions of Nix, `Error::Sys` was an enum variant. Now it's a + /// function, which is compatible with most of the former use cases of the + /// enum variant. But you should use `Error(Errno::...)` instead. + #[deprecated( + since = "0.22.0", + note = "Use Errno::... instead" + )] + #[allow(non_snake_case)] + #[inline] + pub const fn Sys(errno: Errno) -> Error { + errno + } +} + +/// The sentinel value indicates that a function failed and more detailed +/// information about the error can be found in `errno` +pub trait ErrnoSentinel: Sized { + fn sentinel() -> Self; +} + +impl ErrnoSentinel for isize { + fn sentinel() -> Self { -1 } +} + +impl ErrnoSentinel for i32 { + fn sentinel() -> Self { -1 } +} + +impl ErrnoSentinel for i64 { + fn sentinel() -> Self { -1 } +} + +impl ErrnoSentinel for *mut c_void { + fn sentinel() -> Self { -1isize as *mut c_void } +} + +impl ErrnoSentinel for libc::sighandler_t { + fn sentinel() -> Self { libc::SIG_ERR } +} + +impl error::Error for Errno {} + +impl fmt::Display for Errno { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}: {}", self, self.desc()) + } +} + +impl From for io::Error { + fn from(err: Errno) -> Self { + io::Error::from_raw_os_error(err as i32) + } +} + +impl TryFrom for Errno { + type Error = io::Error; + + fn try_from(ioerror: io::Error) -> std::result::Result { + ioerror.raw_os_error() + .map(Errno::from_i32) + .ok_or(ioerror) + } +} + +fn last() -> Errno { + Errno::from_i32(errno()) +} + +fn desc(errno: Errno) -> &'static str { + use self::Errno::*; + match errno { + UnknownErrno => "Unknown errno", + EPERM => "Operation not permitted", + ENOENT => "No such file or directory", + ESRCH => "No such process", + EINTR => "Interrupted system call", + EIO => "I/O error", + ENXIO => "No such device or address", + E2BIG => "Argument list too long", + ENOEXEC => "Exec format error", + EBADF => "Bad file number", + ECHILD => "No child processes", + EAGAIN => "Try again", + ENOMEM => "Out of memory", + EACCES => "Permission denied", + EFAULT => "Bad address", + ENOTBLK => "Block device required", + EBUSY => "Device or resource busy", + EEXIST => "File exists", + EXDEV => "Cross-device link", + ENODEV => "No such device", + ENOTDIR => "Not a directory", + EISDIR => "Is a directory", + EINVAL => "Invalid argument", + ENFILE => "File table overflow", + EMFILE => "Too many open files", + ENOTTY => "Not a typewriter", + ETXTBSY => "Text file busy", + EFBIG => "File too large", + ENOSPC => "No space left on device", + ESPIPE => "Illegal seek", + EROFS => "Read-only file system", + EMLINK => "Too many links", + EPIPE => "Broken pipe", + EDOM => "Math argument out of domain of func", + ERANGE => "Math result not representable", + EDEADLK => "Resource deadlock would occur", + ENAMETOOLONG => "File name too long", + ENOLCK => "No record locks available", + ENOSYS => "Function not implemented", + ENOTEMPTY => "Directory not empty", + ELOOP => "Too many symbolic links encountered", + ENOMSG => "No message of desired type", + EIDRM => "Identifier removed", + EINPROGRESS => "Operation now in progress", + EALREADY => "Operation already in progress", + ENOTSOCK => "Socket operation on non-socket", + EDESTADDRREQ => "Destination address required", + EMSGSIZE => "Message too long", + EPROTOTYPE => "Protocol wrong type for socket", + ENOPROTOOPT => "Protocol not available", + EPROTONOSUPPORT => "Protocol not supported", + ESOCKTNOSUPPORT => "Socket type not supported", + EPFNOSUPPORT => "Protocol family not supported", + EAFNOSUPPORT => "Address family not supported by protocol", + EADDRINUSE => "Address already in use", + EADDRNOTAVAIL => "Cannot assign requested address", + ENETDOWN => "Network is down", + ENETUNREACH => "Network is unreachable", + ENETRESET => "Network dropped connection because of reset", + ECONNABORTED => "Software caused connection abort", + ECONNRESET => "Connection reset by peer", + ENOBUFS => "No buffer space available", + EISCONN => "Transport endpoint is already connected", + ENOTCONN => "Transport endpoint is not connected", + ESHUTDOWN => "Cannot send after transport endpoint shutdown", + ETOOMANYREFS => "Too many references: cannot splice", + ETIMEDOUT => "Connection timed out", + ECONNREFUSED => "Connection refused", + EHOSTDOWN => "Host is down", + EHOSTUNREACH => "No route to host", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ECHRNG => "Channel number out of range", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EL2NSYNC => "Level 2 not synchronized", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EL3HLT => "Level 3 halted", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EL3RST => "Level 3 reset", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ELNRNG => "Link number out of range", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EUNATCH => "Protocol driver not attached", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ENOCSI => "No CSI structure available", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EL2HLT => "Level 2 halted", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EBADE => "Invalid exchange", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EBADR => "Invalid request descriptor", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EXFULL => "Exchange full", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ENOANO => "No anode", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EBADRQC => "Invalid request code", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EBADSLT => "Invalid slot", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EBFONT => "Bad font file format", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ENOSTR => "Device not a stream", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ENODATA => "No data available", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ETIME => "Timer expired", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ENOSR => "Out of streams resources", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ENONET => "Machine is not on the network", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ENOPKG => "Package not installed", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EREMOTE => "Object is remote", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ENOLINK => "Link has been severed", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EADV => "Advertise error", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ESRMNT => "Srmount error", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ECOMM => "Communication error on send", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EPROTO => "Protocol error", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EMULTIHOP => "Multihop attempted", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EDOTDOT => "RFS specific error", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EBADMSG => "Not a data message", + + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + EBADMSG => "Trying to read unreadable message", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EOVERFLOW => "Value too large for defined data type", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ENOTUNIQ => "Name not unique on network", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EBADFD => "File descriptor in bad state", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EREMCHG => "Remote address changed", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ELIBACC => "Can not access a needed shared library", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ELIBBAD => "Accessing a corrupted shared library", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ELIBSCN => ".lib section in a.out corrupted", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ELIBMAX => "Attempting to link in too many shared libraries", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ELIBEXEC => "Cannot exec a shared library directly", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia", target_os = "openbsd"))] + EILSEQ => "Illegal byte sequence", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ERESTART => "Interrupted system call should be restarted", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ESTRPIPE => "Streams pipe error", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + EUSERS => "Too many users", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia", target_os = "netbsd", + target_os = "redox"))] + EOPNOTSUPP => "Operation not supported on transport endpoint", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + ESTALE => "Stale file handle", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EUCLEAN => "Structure needs cleaning", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + ENOTNAM => "Not a XENIX named type file", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + ENAVAIL => "No XENIX semaphores available", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EISNAM => "Is a named type file", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EREMOTEIO => "Remote I/O error", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EDQUOT => "Quota exceeded", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia", target_os = "openbsd", + target_os = "dragonfly"))] + ENOMEDIUM => "No medium found", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia", target_os = "openbsd"))] + EMEDIUMTYPE => "Wrong medium type", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "illumos", target_os = "solaris", + target_os = "fuchsia"))] + ECANCELED => "Operation canceled", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + ENOKEY => "Required key not available", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EKEYEXPIRED => "Key has expired", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EKEYREVOKED => "Key has been revoked", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EKEYREJECTED => "Key was rejected by service", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + EOWNERDEAD => "Owner died", + + #[cfg(any( target_os = "illumos", target_os = "solaris"))] + EOWNERDEAD => "Process died with lock", + + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] + ENOTRECOVERABLE => "State not recoverable", + + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + ENOTRECOVERABLE => "Lock is not recoverable", + + #[cfg(any(all(target_os = "linux", not(target_arch="mips")), + target_os = "fuchsia"))] + ERFKILL => "Operation not possible due to RF-kill", + + #[cfg(any(all(target_os = "linux", not(target_arch="mips")), + target_os = "fuchsia"))] + EHWPOISON => "Memory page has hardware error", + + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + EDOOFUS => "Programming error", + + #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "redox"))] + EMULTIHOP => "Multihop attempted", + + #[cfg(any(target_os = "freebsd", target_os = "dragonfly", + target_os = "redox"))] + ENOLINK => "Link has been severed", + + #[cfg(target_os = "freebsd")] + ENOTCAPABLE => "Capabilities insufficient", + + #[cfg(target_os = "freebsd")] + ECAPMODE => "Not permitted in capability mode", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd"))] + ENEEDAUTH => "Need authenticator", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd", + target_os = "redox", target_os = "illumos", + target_os = "solaris"))] + EOVERFLOW => "Value too large to be stored in data type", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "netbsd", target_os = "redox"))] + EILSEQ => "Illegal byte sequence", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd"))] + ENOATTR => "Attribute not found", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd", + target_os = "redox"))] + EBADMSG => "Bad message", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd", + target_os = "redox"))] + EPROTO => "Protocol error", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "ios", target_os = "openbsd"))] + ENOTRECOVERABLE => "State not recoverable", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "ios", target_os = "openbsd"))] + EOWNERDEAD => "Previous owner died", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd", + target_os = "illumos", target_os = "solaris"))] + ENOTSUP => "Operation not supported", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd"))] + EPROCLIM => "Too many processes", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd", + target_os = "redox"))] + EUSERS => "Too many users", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd", + target_os = "redox", target_os = "illumos", + target_os = "solaris"))] + EDQUOT => "Disc quota exceeded", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd", + target_os = "redox", target_os = "illumos", + target_os = "solaris"))] + ESTALE => "Stale NFS file handle", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd", + target_os = "redox"))] + EREMOTE => "Too many levels of remote in path", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd"))] + EBADRPC => "RPC struct is bad", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd"))] + ERPCMISMATCH => "RPC version wrong", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd"))] + EPROGUNAVAIL => "RPC prog. not avail", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd"))] + EPROGMISMATCH => "Program version wrong", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd"))] + EPROCUNAVAIL => "Bad procedure for program", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd"))] + EFTYPE => "Inappropriate file type or format", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd"))] + EAUTH => "Authentication error", + + #[cfg(any(target_os = "macos", target_os = "freebsd", + target_os = "dragonfly", target_os = "ios", + target_os = "openbsd", target_os = "netbsd", + target_os = "redox"))] + ECANCELED => "Operation canceled", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EPWROFF => "Device power is off", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EDEVERR => "Device error, e.g. paper out", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EBADEXEC => "Bad executable", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EBADARCH => "Bad CPU type in executable", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + ESHLIBVERS => "Shared library version mismatch", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EBADMACHO => "Malformed Macho file", + + #[cfg(any(target_os = "macos", target_os = "ios", + target_os = "netbsd"))] + EMULTIHOP => "Reserved", + + #[cfg(any(target_os = "macos", target_os = "ios", + target_os = "netbsd", target_os = "redox"))] + ENODATA => "No message available on STREAM", + + #[cfg(any(target_os = "macos", target_os = "ios", + target_os = "netbsd"))] + ENOLINK => "Reserved", + + #[cfg(any(target_os = "macos", target_os = "ios", + target_os = "netbsd", target_os = "redox"))] + ENOSR => "No STREAM resources", + + #[cfg(any(target_os = "macos", target_os = "ios", + target_os = "netbsd", target_os = "redox"))] + ENOSTR => "Not a STREAM", + + #[cfg(any(target_os = "macos", target_os = "ios", + target_os = "netbsd", target_os = "redox"))] + ETIME => "STREAM ioctl timeout", + + #[cfg(any(target_os = "macos", target_os = "ios", + target_os = "illumos", target_os = "solaris"))] + EOPNOTSUPP => "Operation not supported on socket", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + ENOPOLICY => "No such policy registered", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EQFULL => "Interface output queue is full", + + #[cfg(target_os = "openbsd")] + EOPNOTSUPP => "Operation not supported", + + #[cfg(target_os = "openbsd")] + EIPSEC => "IPsec processing failure", + + #[cfg(target_os = "dragonfly")] + EASYNC => "Async", + + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + EDEADLOCK => "Resource deadlock would occur", + + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + ELOCKUNMAPPED => "Locked lock was unmapped", + + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + ENOTACTIVE => "Facility is not active", + } +} + +#[cfg(any(target_os = "linux", target_os = "android", + target_os = "fuchsia"))] +mod consts { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + #[non_exhaustive] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EAGAIN = libc::EAGAIN, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EDEADLK = libc::EDEADLK, + ENAMETOOLONG = libc::ENAMETOOLONG, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + ENOTEMPTY = libc::ENOTEMPTY, + ELOOP = libc::ELOOP, + ENOMSG = libc::ENOMSG, + EIDRM = libc::EIDRM, + ECHRNG = libc::ECHRNG, + EL2NSYNC = libc::EL2NSYNC, + EL3HLT = libc::EL3HLT, + EL3RST = libc::EL3RST, + ELNRNG = libc::ELNRNG, + EUNATCH = libc::EUNATCH, + ENOCSI = libc::ENOCSI, + EL2HLT = libc::EL2HLT, + EBADE = libc::EBADE, + EBADR = libc::EBADR, + EXFULL = libc::EXFULL, + ENOANO = libc::ENOANO, + EBADRQC = libc::EBADRQC, + EBADSLT = libc::EBADSLT, + EBFONT = libc::EBFONT, + ENOSTR = libc::ENOSTR, + ENODATA = libc::ENODATA, + ETIME = libc::ETIME, + ENOSR = libc::ENOSR, + ENONET = libc::ENONET, + ENOPKG = libc::ENOPKG, + EREMOTE = libc::EREMOTE, + ENOLINK = libc::ENOLINK, + EADV = libc::EADV, + ESRMNT = libc::ESRMNT, + ECOMM = libc::ECOMM, + EPROTO = libc::EPROTO, + EMULTIHOP = libc::EMULTIHOP, + EDOTDOT = libc::EDOTDOT, + EBADMSG = libc::EBADMSG, + EOVERFLOW = libc::EOVERFLOW, + ENOTUNIQ = libc::ENOTUNIQ, + EBADFD = libc::EBADFD, + EREMCHG = libc::EREMCHG, + ELIBACC = libc::ELIBACC, + ELIBBAD = libc::ELIBBAD, + ELIBSCN = libc::ELIBSCN, + ELIBMAX = libc::ELIBMAX, + ELIBEXEC = libc::ELIBEXEC, + EILSEQ = libc::EILSEQ, + ERESTART = libc::ERESTART, + ESTRPIPE = libc::ESTRPIPE, + EUSERS = libc::EUSERS, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + EOPNOTSUPP = libc::EOPNOTSUPP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + EALREADY = libc::EALREADY, + EINPROGRESS = libc::EINPROGRESS, + ESTALE = libc::ESTALE, + EUCLEAN = libc::EUCLEAN, + ENOTNAM = libc::ENOTNAM, + ENAVAIL = libc::ENAVAIL, + EISNAM = libc::EISNAM, + EREMOTEIO = libc::EREMOTEIO, + EDQUOT = libc::EDQUOT, + ENOMEDIUM = libc::ENOMEDIUM, + EMEDIUMTYPE = libc::EMEDIUMTYPE, + ECANCELED = libc::ECANCELED, + ENOKEY = libc::ENOKEY, + EKEYEXPIRED = libc::EKEYEXPIRED, + EKEYREVOKED = libc::EKEYREVOKED, + EKEYREJECTED = libc::EKEYREJECTED, + EOWNERDEAD = libc::EOWNERDEAD, + ENOTRECOVERABLE = libc::ENOTRECOVERABLE, + #[cfg(not(any(target_os = "android", target_arch="mips")))] + ERFKILL = libc::ERFKILL, + #[cfg(not(any(target_os = "android", target_arch="mips")))] + EHWPOISON = libc::EHWPOISON, + } + + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EWOULDBLOCK instead" + )] + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EDEADLOCK instead" + )] + pub const EDEADLOCK: Errno = Errno::EDEADLK; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::ENOTSUP instead" + )] + pub const ENOTSUP: Errno = Errno::EOPNOTSUPP; + + impl Errno { + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + pub const EDEADLOCK: Errno = Errno::EDEADLK; + pub const ENOTSUP: Errno = Errno::EOPNOTSUPP; + } + + pub const fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EAGAIN => EAGAIN, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EDEADLK => EDEADLK, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::ENOTEMPTY => ENOTEMPTY, + libc::ELOOP => ELOOP, + libc::ENOMSG => ENOMSG, + libc::EIDRM => EIDRM, + libc::ECHRNG => ECHRNG, + libc::EL2NSYNC => EL2NSYNC, + libc::EL3HLT => EL3HLT, + libc::EL3RST => EL3RST, + libc::ELNRNG => ELNRNG, + libc::EUNATCH => EUNATCH, + libc::ENOCSI => ENOCSI, + libc::EL2HLT => EL2HLT, + libc::EBADE => EBADE, + libc::EBADR => EBADR, + libc::EXFULL => EXFULL, + libc::ENOANO => ENOANO, + libc::EBADRQC => EBADRQC, + libc::EBADSLT => EBADSLT, + libc::EBFONT => EBFONT, + libc::ENOSTR => ENOSTR, + libc::ENODATA => ENODATA, + libc::ETIME => ETIME, + libc::ENOSR => ENOSR, + libc::ENONET => ENONET, + libc::ENOPKG => ENOPKG, + libc::EREMOTE => EREMOTE, + libc::ENOLINK => ENOLINK, + libc::EADV => EADV, + libc::ESRMNT => ESRMNT, + libc::ECOMM => ECOMM, + libc::EPROTO => EPROTO, + libc::EMULTIHOP => EMULTIHOP, + libc::EDOTDOT => EDOTDOT, + libc::EBADMSG => EBADMSG, + libc::EOVERFLOW => EOVERFLOW, + libc::ENOTUNIQ => ENOTUNIQ, + libc::EBADFD => EBADFD, + libc::EREMCHG => EREMCHG, + libc::ELIBACC => ELIBACC, + libc::ELIBBAD => ELIBBAD, + libc::ELIBSCN => ELIBSCN, + libc::ELIBMAX => ELIBMAX, + libc::ELIBEXEC => ELIBEXEC, + libc::EILSEQ => EILSEQ, + libc::ERESTART => ERESTART, + libc::ESTRPIPE => ESTRPIPE, + libc::EUSERS => EUSERS, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::EOPNOTSUPP => EOPNOTSUPP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::EALREADY => EALREADY, + libc::EINPROGRESS => EINPROGRESS, + libc::ESTALE => ESTALE, + libc::EUCLEAN => EUCLEAN, + libc::ENOTNAM => ENOTNAM, + libc::ENAVAIL => ENAVAIL, + libc::EISNAM => EISNAM, + libc::EREMOTEIO => EREMOTEIO, + libc::EDQUOT => EDQUOT, + libc::ENOMEDIUM => ENOMEDIUM, + libc::EMEDIUMTYPE => EMEDIUMTYPE, + libc::ECANCELED => ECANCELED, + libc::ENOKEY => ENOKEY, + libc::EKEYEXPIRED => EKEYEXPIRED, + libc::EKEYREVOKED => EKEYREVOKED, + libc::EKEYREJECTED => EKEYREJECTED, + libc::EOWNERDEAD => EOWNERDEAD, + libc::ENOTRECOVERABLE => ENOTRECOVERABLE, + #[cfg(not(any(target_os = "android", target_arch="mips")))] + libc::ERFKILL => ERFKILL, + #[cfg(not(any(target_os = "android", target_arch="mips")))] + libc::EHWPOISON => EHWPOISON, + _ => UnknownErrno, + } + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod consts { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + #[non_exhaustive] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + ENOTSUP = libc::ENOTSUP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + EBADRPC = libc::EBADRPC, + ERPCMISMATCH = libc::ERPCMISMATCH, + EPROGUNAVAIL = libc::EPROGUNAVAIL, + EPROGMISMATCH = libc::EPROGMISMATCH, + EPROCUNAVAIL = libc::EPROCUNAVAIL, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EFTYPE = libc::EFTYPE, + EAUTH = libc::EAUTH, + ENEEDAUTH = libc::ENEEDAUTH, + EPWROFF = libc::EPWROFF, + EDEVERR = libc::EDEVERR, + EOVERFLOW = libc::EOVERFLOW, + EBADEXEC = libc::EBADEXEC, + EBADARCH = libc::EBADARCH, + ESHLIBVERS = libc::ESHLIBVERS, + EBADMACHO = libc::EBADMACHO, + ECANCELED = libc::ECANCELED, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + EILSEQ = libc::EILSEQ, + ENOATTR = libc::ENOATTR, + EBADMSG = libc::EBADMSG, + EMULTIHOP = libc::EMULTIHOP, + ENODATA = libc::ENODATA, + ENOLINK = libc::ENOLINK, + ENOSR = libc::ENOSR, + ENOSTR = libc::ENOSTR, + EPROTO = libc::EPROTO, + ETIME = libc::ETIME, + EOPNOTSUPP = libc::EOPNOTSUPP, + ENOPOLICY = libc::ENOPOLICY, + ENOTRECOVERABLE = libc::ENOTRECOVERABLE, + EOWNERDEAD = libc::EOWNERDEAD, + EQFULL = libc::EQFULL, + } + + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::ELAST instead" + )] + pub const ELAST: Errno = Errno::EQFULL; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EWOULDBLOCK instead" + )] + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EDEADLOCK instead" + )] + pub const EDEADLOCK: Errno = Errno::EDEADLK; + + impl Errno { + pub const ELAST: Errno = Errno::EQFULL; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + pub const EDEADLOCK: Errno = Errno::EDEADLK; + } + + pub const fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::ENOTSUP => ENOTSUP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::EBADRPC => EBADRPC, + libc::ERPCMISMATCH => ERPCMISMATCH, + libc::EPROGUNAVAIL => EPROGUNAVAIL, + libc::EPROGMISMATCH => EPROGMISMATCH, + libc::EPROCUNAVAIL => EPROCUNAVAIL, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EFTYPE => EFTYPE, + libc::EAUTH => EAUTH, + libc::ENEEDAUTH => ENEEDAUTH, + libc::EPWROFF => EPWROFF, + libc::EDEVERR => EDEVERR, + libc::EOVERFLOW => EOVERFLOW, + libc::EBADEXEC => EBADEXEC, + libc::EBADARCH => EBADARCH, + libc::ESHLIBVERS => ESHLIBVERS, + libc::EBADMACHO => EBADMACHO, + libc::ECANCELED => ECANCELED, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::EILSEQ => EILSEQ, + libc::ENOATTR => ENOATTR, + libc::EBADMSG => EBADMSG, + libc::EMULTIHOP => EMULTIHOP, + libc::ENODATA => ENODATA, + libc::ENOLINK => ENOLINK, + libc::ENOSR => ENOSR, + libc::ENOSTR => ENOSTR, + libc::EPROTO => EPROTO, + libc::ETIME => ETIME, + libc::EOPNOTSUPP => EOPNOTSUPP, + libc::ENOPOLICY => ENOPOLICY, + libc::ENOTRECOVERABLE => ENOTRECOVERABLE, + libc::EOWNERDEAD => EOWNERDEAD, + libc::EQFULL => EQFULL, + _ => UnknownErrno, + } + } +} + +#[cfg(target_os = "freebsd")] +mod consts { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + #[non_exhaustive] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + ENOTSUP = libc::ENOTSUP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + EBADRPC = libc::EBADRPC, + ERPCMISMATCH = libc::ERPCMISMATCH, + EPROGUNAVAIL = libc::EPROGUNAVAIL, + EPROGMISMATCH = libc::EPROGMISMATCH, + EPROCUNAVAIL = libc::EPROCUNAVAIL, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EFTYPE = libc::EFTYPE, + EAUTH = libc::EAUTH, + ENEEDAUTH = libc::ENEEDAUTH, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + EOVERFLOW = libc::EOVERFLOW, + ECANCELED = libc::ECANCELED, + EILSEQ = libc::EILSEQ, + ENOATTR = libc::ENOATTR, + EDOOFUS = libc::EDOOFUS, + EBADMSG = libc::EBADMSG, + EMULTIHOP = libc::EMULTIHOP, + ENOLINK = libc::ENOLINK, + EPROTO = libc::EPROTO, + ENOTCAPABLE = libc::ENOTCAPABLE, + ECAPMODE = libc::ECAPMODE, + ENOTRECOVERABLE = libc::ENOTRECOVERABLE, + EOWNERDEAD = libc::EOWNERDEAD, + } + + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::ELAST instead" + )] + pub const ELAST: Errno = Errno::EOWNERDEAD; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EWOULDBLOCK instead" + )] + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EDEADLOCK instead" + )] + pub const EDEADLOCK: Errno = Errno::EDEADLK; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EOPNOTSUPP instead" + )] + pub const EOPNOTSUPP: Errno = Errno::ENOTSUP; + + impl Errno { + pub const ELAST: Errno = Errno::EOWNERDEAD; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + pub const EDEADLOCK: Errno = Errno::EDEADLK; + pub const EOPNOTSUPP: Errno = Errno::ENOTSUP; + } + + pub const fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::ENOTSUP => ENOTSUP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::EBADRPC => EBADRPC, + libc::ERPCMISMATCH => ERPCMISMATCH, + libc::EPROGUNAVAIL => EPROGUNAVAIL, + libc::EPROGMISMATCH => EPROGMISMATCH, + libc::EPROCUNAVAIL => EPROCUNAVAIL, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EFTYPE => EFTYPE, + libc::EAUTH => EAUTH, + libc::ENEEDAUTH => ENEEDAUTH, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::EOVERFLOW => EOVERFLOW, + libc::ECANCELED => ECANCELED, + libc::EILSEQ => EILSEQ, + libc::ENOATTR => ENOATTR, + libc::EDOOFUS => EDOOFUS, + libc::EBADMSG => EBADMSG, + libc::EMULTIHOP => EMULTIHOP, + libc::ENOLINK => ENOLINK, + libc::EPROTO => EPROTO, + libc::ENOTCAPABLE => ENOTCAPABLE, + libc::ECAPMODE => ECAPMODE, + libc::ENOTRECOVERABLE => ENOTRECOVERABLE, + libc::EOWNERDEAD => EOWNERDEAD, + _ => UnknownErrno, + } + } +} + + +#[cfg(target_os = "dragonfly")] +mod consts { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + #[non_exhaustive] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + ENOTSUP = libc::ENOTSUP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + EBADRPC = libc::EBADRPC, + ERPCMISMATCH = libc::ERPCMISMATCH, + EPROGUNAVAIL = libc::EPROGUNAVAIL, + EPROGMISMATCH = libc::EPROGMISMATCH, + EPROCUNAVAIL = libc::EPROCUNAVAIL, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EFTYPE = libc::EFTYPE, + EAUTH = libc::EAUTH, + ENEEDAUTH = libc::ENEEDAUTH, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + EOVERFLOW = libc::EOVERFLOW, + ECANCELED = libc::ECANCELED, + EILSEQ = libc::EILSEQ, + ENOATTR = libc::ENOATTR, + EDOOFUS = libc::EDOOFUS, + EBADMSG = libc::EBADMSG, + EMULTIHOP = libc::EMULTIHOP, + ENOLINK = libc::ENOLINK, + EPROTO = libc::EPROTO, + ENOMEDIUM = libc::ENOMEDIUM, + EASYNC = libc::EASYNC, + } + + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::ELAST instead" + )] + pub const ELAST: Errno = Errno::EASYNC; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EWOULDBLOCK instead" + )] + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EDEADLOCK instead" + )] + pub const EDEADLOCK: Errno = Errno::EDEADLK; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EOPNOTSUPP instead" + )] + pub const EOPNOTSUPP: Errno = Errno::ENOTSUP; + + impl Errno { + pub const ELAST: Errno = Errno::EASYNC; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + pub const EDEADLOCK: Errno = Errno::EDEADLK; + pub const EOPNOTSUPP: Errno = Errno::ENOTSUP; + } + + pub const fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR=> EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::ENOTSUP => ENOTSUP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::EBADRPC => EBADRPC, + libc::ERPCMISMATCH => ERPCMISMATCH, + libc::EPROGUNAVAIL => EPROGUNAVAIL, + libc::EPROGMISMATCH => EPROGMISMATCH, + libc::EPROCUNAVAIL => EPROCUNAVAIL, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EFTYPE => EFTYPE, + libc::EAUTH => EAUTH, + libc::ENEEDAUTH => ENEEDAUTH, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::EOVERFLOW => EOVERFLOW, + libc::ECANCELED => ECANCELED, + libc::EILSEQ => EILSEQ, + libc::ENOATTR => ENOATTR, + libc::EDOOFUS => EDOOFUS, + libc::EBADMSG => EBADMSG, + libc::EMULTIHOP => EMULTIHOP, + libc::ENOLINK => ENOLINK, + libc::EPROTO => EPROTO, + libc::ENOMEDIUM => ENOMEDIUM, + libc::EASYNC => EASYNC, + _ => UnknownErrno, + } + } +} + + +#[cfg(target_os = "openbsd")] +mod consts { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + #[non_exhaustive] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + EOPNOTSUPP = libc::EOPNOTSUPP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + EBADRPC = libc::EBADRPC, + ERPCMISMATCH = libc::ERPCMISMATCH, + EPROGUNAVAIL = libc::EPROGUNAVAIL, + EPROGMISMATCH = libc::EPROGMISMATCH, + EPROCUNAVAIL = libc::EPROCUNAVAIL, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EFTYPE = libc::EFTYPE, + EAUTH = libc::EAUTH, + ENEEDAUTH = libc::ENEEDAUTH, + EIPSEC = libc::EIPSEC, + ENOATTR = libc::ENOATTR, + EILSEQ = libc::EILSEQ, + ENOMEDIUM = libc::ENOMEDIUM, + EMEDIUMTYPE = libc::EMEDIUMTYPE, + EOVERFLOW = libc::EOVERFLOW, + ECANCELED = libc::ECANCELED, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + ENOTSUP = libc::ENOTSUP, + EBADMSG = libc::EBADMSG, + ENOTRECOVERABLE = libc::ENOTRECOVERABLE, + EOWNERDEAD = libc::EOWNERDEAD, + EPROTO = libc::EPROTO, + } + + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::ELAST instead" + )] + pub const ELAST: Errno = Errno::ENOTSUP; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EWOULDBLOCK instead" + )] + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + + impl Errno { + pub const ELAST: Errno = Errno::ENOTSUP; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + } + + pub const fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::EOPNOTSUPP => EOPNOTSUPP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::EBADRPC => EBADRPC, + libc::ERPCMISMATCH => ERPCMISMATCH, + libc::EPROGUNAVAIL => EPROGUNAVAIL, + libc::EPROGMISMATCH => EPROGMISMATCH, + libc::EPROCUNAVAIL => EPROCUNAVAIL, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EFTYPE => EFTYPE, + libc::EAUTH => EAUTH, + libc::ENEEDAUTH => ENEEDAUTH, + libc::EIPSEC => EIPSEC, + libc::ENOATTR => ENOATTR, + libc::EILSEQ => EILSEQ, + libc::ENOMEDIUM => ENOMEDIUM, + libc::EMEDIUMTYPE => EMEDIUMTYPE, + libc::EOVERFLOW => EOVERFLOW, + libc::ECANCELED => ECANCELED, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::ENOTSUP => ENOTSUP, + libc::EBADMSG => EBADMSG, + libc::ENOTRECOVERABLE => ENOTRECOVERABLE, + libc::EOWNERDEAD => EOWNERDEAD, + libc::EPROTO => EPROTO, + _ => UnknownErrno, + } + } +} + +#[cfg(target_os = "netbsd")] +mod consts { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + #[non_exhaustive] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + EOPNOTSUPP = libc::EOPNOTSUPP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + EBADRPC = libc::EBADRPC, + ERPCMISMATCH = libc::ERPCMISMATCH, + EPROGUNAVAIL = libc::EPROGUNAVAIL, + EPROGMISMATCH = libc::EPROGMISMATCH, + EPROCUNAVAIL = libc::EPROCUNAVAIL, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EFTYPE = libc::EFTYPE, + EAUTH = libc::EAUTH, + ENEEDAUTH = libc::ENEEDAUTH, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + EOVERFLOW = libc::EOVERFLOW, + EILSEQ = libc::EILSEQ, + ENOTSUP = libc::ENOTSUP, + ECANCELED = libc::ECANCELED, + EBADMSG = libc::EBADMSG, + ENODATA = libc::ENODATA, + ENOSR = libc::ENOSR, + ENOSTR = libc::ENOSTR, + ETIME = libc::ETIME, + ENOATTR = libc::ENOATTR, + EMULTIHOP = libc::EMULTIHOP, + ENOLINK = libc::ENOLINK, + EPROTO = libc::EPROTO, + } + + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::ELAST instead" + )] + pub const ELAST: Errno = Errno::ENOTSUP; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EWOULDBLOCK instead" + )] + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + + impl Errno { + pub const ELAST: Errno = Errno::ENOTSUP; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + } + + pub const fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::EOPNOTSUPP => EOPNOTSUPP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::EBADRPC => EBADRPC, + libc::ERPCMISMATCH => ERPCMISMATCH, + libc::EPROGUNAVAIL => EPROGUNAVAIL, + libc::EPROGMISMATCH => EPROGMISMATCH, + libc::EPROCUNAVAIL => EPROCUNAVAIL, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EFTYPE => EFTYPE, + libc::EAUTH => EAUTH, + libc::ENEEDAUTH => ENEEDAUTH, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::EOVERFLOW => EOVERFLOW, + libc::EILSEQ => EILSEQ, + libc::ENOTSUP => ENOTSUP, + libc::ECANCELED => ECANCELED, + libc::EBADMSG => EBADMSG, + libc::ENODATA => ENODATA, + libc::ENOSR => ENOSR, + libc::ENOSTR => ENOSTR, + libc::ETIME => ETIME, + libc::ENOATTR => ENOATTR, + libc::EMULTIHOP => EMULTIHOP, + libc::ENOLINK => ENOLINK, + libc::EPROTO => EPROTO, + _ => UnknownErrno, + } + } +} + +#[cfg(target_os = "redox")] +mod consts { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + #[non_exhaustive] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + EOPNOTSUPP = libc::EOPNOTSUPP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + EOVERFLOW = libc::EOVERFLOW, + EILSEQ = libc::EILSEQ, + ECANCELED = libc::ECANCELED, + EBADMSG = libc::EBADMSG, + ENODATA = libc::ENODATA, + ENOSR = libc::ENOSR, + ENOSTR = libc::ENOSTR, + ETIME = libc::ETIME, + EMULTIHOP = libc::EMULTIHOP, + ENOLINK = libc::ENOLINK, + EPROTO = libc::EPROTO, + } + + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EWOULDBLOCK instead" + )] + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + + impl Errno { + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + } + + pub const fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::EOPNOTSUPP => EOPNOTSUPP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::EOVERFLOW => EOVERFLOW, + libc::EILSEQ => EILSEQ, + libc::ECANCELED => ECANCELED, + libc::EBADMSG => EBADMSG, + libc::ENODATA => ENODATA, + libc::ENOSR => ENOSR, + libc::ENOSTR => ENOSTR, + libc::ETIME => ETIME, + libc::EMULTIHOP => EMULTIHOP, + libc::ENOLINK => ENOLINK, + libc::EPROTO => EPROTO, + _ => UnknownErrno, + } + } +} + +#[cfg(any(target_os = "illumos", target_os = "solaris"))] +mod consts { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + #[non_exhaustive] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EAGAIN = libc::EAGAIN, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + ENOMSG = libc::ENOMSG, + EIDRM = libc::EIDRM, + ECHRNG = libc::ECHRNG, + EL2NSYNC = libc::EL2NSYNC, + EL3HLT = libc::EL3HLT, + EL3RST = libc::EL3RST, + ELNRNG = libc::ELNRNG, + EUNATCH = libc::EUNATCH, + ENOCSI = libc::ENOCSI, + EL2HLT = libc::EL2HLT, + EDEADLK = libc::EDEADLK, + ENOLCK = libc::ENOLCK, + ECANCELED = libc::ECANCELED, + ENOTSUP = libc::ENOTSUP, + EDQUOT = libc::EDQUOT, + EBADE = libc::EBADE, + EBADR = libc::EBADR, + EXFULL = libc::EXFULL, + ENOANO = libc::ENOANO, + EBADRQC = libc::EBADRQC, + EBADSLT = libc::EBADSLT, + EDEADLOCK = libc::EDEADLOCK, + EBFONT = libc::EBFONT, + EOWNERDEAD = libc::EOWNERDEAD, + ENOTRECOVERABLE = libc::ENOTRECOVERABLE, + ENOSTR = libc::ENOSTR, + ENODATA = libc::ENODATA, + ETIME = libc::ETIME, + ENOSR = libc::ENOSR, + ENONET = libc::ENONET, + ENOPKG = libc::ENOPKG, + EREMOTE = libc::EREMOTE, + ENOLINK = libc::ENOLINK, + EADV = libc::EADV, + ESRMNT = libc::ESRMNT, + ECOMM = libc::ECOMM, + EPROTO = libc::EPROTO, + ELOCKUNMAPPED = libc::ELOCKUNMAPPED, + ENOTACTIVE = libc::ENOTACTIVE, + EMULTIHOP = libc::EMULTIHOP, + EBADMSG = libc::EBADMSG, + ENAMETOOLONG = libc::ENAMETOOLONG, + EOVERFLOW = libc::EOVERFLOW, + ENOTUNIQ = libc::ENOTUNIQ, + EBADFD = libc::EBADFD, + EREMCHG = libc::EREMCHG, + ELIBACC = libc::ELIBACC, + ELIBBAD = libc::ELIBBAD, + ELIBSCN = libc::ELIBSCN, + ELIBMAX = libc::ELIBMAX, + ELIBEXEC = libc::ELIBEXEC, + EILSEQ = libc::EILSEQ, + ENOSYS = libc::ENOSYS, + ELOOP = libc::ELOOP, + ERESTART = libc::ERESTART, + ESTRPIPE = libc::ESTRPIPE, + ENOTEMPTY = libc::ENOTEMPTY, + EUSERS = libc::EUSERS, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + EOPNOTSUPP = libc::EOPNOTSUPP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + EALREADY = libc::EALREADY, + EINPROGRESS = libc::EINPROGRESS, + ESTALE = libc::ESTALE, + } + + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::ELAST instead" + )] + pub const ELAST: Errno = Errno::ELAST; + #[deprecated( + since = "0.22.1", + note = "use nix::errno::Errno::EWOULDBLOCK instead" + )] + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + + impl Errno { + pub const ELAST: Errno = Errno::ESTALE; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + } + + pub const fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EAGAIN => EAGAIN, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::ENOMSG => ENOMSG, + libc::EIDRM => EIDRM, + libc::ECHRNG => ECHRNG, + libc::EL2NSYNC => EL2NSYNC, + libc::EL3HLT => EL3HLT, + libc::EL3RST => EL3RST, + libc::ELNRNG => ELNRNG, + libc::EUNATCH => EUNATCH, + libc::ENOCSI => ENOCSI, + libc::EL2HLT => EL2HLT, + libc::EDEADLK => EDEADLK, + libc::ENOLCK => ENOLCK, + libc::ECANCELED => ECANCELED, + libc::ENOTSUP => ENOTSUP, + libc::EDQUOT => EDQUOT, + libc::EBADE => EBADE, + libc::EBADR => EBADR, + libc::EXFULL => EXFULL, + libc::ENOANO => ENOANO, + libc::EBADRQC => EBADRQC, + libc::EBADSLT => EBADSLT, + libc::EDEADLOCK => EDEADLOCK, + libc::EBFONT => EBFONT, + libc::EOWNERDEAD => EOWNERDEAD, + libc::ENOTRECOVERABLE => ENOTRECOVERABLE, + libc::ENOSTR => ENOSTR, + libc::ENODATA => ENODATA, + libc::ETIME => ETIME, + libc::ENOSR => ENOSR, + libc::ENONET => ENONET, + libc::ENOPKG => ENOPKG, + libc::EREMOTE => EREMOTE, + libc::ENOLINK => ENOLINK, + libc::EADV => EADV, + libc::ESRMNT => ESRMNT, + libc::ECOMM => ECOMM, + libc::EPROTO => EPROTO, + libc::ELOCKUNMAPPED => ELOCKUNMAPPED, + libc::ENOTACTIVE => ENOTACTIVE, + libc::EMULTIHOP => EMULTIHOP, + libc::EBADMSG => EBADMSG, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EOVERFLOW => EOVERFLOW, + libc::ENOTUNIQ => ENOTUNIQ, + libc::EBADFD => EBADFD, + libc::EREMCHG => EREMCHG, + libc::ELIBACC => ELIBACC, + libc::ELIBBAD => ELIBBAD, + libc::ELIBSCN => ELIBSCN, + libc::ELIBMAX => ELIBMAX, + libc::ELIBEXEC => ELIBEXEC, + libc::EILSEQ => EILSEQ, + libc::ENOSYS => ENOSYS, + libc::ELOOP => ELOOP, + libc::ERESTART => ERESTART, + libc::ESTRPIPE => ESTRPIPE, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EUSERS => EUSERS, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::EOPNOTSUPP => EOPNOTSUPP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::EALREADY => EALREADY, + libc::EINPROGRESS => EINPROGRESS, + libc::ESTALE => ESTALE, + _ => UnknownErrno, + } + } +} diff --git a/vendor/nix-v0.23.1-patched/src/fcntl.rs b/vendor/nix-v0.23.1-patched/src/fcntl.rs new file mode 100644 index 000000000..dd8e59a6e --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/fcntl.rs @@ -0,0 +1,696 @@ +use crate::errno::Errno; +use libc::{self, c_char, c_int, c_uint, size_t, ssize_t}; +use std::ffi::OsString; +#[cfg(not(target_os = "redox"))] +use std::os::raw; +use std::os::unix::ffi::OsStringExt; +use std::os::unix::io::RawFd; +use crate::sys::stat::Mode; +use crate::{NixPath, Result}; + +#[cfg(any(target_os = "android", target_os = "linux"))] +use std::ptr; // For splice and copy_file_range +#[cfg(any(target_os = "android", target_os = "linux"))] +use crate::sys::uio::IoVec; // For vmsplice + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + any(target_os = "wasi", target_env = "wasi"), + target_env = "uclibc", + target_os = "freebsd" +))] +pub use self::posix_fadvise::*; + +#[cfg(not(target_os = "redox"))] +libc_bitflags! { + pub struct AtFlags: c_int { + AT_REMOVEDIR; + AT_SYMLINK_FOLLOW; + AT_SYMLINK_NOFOLLOW; + #[cfg(any(target_os = "android", target_os = "linux"))] + AT_NO_AUTOMOUNT; + #[cfg(any(target_os = "android", target_os = "linux"))] + AT_EMPTY_PATH; + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + AT_EACCESS; + } +} + +libc_bitflags!( + /// Configuration options for opened files. + pub struct OFlag: c_int { + /// Mask for the access mode of the file. + O_ACCMODE; + /// Use alternate I/O semantics. + #[cfg(target_os = "netbsd")] + O_ALT_IO; + /// Open the file in append-only mode. + O_APPEND; + /// Generate a signal when input or output becomes possible. + #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] + O_ASYNC; + /// Closes the file descriptor once an `execve` call is made. + /// + /// Also sets the file offset to the beginning of the file. + O_CLOEXEC; + /// Create the file if it does not exist. + O_CREAT; + /// Try to minimize cache effects of the I/O for this file. + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd"))] + O_DIRECT; + /// If the specified path isn't a directory, fail. + #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] + O_DIRECTORY; + /// Implicitly follow each `write()` with an `fdatasync()`. + #[cfg(any(target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + O_DSYNC; + /// Error out if a file was not created. + O_EXCL; + /// Open for execute only. + #[cfg(target_os = "freebsd")] + O_EXEC; + /// Open with an exclusive file lock. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox"))] + O_EXLOCK; + /// Same as `O_SYNC`. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + all(target_os = "linux", not(target_env = "musl")), + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox"))] + O_FSYNC; + /// Allow files whose sizes can't be represented in an `off_t` to be opened. + #[cfg(any(target_os = "android", target_os = "linux"))] + O_LARGEFILE; + /// Do not update the file last access time during `read(2)`s. + #[cfg(any(target_os = "android", target_os = "linux"))] + O_NOATIME; + /// Don't attach the device as the process' controlling terminal. + #[cfg(not(target_os = "redox"))] + O_NOCTTY; + /// Same as `O_NONBLOCK`. + #[cfg(not(target_os = "redox"))] + O_NDELAY; + /// `open()` will fail if the given path is a symbolic link. + O_NOFOLLOW; + /// When possible, open the file in nonblocking mode. + O_NONBLOCK; + /// Don't deliver `SIGPIPE`. + #[cfg(target_os = "netbsd")] + O_NOSIGPIPE; + /// Obtain a file descriptor for low-level access. + /// + /// The file itself is not opened and other file operations will fail. + #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))] + O_PATH; + /// Only allow reading. + /// + /// This should not be combined with `O_WRONLY` or `O_RDWR`. + O_RDONLY; + /// Allow both reading and writing. + /// + /// This should not be combined with `O_WRONLY` or `O_RDONLY`. + O_RDWR; + /// Similar to `O_DSYNC` but applies to `read`s instead. + #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))] + O_RSYNC; + /// Skip search permission checks. + #[cfg(target_os = "netbsd")] + O_SEARCH; + /// Open with a shared file lock. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox"))] + O_SHLOCK; + /// Implicitly follow each `write()` with an `fsync()`. + #[cfg(not(target_os = "redox"))] + O_SYNC; + /// Create an unnamed temporary file. + #[cfg(any(target_os = "android", target_os = "linux"))] + O_TMPFILE; + /// Truncate an existing regular file to 0 length if it allows writing. + O_TRUNC; + /// Restore default TTY attributes. + #[cfg(target_os = "freebsd")] + O_TTY_INIT; + /// Only allow writing. + /// + /// This should not be combined with `O_RDONLY` or `O_RDWR`. + O_WRONLY; + } +); + +// The conversion is not identical on all operating systems. +#[allow(clippy::useless_conversion)] +pub fn open(path: &P, oflag: OFlag, mode: Mode) -> Result { + let fd = path.with_nix_path(|cstr| { + unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) } + })?; + + Errno::result(fd) +} + +// The conversion is not identical on all operating systems. +#[allow(clippy::useless_conversion)] +#[cfg(not(target_os = "redox"))] +pub fn openat( + dirfd: RawFd, + path: &P, + oflag: OFlag, + mode: Mode, +) -> Result { + let fd = path.with_nix_path(|cstr| { + unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) } + })?; + Errno::result(fd) +} + +#[cfg(not(target_os = "redox"))] +pub fn renameat( + old_dirfd: Option, + old_path: &P1, + new_dirfd: Option, + new_path: &P2, +) -> Result<()> { + let res = old_path.with_nix_path(|old_cstr| { + new_path.with_nix_path(|new_cstr| unsafe { + libc::renameat( + at_rawfd(old_dirfd), + old_cstr.as_ptr(), + at_rawfd(new_dirfd), + new_cstr.as_ptr(), + ) + }) + })??; + Errno::result(res).map(drop) +} + +#[cfg(all( + target_os = "linux", + target_env = "gnu", +))] +libc_bitflags! { + pub struct RenameFlags: u32 { + RENAME_EXCHANGE; + RENAME_NOREPLACE; + RENAME_WHITEOUT; + } +} + +#[cfg(all( + target_os = "linux", + target_env = "gnu", +))] +pub fn renameat2( + old_dirfd: Option, + old_path: &P1, + new_dirfd: Option, + new_path: &P2, + flags: RenameFlags, +) -> Result<()> { + let res = old_path.with_nix_path(|old_cstr| { + new_path.with_nix_path(|new_cstr| unsafe { + libc::renameat2( + at_rawfd(old_dirfd), + old_cstr.as_ptr(), + at_rawfd(new_dirfd), + new_cstr.as_ptr(), + flags.bits(), + ) + }) + })??; + Errno::result(res).map(drop) +} + +fn wrap_readlink_result(mut v: Vec, len: ssize_t) -> Result { + unsafe { v.set_len(len as usize) } + v.shrink_to_fit(); + Ok(OsString::from_vec(v.to_vec())) +} + +fn readlink_maybe_at( + dirfd: Option, + path: &P, + v: &mut Vec, +) -> Result { + path.with_nix_path(|cstr| unsafe { + match dirfd { + #[cfg(target_os = "redox")] + Some(_) => unreachable!(), + #[cfg(not(target_os = "redox"))] + Some(dirfd) => libc::readlinkat( + dirfd, + cstr.as_ptr(), + v.as_mut_ptr() as *mut c_char, + v.capacity() as size_t, + ), + None => libc::readlink( + cstr.as_ptr(), + v.as_mut_ptr() as *mut c_char, + v.capacity() as size_t, + ), + } + }) +} + +fn inner_readlink(dirfd: Option, path: &P) -> Result { + let mut v = Vec::with_capacity(libc::PATH_MAX as usize); + // simple case: result is strictly less than `PATH_MAX` + let res = readlink_maybe_at(dirfd, path, &mut v)?; + let len = Errno::result(res)?; + debug_assert!(len >= 0); + if (len as usize) < v.capacity() { + return wrap_readlink_result(v, res); + } + // Uh oh, the result is too long... + // Let's try to ask lstat how many bytes to allocate. + let reported_size = match dirfd { + #[cfg(target_os = "redox")] + Some(_) => unreachable!(), + #[cfg(any(target_os = "android", target_os = "linux"))] + Some(dirfd) => { + let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() }; + super::sys::stat::fstatat(dirfd, path, flags | AtFlags::AT_SYMLINK_NOFOLLOW) + }, + #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "redox")))] + Some(dirfd) => super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW), + None => super::sys::stat::lstat(path) + } + .map(|x| x.st_size) + .unwrap_or(0); + let mut try_size = if reported_size > 0 { + // Note: even if `lstat`'s apparently valid answer turns out to be + // wrong, we will still read the full symlink no matter what. + reported_size as usize + 1 + } else { + // If lstat doesn't cooperate, or reports an error, be a little less + // precise. + (libc::PATH_MAX as usize).max(128) << 1 + }; + loop { + v.reserve_exact(try_size); + let res = readlink_maybe_at(dirfd, path, &mut v)?; + let len = Errno::result(res)?; + debug_assert!(len >= 0); + if (len as usize) < v.capacity() { + break wrap_readlink_result(v, res); + } else { + // Ugh! Still not big enough! + match try_size.checked_shl(1) { + Some(next_size) => try_size = next_size, + // It's absurd that this would happen, but handle it sanely + // anyway. + None => break Err(Errno::ENAMETOOLONG), + } + } + } +} + +pub fn readlink(path: &P) -> Result { + inner_readlink(None, path) +} + +#[cfg(not(target_os = "redox"))] +pub fn readlinkat(dirfd: RawFd, path: &P) -> Result { + inner_readlink(Some(dirfd), path) +} + +/// Computes the raw fd consumed by a function of the form `*at`. +#[cfg(not(target_os = "redox"))] +pub(crate) fn at_rawfd(fd: Option) -> raw::c_int { + match fd { + None => libc::AT_FDCWD, + Some(fd) => fd, + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +libc_bitflags!( + /// Additional flags for file sealing, which allows for limiting operations on a file. + pub struct SealFlag: c_int { + /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`. + F_SEAL_SEAL; + /// The file cannot be reduced in size. + F_SEAL_SHRINK; + /// The size of the file cannot be increased. + F_SEAL_GROW; + /// The file contents cannot be modified. + F_SEAL_WRITE; + } +); + +libc_bitflags!( + /// Additional configuration flags for `fcntl`'s `F_SETFD`. + pub struct FdFlag: c_int { + /// The file descriptor will automatically be closed during a successful `execve(2)`. + FD_CLOEXEC; + } +); + +#[cfg(not(target_os = "redox"))] +#[derive(Debug, Eq, Hash, PartialEq)] +#[non_exhaustive] +pub enum FcntlArg<'a> { + F_DUPFD(RawFd), + F_DUPFD_CLOEXEC(RawFd), + F_GETFD, + F_SETFD(FdFlag), // FD_FLAGS + F_GETFL, + F_SETFL(OFlag), // O_NONBLOCK + F_SETLK(&'a libc::flock), + F_SETLKW(&'a libc::flock), + F_GETLK(&'a mut libc::flock), + #[cfg(any(target_os = "linux", target_os = "android"))] + F_OFD_SETLK(&'a libc::flock), + #[cfg(any(target_os = "linux", target_os = "android"))] + F_OFD_SETLKW(&'a libc::flock), + #[cfg(any(target_os = "linux", target_os = "android"))] + F_OFD_GETLK(&'a mut libc::flock), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_ADD_SEALS(SealFlag), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_GET_SEALS, + #[cfg(any(target_os = "macos", target_os = "ios"))] + F_FULLFSYNC, + #[cfg(any(target_os = "linux", target_os = "android"))] + F_GETPIPE_SZ, + #[cfg(any(target_os = "linux", target_os = "android"))] + F_SETPIPE_SZ(c_int), + // TODO: Rest of flags +} + +#[cfg(target_os = "redox")] +#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] +#[non_exhaustive] +pub enum FcntlArg { + F_DUPFD(RawFd), + F_DUPFD_CLOEXEC(RawFd), + F_GETFD, + F_SETFD(FdFlag), // FD_FLAGS + F_GETFL, + F_SETFL(OFlag), // O_NONBLOCK +} +pub use self::FcntlArg::*; + +// TODO: Figure out how to handle value fcntl returns +pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result { + let res = unsafe { + match arg { + F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd), + F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd), + F_GETFD => libc::fcntl(fd, libc::F_GETFD), + F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()), + F_GETFL => libc::fcntl(fd, libc::F_GETFL), + F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()), + #[cfg(not(target_os = "redox"))] + F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock), + #[cfg(not(target_os = "redox"))] + F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock), + #[cfg(not(target_os = "redox"))] + F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS), + #[cfg(any(target_os = "macos", target_os = "ios"))] + F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC), + #[cfg(any(target_os = "linux", target_os = "android"))] + F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ), + #[cfg(any(target_os = "linux", target_os = "android"))] + F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size), + } + }; + + Errno::result(res) +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[non_exhaustive] +pub enum FlockArg { + LockShared, + LockExclusive, + Unlock, + LockSharedNonblock, + LockExclusiveNonblock, + UnlockNonblock, +} + +#[cfg(not(target_os = "redox"))] +pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> { + use self::FlockArg::*; + + let res = unsafe { + match arg { + LockShared => libc::flock(fd, libc::LOCK_SH), + LockExclusive => libc::flock(fd, libc::LOCK_EX), + Unlock => libc::flock(fd, libc::LOCK_UN), + LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB), + LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB), + UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB), + } + }; + + Errno::result(res).map(drop) +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +libc_bitflags! { + /// Additional flags to `splice` and friends. + pub struct SpliceFFlags: c_uint { + /// Request that pages be moved instead of copied. + /// + /// Not applicable to `vmsplice`. + SPLICE_F_MOVE; + /// Do not block on I/O. + SPLICE_F_NONBLOCK; + /// Hint that more data will be coming in a subsequent splice. + /// + /// Not applicable to `vmsplice`. + SPLICE_F_MORE; + /// Gift the user pages to the kernel. + /// + /// Not applicable to `splice`. + SPLICE_F_GIFT; + } +} + +/// Copy a range of data from one file to another +/// +/// The `copy_file_range` system call performs an in-kernel copy between +/// file descriptors `fd_in` and `fd_out` without the additional cost of +/// transferring data from the kernel to user space and then back into the +/// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to +/// file descriptor `fd_out`, overwriting any data that exists within the +/// requested range of the target file. +/// +/// If the `off_in` and/or `off_out` arguments are used, the values +/// will be mutated to reflect the new position within the file after +/// copying. If they are not used, the relevant filedescriptors will be seeked +/// to the new position. +/// +/// On successful completion the number of bytes actually copied will be +/// returned. +#[cfg(any(target_os = "android", target_os = "linux"))] +pub fn copy_file_range( + fd_in: RawFd, + off_in: Option<&mut libc::loff_t>, + fd_out: RawFd, + off_out: Option<&mut libc::loff_t>, + len: usize, +) -> Result { + let off_in = off_in + .map(|offset| offset as *mut libc::loff_t) + .unwrap_or(ptr::null_mut()); + let off_out = off_out + .map(|offset| offset as *mut libc::loff_t) + .unwrap_or(ptr::null_mut()); + + let ret = unsafe { + libc::syscall( + libc::SYS_copy_file_range, + fd_in, + off_in, + fd_out, + off_out, + len, + 0, + ) + }; + Errno::result(ret).map(|r| r as usize) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn splice( + fd_in: RawFd, + off_in: Option<&mut libc::loff_t>, + fd_out: RawFd, + off_out: Option<&mut libc::loff_t>, + len: usize, + flags: SpliceFFlags, +) -> Result { + let off_in = off_in + .map(|offset| offset as *mut libc::loff_t) + .unwrap_or(ptr::null_mut()); + let off_out = off_out + .map(|offset| offset as *mut libc::loff_t) + .unwrap_or(ptr::null_mut()); + + let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) }; + Errno::result(ret).map(|r| r as usize) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result { + let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) }; + Errno::result(ret).map(|r| r as usize) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result { + let ret = unsafe { + libc::vmsplice( + fd, + iov.as_ptr() as *const libc::iovec, + iov.len(), + flags.bits(), + ) + }; + Errno::result(ret).map(|r| r as usize) +} + +#[cfg(any(target_os = "linux"))] +libc_bitflags!( + /// Mode argument flags for fallocate determining operation performed on a given range. + pub struct FallocateFlags: c_int { + /// File size is not changed. + /// + /// offset + len can be greater than file size. + FALLOC_FL_KEEP_SIZE; + /// Deallocates space by creating a hole. + /// + /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes. + FALLOC_FL_PUNCH_HOLE; + /// Removes byte range from a file without leaving a hole. + /// + /// Byte range to collapse starts at offset and continues for len bytes. + FALLOC_FL_COLLAPSE_RANGE; + /// Zeroes space in specified byte range. + /// + /// Byte range starts at offset and continues for len bytes. + FALLOC_FL_ZERO_RANGE; + /// Increases file space by inserting a hole within the file size. + /// + /// Does not overwrite existing data. Hole starts at offset and continues for len bytes. + FALLOC_FL_INSERT_RANGE; + /// Shared file data extants are made private to the file. + /// + /// Gaurantees that a subsequent write will not fail due to lack of space. + FALLOC_FL_UNSHARE_RANGE; + } +); + +/// Manipulates file space. +/// +/// Allows the caller to directly manipulate the allocated disk space for the +/// file referred to by fd. +#[cfg(any(target_os = "linux"))] +pub fn fallocate( + fd: RawFd, + mode: FallocateFlags, + offset: libc::off_t, + len: libc::off_t, +) -> Result<()> { + let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) }; + Errno::result(res).map(drop) +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + any(target_os = "wasi", target_env = "wasi"), + target_env = "uclibc", + target_os = "freebsd" +))] +mod posix_fadvise { + use crate::errno::Errno; + use std::os::unix::io::RawFd; + use crate::Result; + + libc_enum! { + #[repr(i32)] + #[non_exhaustive] + pub enum PosixFadviseAdvice { + POSIX_FADV_NORMAL, + POSIX_FADV_SEQUENTIAL, + POSIX_FADV_RANDOM, + POSIX_FADV_NOREUSE, + POSIX_FADV_WILLNEED, + POSIX_FADV_DONTNEED, + } + } + + pub fn posix_fadvise( + fd: RawFd, + offset: libc::off_t, + len: libc::off_t, + advice: PosixFadviseAdvice, + ) -> Result<()> { + let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) }; + + if res == 0 { + Ok(()) + } else { + Err(Errno::from_i32(res)) + } + } +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + any(target_os = "wasi", target_env = "wasi"), + target_os = "freebsd" +))] +pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> { + let res = unsafe { libc::posix_fallocate(fd, offset, len) }; + match Errno::result(res) { + Err(err) => Err(err), + Ok(0) => Ok(()), + Ok(errno) => Err(Errno::from_i32(errno)), + } +} diff --git a/vendor/nix-v0.23.1-patched/src/features.rs b/vendor/nix-v0.23.1-patched/src/features.rs new file mode 100644 index 000000000..ed80fd714 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/features.rs @@ -0,0 +1,121 @@ +//! Feature tests for OS functionality +pub use self::os::*; + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod os { + use crate::sys::utsname::uname; + + // Features: + // * atomic cloexec on socket: 2.6.27 + // * pipe2: 2.6.27 + // * accept4: 2.6.28 + + static VERS_UNKNOWN: usize = 1; + static VERS_2_6_18: usize = 2; + static VERS_2_6_27: usize = 3; + static VERS_2_6_28: usize = 4; + static VERS_3: usize = 5; + + #[inline] + fn digit(dst: &mut usize, b: u8) { + *dst *= 10; + *dst += (b - b'0') as usize; + } + + fn parse_kernel_version() -> usize { + let u = uname(); + + let mut curr: usize = 0; + let mut major: usize = 0; + let mut minor: usize = 0; + let mut patch: usize = 0; + + for b in u.release().bytes() { + if curr >= 3 { + break; + } + + match b { + b'.' | b'-' => { + curr += 1; + } + b'0'..=b'9' => { + match curr { + 0 => digit(&mut major, b), + 1 => digit(&mut minor, b), + _ => digit(&mut patch, b), + } + } + _ => break, + } + } + + if major >= 3 { + VERS_3 + } else if major >= 2 { + if minor >= 7 { + VERS_UNKNOWN + } else if minor >= 6 { + if patch >= 28 { + VERS_2_6_28 + } else if patch >= 27 { + VERS_2_6_27 + } else { + VERS_2_6_18 + } + } else { + VERS_UNKNOWN + } + } else { + VERS_UNKNOWN + } + } + + fn kernel_version() -> usize { + static mut KERNEL_VERS: usize = 0; + + unsafe { + if KERNEL_VERS == 0 { + KERNEL_VERS = parse_kernel_version(); + } + + KERNEL_VERS + } + } + + /// Check if the OS supports atomic close-on-exec for sockets + pub fn socket_atomic_cloexec() -> bool { + kernel_version() >= VERS_2_6_27 + } + + #[test] + pub fn test_parsing_kernel_version() { + assert!(kernel_version() > 0); + } +} + +#[cfg(any( + target_os = "dragonfly", // Since ??? + target_os = "freebsd", // Since 10.0 + target_os = "illumos", // Since ??? + target_os = "netbsd", // Since 6.0 + target_os = "openbsd", // Since 5.7 + target_os = "redox", // Since 1-july-2020 +))] +mod os { + /// Check if the OS supports atomic close-on-exec for sockets + pub const fn socket_atomic_cloexec() -> bool { + true + } +} + +#[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "fuchsia", + target_os = "solaris"))] +mod os { + /// Check if the OS supports atomic close-on-exec for sockets + pub const fn socket_atomic_cloexec() -> bool { + false + } +} diff --git a/vendor/nix-v0.23.1-patched/src/ifaddrs.rs b/vendor/nix-v0.23.1-patched/src/ifaddrs.rs new file mode 100644 index 000000000..ed6328f3e --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/ifaddrs.rs @@ -0,0 +1,147 @@ +//! Query network interface addresses +//! +//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list +//! of interfaces and their associated addresses. + +use cfg_if::cfg_if; +use std::ffi; +use std::iter::Iterator; +use std::mem; +use std::option::Option; + +use crate::{Result, Errno}; +use crate::sys::socket::SockAddr; +use crate::net::if_::*; + +/// Describes a single address for an interface as returned by `getifaddrs`. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct InterfaceAddress { + /// Name of the network interface + pub interface_name: String, + /// Flags as from `SIOCGIFFLAGS` ioctl + pub flags: InterfaceFlags, + /// Network address of this interface + pub address: Option, + /// Netmask of this interface + pub netmask: Option, + /// Broadcast address of this interface, if applicable + pub broadcast: Option, + /// Point-to-point destination address + pub destination: Option, +} + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] { + fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr { + info.ifa_ifu + } + } else { + fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr { + info.ifa_dstaddr + } + } +} + +impl InterfaceAddress { + /// Create an `InterfaceAddress` from the libc struct. + fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress { + let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) }; + let address = unsafe { SockAddr::from_libc_sockaddr(info.ifa_addr) }; + let netmask = unsafe { SockAddr::from_libc_sockaddr(info.ifa_netmask) }; + let mut addr = InterfaceAddress { + interface_name: ifname.to_string_lossy().to_string(), + flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32), + address, + netmask, + broadcast: None, + destination: None, + }; + + let ifu = get_ifu_from_sockaddr(info); + if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) { + addr.destination = unsafe { SockAddr::from_libc_sockaddr(ifu) }; + } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) { + addr.broadcast = unsafe { SockAddr::from_libc_sockaddr(ifu) }; + } + + addr + } +} + +/// Holds the results of `getifaddrs`. +/// +/// Use the function `getifaddrs` to create this Iterator. Note that the +/// actual list of interfaces can be iterated once and will be freed as +/// soon as the Iterator goes out of scope. +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct InterfaceAddressIterator { + base: *mut libc::ifaddrs, + next: *mut libc::ifaddrs, +} + +impl Drop for InterfaceAddressIterator { + fn drop(&mut self) { + unsafe { libc::freeifaddrs(self.base) }; + } +} + +impl Iterator for InterfaceAddressIterator { + type Item = InterfaceAddress; + fn next(&mut self) -> Option<::Item> { + match unsafe { self.next.as_ref() } { + Some(ifaddr) => { + self.next = ifaddr.ifa_next; + Some(InterfaceAddress::from_libc_ifaddrs(ifaddr)) + } + None => None, + } + } +} + +/// Get interface addresses using libc's `getifaddrs` +/// +/// Note that the underlying implementation differs between OSes. Only the +/// most common address families are supported by the nix crate (due to +/// lack of time and complexity of testing). The address family is encoded +/// in the specific variant of `SockAddr` returned for the fields `address`, +/// `netmask`, `broadcast`, and `destination`. For any entry not supported, +/// the returned list will contain a `None` entry. +/// +/// # Example +/// ``` +/// let addrs = nix::ifaddrs::getifaddrs().unwrap(); +/// for ifaddr in addrs { +/// match ifaddr.address { +/// Some(address) => { +/// println!("interface {} address {}", +/// ifaddr.interface_name, address); +/// }, +/// None => { +/// println!("interface {} with unsupported address family", +/// ifaddr.interface_name); +/// } +/// } +/// } +/// ``` +pub fn getifaddrs() -> Result { + let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit(); + unsafe { + Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| { + InterfaceAddressIterator { + base: addrs.assume_init(), + next: addrs.assume_init(), + } + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Only checks if `getifaddrs` can be invoked without panicking. + #[test] + fn test_getifaddrs() { + let _ = getifaddrs(); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/kmod.rs b/vendor/nix-v0.23.1-patched/src/kmod.rs new file mode 100644 index 000000000..c42068c70 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/kmod.rs @@ -0,0 +1,122 @@ +//! Load and unload kernel modules. +//! +//! For more details see + +use std::ffi::CStr; +use std::os::unix::io::AsRawFd; + +use crate::errno::Errno; +use crate::Result; + +/// Loads a kernel module from a buffer. +/// +/// It loads an ELF image into kernel space, +/// performs any necessary symbol relocations, +/// initializes module parameters to values provided by the caller, +/// and then runs the module's init function. +/// +/// This function requires `CAP_SYS_MODULE` privilege. +/// +/// The `module_image` argument points to a buffer containing the binary image +/// to be loaded. The buffer should contain a valid ELF image +/// built for the running kernel. +/// +/// The `param_values` argument is a string containing space-delimited specifications +/// of the values for module parameters. +/// Each of the parameter specifications has the form: +/// +/// `name[=value[,value...]]` +/// +/// # Example +/// +/// ```no_run +/// use std::fs::File; +/// use std::io::Read; +/// use std::ffi::CString; +/// use nix::kmod::init_module; +/// +/// let mut f = File::open("mykernel.ko").unwrap(); +/// let mut contents: Vec = Vec::new(); +/// f.read_to_end(&mut contents).unwrap(); +/// init_module(&mut contents, &CString::new("who=Rust when=Now,12").unwrap()).unwrap(); +/// ``` +/// +/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information. +pub fn init_module(module_image: &[u8], param_values: &CStr) -> Result<()> { + let res = unsafe { + libc::syscall( + libc::SYS_init_module, + module_image.as_ptr(), + module_image.len(), + param_values.as_ptr(), + ) + }; + + Errno::result(res).map(drop) +} + +libc_bitflags!( + /// Flags used by the `finit_module` function. + pub struct ModuleInitFlags: libc::c_uint { + /// Ignore symbol version hashes. + MODULE_INIT_IGNORE_MODVERSIONS; + /// Ignore kernel version magic. + MODULE_INIT_IGNORE_VERMAGIC; + } +); + +/// Loads a kernel module from a given file descriptor. +/// +/// # Example +/// +/// ```no_run +/// use std::fs::File; +/// use std::ffi::CString; +/// use nix::kmod::{finit_module, ModuleInitFlags}; +/// +/// let f = File::open("mymod.ko").unwrap(); +/// finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()).unwrap(); +/// ``` +/// +/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information. +pub fn finit_module(fd: &T, param_values: &CStr, flags: ModuleInitFlags) -> Result<()> { + let res = unsafe { + libc::syscall( + libc::SYS_finit_module, + fd.as_raw_fd(), + param_values.as_ptr(), + flags.bits(), + ) + }; + + Errno::result(res).map(drop) +} + +libc_bitflags!( + /// Flags used by `delete_module`. + /// + /// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html) + /// for a detailed description how these flags work. + pub struct DeleteModuleFlags: libc::c_int { + O_NONBLOCK; + O_TRUNC; + } +); + +/// Unloads the kernel module with the given name. +/// +/// # Example +/// +/// ```no_run +/// use std::ffi::CString; +/// use nix::kmod::{delete_module, DeleteModuleFlags}; +/// +/// delete_module(&CString::new("mymod").unwrap(), DeleteModuleFlags::O_NONBLOCK).unwrap(); +/// ``` +/// +/// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html) for more information. +pub fn delete_module(name: &CStr, flags: DeleteModuleFlags) -> Result<()> { + let res = unsafe { libc::syscall(libc::SYS_delete_module, name.as_ptr(), flags.bits()) }; + + Errno::result(res).map(drop) +} diff --git a/vendor/nix-v0.23.1-patched/src/lib.rs b/vendor/nix-v0.23.1-patched/src/lib.rs new file mode 100644 index 000000000..3a2b63ab0 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/lib.rs @@ -0,0 +1,227 @@ +//! Rust friendly bindings to the various *nix system functions. +//! +//! Modules are structured according to the C header file that they would be +//! defined in. +#![crate_name = "nix"] +#![cfg(unix)] +#![allow(non_camel_case_types)] +#![cfg_attr(test, deny(warnings))] +#![recursion_limit = "500"] +#![deny(unused)] +#![deny(unstable_features)] +#![deny(missing_copy_implementations)] +#![deny(missing_debug_implementations)] +#![warn(missing_docs)] + +// Re-exported external crates +pub use libc; + +// Private internal modules +#[macro_use] mod macros; + +// Public crates +#[cfg(not(target_os = "redox"))] +#[allow(missing_docs)] +pub mod dir; +pub mod env; +#[allow(missing_docs)] +pub mod errno; +pub mod features; +#[allow(missing_docs)] +pub mod fcntl; +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "illumos", + target_os = "openbsd"))] +pub mod ifaddrs; +#[cfg(any(target_os = "android", + target_os = "linux"))] +#[allow(missing_docs)] +pub mod kmod; +#[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "linux"))] +pub mod mount; +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "fushsia", + target_os = "linux", + target_os = "netbsd"))] +#[allow(missing_docs)] +pub mod mqueue; +#[cfg(not(target_os = "redox"))] +pub mod net; +pub mod poll; +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +pub mod pty; +pub mod sched; +pub mod sys; +#[allow(missing_docs)] +pub mod time; +// This can be implemented for other platforms as soon as libc +// provides bindings for them. +#[cfg(all(target_os = "linux", + any(target_arch = "x86", target_arch = "x86_64")))] +#[allow(missing_docs)] +pub mod ucontext; +#[allow(missing_docs)] +pub mod unistd; + +/* + * + * ===== Result / Error ===== + * + */ + +use libc::{c_char, PATH_MAX}; + +use std::{ptr, result}; +use std::ffi::{CStr, OsStr}; +use std::os::unix::ffi::OsStrExt; +use std::path::{Path, PathBuf}; + +use errno::Errno; + +/// Nix Result Type +pub type Result = result::Result; + +/// Nix's main error type. +/// +/// It's a wrapper around Errno. As such, it's very interoperable with +/// [`std::io::Error`], but it has the advantages of: +/// * `Clone` +/// * `Copy` +/// * `Eq` +/// * Small size +/// * Represents all of the system's errnos, instead of just the most common +/// ones. +pub type Error = Errno; + +/// Common trait used to represent file system paths by many Nix functions. +pub trait NixPath { + /// Is the path empty? + fn is_empty(&self) -> bool; + + /// Length of the path in bytes + fn len(&self) -> usize; + + /// Execute a function with this path as a `CStr`. + /// + /// Mostly used internally by Nix. + fn with_nix_path(&self, f: F) -> Result + where F: FnOnce(&CStr) -> T; +} + +impl NixPath for str { + fn is_empty(&self) -> bool { + NixPath::is_empty(OsStr::new(self)) + } + + fn len(&self) -> usize { + NixPath::len(OsStr::new(self)) + } + + fn with_nix_path(&self, f: F) -> Result + where F: FnOnce(&CStr) -> T { + OsStr::new(self).with_nix_path(f) + } +} + +impl NixPath for OsStr { + fn is_empty(&self) -> bool { + self.as_bytes().is_empty() + } + + fn len(&self) -> usize { + self.as_bytes().len() + } + + fn with_nix_path(&self, f: F) -> Result + where F: FnOnce(&CStr) -> T { + self.as_bytes().with_nix_path(f) + } +} + +impl NixPath for CStr { + fn is_empty(&self) -> bool { + self.to_bytes().is_empty() + } + + fn len(&self) -> usize { + self.to_bytes().len() + } + + fn with_nix_path(&self, f: F) -> Result + where F: FnOnce(&CStr) -> T { + // Equivalence with the [u8] impl. + if self.len() >= PATH_MAX as usize { + return Err(Errno::ENAMETOOLONG) + } + + Ok(f(self)) + } +} + +impl NixPath for [u8] { + fn is_empty(&self) -> bool { + self.is_empty() + } + + fn len(&self) -> usize { + self.len() + } + + fn with_nix_path(&self, f: F) -> Result + where F: FnOnce(&CStr) -> T { + let mut buf = [0u8; PATH_MAX as usize]; + + if self.len() >= PATH_MAX as usize { + return Err(Errno::ENAMETOOLONG) + } + + match self.iter().position(|b| *b == 0) { + Some(_) => Err(Errno::EINVAL), + None => { + unsafe { + // TODO: Replace with bytes::copy_memory. rust-lang/rust#24028 + ptr::copy_nonoverlapping(self.as_ptr(), buf.as_mut_ptr(), self.len()); + Ok(f(CStr::from_ptr(buf.as_ptr() as *const c_char))) + } + + } + } + } +} + +impl NixPath for Path { + fn is_empty(&self) -> bool { + NixPath::is_empty(self.as_os_str()) + } + + fn len(&self) -> usize { + NixPath::len(self.as_os_str()) + } + + fn with_nix_path(&self, f: F) -> Result where F: FnOnce(&CStr) -> T { + self.as_os_str().with_nix_path(f) + } +} + +impl NixPath for PathBuf { + fn is_empty(&self) -> bool { + NixPath::is_empty(self.as_os_str()) + } + + fn len(&self) -> usize { + NixPath::len(self.as_os_str()) + } + + fn with_nix_path(&self, f: F) -> Result where F: FnOnce(&CStr) -> T { + self.as_os_str().with_nix_path(f) + } +} diff --git a/vendor/nix-v0.23.1-patched/src/macros.rs b/vendor/nix-v0.23.1-patched/src/macros.rs new file mode 100644 index 000000000..3ccbfdd43 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/macros.rs @@ -0,0 +1,311 @@ +/// The `libc_bitflags!` macro helps with a common use case of defining a public bitflags type +/// with values from the libc crate. It is used the same way as the `bitflags!` macro, except +/// that only the name of the flag value has to be given. +/// +/// The `libc` crate must be in scope with the name `libc`. +/// +/// # Example +/// ``` +/// libc_bitflags!{ +/// pub struct ProtFlags: libc::c_int { +/// PROT_NONE; +/// PROT_READ; +/// /// PROT_WRITE enables write protect +/// PROT_WRITE; +/// PROT_EXEC; +/// #[cfg(any(target_os = "linux", target_os = "android"))] +/// PROT_GROWSDOWN; +/// #[cfg(any(target_os = "linux", target_os = "android"))] +/// PROT_GROWSUP; +/// } +/// } +/// ``` +/// +/// Example with casting, due to a mistake in libc. In this example, the +/// various flags have different types, so we cast the broken ones to the right +/// type. +/// +/// ``` +/// libc_bitflags!{ +/// pub struct SaFlags: libc::c_ulong { +/// SA_NOCLDSTOP as libc::c_ulong; +/// SA_NOCLDWAIT; +/// SA_NODEFER as libc::c_ulong; +/// SA_ONSTACK; +/// SA_RESETHAND as libc::c_ulong; +/// SA_RESTART as libc::c_ulong; +/// SA_SIGINFO; +/// } +/// } +/// ``` +macro_rules! libc_bitflags { + ( + $(#[$outer:meta])* + pub struct $BitFlags:ident: $T:ty { + $( + $(#[$inner:ident $($args:tt)*])* + $Flag:ident $(as $cast:ty)*; + )+ + } + ) => { + ::bitflags::bitflags! { + $(#[$outer])* + pub struct $BitFlags: $T { + $( + $(#[$inner $($args)*])* + const $Flag = libc::$Flag $(as $cast)*; + )+ + } + } + }; +} + +/// The `libc_enum!` macro helps with a common use case of defining an enum exclusively using +/// values from the `libc` crate. This macro supports both `pub` and private `enum`s. +/// +/// The `libc` crate must be in scope with the name `libc`. +/// +/// # Example +/// ``` +/// libc_enum!{ +/// pub enum ProtFlags { +/// PROT_NONE, +/// PROT_READ, +/// PROT_WRITE, +/// PROT_EXEC, +/// #[cfg(any(target_os = "linux", target_os = "android"))] +/// PROT_GROWSDOWN, +/// #[cfg(any(target_os = "linux", target_os = "android"))] +/// PROT_GROWSUP, +/// } +/// } +/// ``` +macro_rules! libc_enum { + // Exit rule. + (@make_enum + name: $BitFlags:ident, + { + $v:vis + attrs: [$($attrs:tt)*], + entries: [$($entries:tt)*], + } + ) => { + $($attrs)* + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + $v enum $BitFlags { + $($entries)* + } + }; + + // Exit rule including TryFrom + (@make_enum + name: $BitFlags:ident, + { + $v:vis + attrs: [$($attrs:tt)*], + entries: [$($entries:tt)*], + from_type: $repr:path, + try_froms: [$($try_froms:tt)*] + } + ) => { + $($attrs)* + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + $v enum $BitFlags { + $($entries)* + } + impl ::std::convert::TryFrom<$repr> for $BitFlags { + type Error = $crate::Error; + #[allow(unused_doc_comments)] + fn try_from(x: $repr) -> $crate::Result { + match x { + $($try_froms)* + _ => Err($crate::Error::EINVAL) + } + } + } + }; + + // Done accumulating. + (@accumulate_entries + name: $BitFlags:ident, + { + $v:vis + attrs: $attrs:tt, + }, + $entries:tt, + $try_froms:tt; + ) => { + libc_enum! { + @make_enum + name: $BitFlags, + { + $v + attrs: $attrs, + entries: $entries, + } + } + }; + + // Done accumulating and want TryFrom + (@accumulate_entries + name: $BitFlags:ident, + { + $v:vis + attrs: $attrs:tt, + from_type: $repr:path, + }, + $entries:tt, + $try_froms:tt; + ) => { + libc_enum! { + @make_enum + name: $BitFlags, + { + $v + attrs: $attrs, + entries: $entries, + from_type: $repr, + try_froms: $try_froms + } + } + }; + + // Munch an attr. + (@accumulate_entries + name: $BitFlags:ident, + $prefix:tt, + [$($entries:tt)*], + [$($try_froms:tt)*]; + #[$attr:meta] $($tail:tt)* + ) => { + libc_enum! { + @accumulate_entries + name: $BitFlags, + $prefix, + [ + $($entries)* + #[$attr] + ], + [ + $($try_froms)* + #[$attr] + ]; + $($tail)* + } + }; + + // Munch last ident if not followed by a comma. + (@accumulate_entries + name: $BitFlags:ident, + $prefix:tt, + [$($entries:tt)*], + [$($try_froms:tt)*]; + $entry:ident + ) => { + libc_enum! { + @accumulate_entries + name: $BitFlags, + $prefix, + [ + $($entries)* + $entry = libc::$entry, + ], + [ + $($try_froms)* + libc::$entry => Ok($BitFlags::$entry), + ]; + } + }; + + // Munch an ident; covers terminating comma case. + (@accumulate_entries + name: $BitFlags:ident, + $prefix:tt, + [$($entries:tt)*], + [$($try_froms:tt)*]; + $entry:ident, + $($tail:tt)* + ) => { + libc_enum! { + @accumulate_entries + name: $BitFlags, + $prefix, + [ + $($entries)* + $entry = libc::$entry, + ], + [ + $($try_froms)* + libc::$entry => Ok($BitFlags::$entry), + ]; + $($tail)* + } + }; + + // Munch an ident and cast it to the given type; covers terminating comma. + (@accumulate_entries + name: $BitFlags:ident, + $prefix:tt, + [$($entries:tt)*], + [$($try_froms:tt)*]; + $entry:ident as $ty:ty, + $($tail:tt)* + ) => { + libc_enum! { + @accumulate_entries + name: $BitFlags, + $prefix, + [ + $($entries)* + $entry = libc::$entry as $ty, + ], + [ + $($try_froms)* + libc::$entry as $ty => Ok($BitFlags::$entry), + ]; + $($tail)* + } + }; + + // Entry rule. + ( + $(#[$attr:meta])* + $v:vis enum $BitFlags:ident { + $($vals:tt)* + } + ) => { + libc_enum! { + @accumulate_entries + name: $BitFlags, + { + $v + attrs: [$(#[$attr])*], + }, + [], + []; + $($vals)* + } + }; + + // Entry rule including TryFrom + ( + $(#[$attr:meta])* + $v:vis enum $BitFlags:ident { + $($vals:tt)* + } + impl TryFrom<$repr:path> + ) => { + libc_enum! { + @accumulate_entries + name: $BitFlags, + { + $v + attrs: [$(#[$attr])*], + from_type: $repr, + }, + [], + []; + $($vals)* + } + }; +} diff --git a/vendor/nix-v0.23.1-patched/src/mount/bsd.rs b/vendor/nix-v0.23.1-patched/src/mount/bsd.rs new file mode 100644 index 000000000..627bfa5ec --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/mount/bsd.rs @@ -0,0 +1,426 @@ +use crate::{ + Error, + Errno, + NixPath, + Result, + sys::uio::IoVec +}; +use libc::{c_char, c_int, c_uint, c_void}; +use std::{ + borrow::Cow, + ffi::{CString, CStr}, + fmt, + io, + ptr +}; + + +libc_bitflags!( + /// Used with [`Nmount::nmount`]. + pub struct MntFlags: c_int { + /// ACL support enabled. + #[cfg(any(target_os = "netbsd", target_os = "freebsd"))] + MNT_ACLS; + /// All I/O to the file system should be done asynchronously. + MNT_ASYNC; + /// dir should instead be a file system ID encoded as “FSID:val0:val1”. + #[cfg(target_os = "freebsd")] + MNT_BYFSID; + /// Force a read-write mount even if the file system appears to be + /// unclean. + MNT_FORCE; + /// GEOM journal support enabled. + #[cfg(target_os = "freebsd")] + MNT_GJOURNAL; + /// MAC support for objects. + #[cfg(any(target_os = "macos", target_os = "freebsd"))] + MNT_MULTILABEL; + /// Disable read clustering. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MNT_NOCLUSTERR; + /// Disable write clustering. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MNT_NOCLUSTERW; + /// Enable NFS version 4 ACLs. + #[cfg(target_os = "freebsd")] + MNT_NFS4ACLS; + /// Do not update access times. + MNT_NOATIME; + /// Disallow program execution. + MNT_NOEXEC; + /// Do not honor setuid or setgid bits on files when executing them. + MNT_NOSUID; + /// Do not follow symlinks. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MNT_NOSYMFOLLOW; + /// Mount read-only. + MNT_RDONLY; + /// Causes the vfs subsystem to update its data structures pertaining to + /// the specified already mounted file system. + MNT_RELOAD; + /// Create a snapshot of the file system. + /// + /// See [mksnap_ffs(8)](https://www.freebsd.org/cgi/man.cgi?query=mksnap_ffs) + #[cfg(any(target_os = "macos", target_os = "freebsd"))] + MNT_SNAPSHOT; + /// Using soft updates. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + MNT_SOFTDEP; + /// Directories with the SUID bit set chown new files to their own + /// owner. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MNT_SUIDDIR; + /// All I/O to the file system should be done synchronously. + MNT_SYNCHRONOUS; + /// Union with underlying fs. + #[cfg(any( + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd" + ))] + MNT_UNION; + /// Indicates that the mount command is being applied to an already + /// mounted file system. + MNT_UPDATE; + /// Check vnode use counts. + #[cfg(target_os = "freebsd")] + MNT_NONBUSY; + } +); + + +/// The Error type of [`Nmount::nmount`]. +/// +/// It wraps an [`Errno`], but also may contain an additional message returned +/// by `nmount(2)`. +#[derive(Debug)] +pub struct NmountError { + errno: Error, + errmsg: Option +} + +impl NmountError { + /// Returns the additional error string sometimes generated by `nmount(2)`. + pub fn errmsg(&self) -> Option<&str> { + self.errmsg.as_deref() + } + + /// Returns the inner [`Error`] + pub const fn error(&self) -> Error { + self.errno + } + + fn new(error: Error, errmsg: Option<&CStr>) -> Self { + Self { + errno: error, + errmsg: errmsg.map(CStr::to_string_lossy).map(Cow::into_owned) + } + } +} + +impl std::error::Error for NmountError {} + +impl fmt::Display for NmountError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(errmsg) = &self.errmsg { + write!(f, "{:?}: {}: {}", self.errno, errmsg, self.errno.desc()) + } else { + write!(f, "{:?}: {}", self.errno, self.errno.desc()) + } + } +} + +impl From for io::Error { + fn from(err: NmountError) -> Self { + err.errno.into() + } +} + +/// Result type of [`Nmount::nmount`]. +pub type NmountResult = std::result::Result<(), NmountError>; + +/// Mount a FreeBSD file system. +/// +/// The `nmount(2)` system call works similarly to the `mount(8)` program; it +/// takes its options as a series of name-value pairs. Most of the values are +/// strings, as are all of the names. The `Nmount` structure builds up an +/// argument list and then executes the syscall. +/// +/// # Examples +/// +/// To mount `target` onto `mountpoint` with `nullfs`: +/// ``` +/// # use nix::unistd::Uid; +/// # use ::sysctl::CtlValue; +/// # if !Uid::current().is_root() && CtlValue::Int(0) == ::sysctl::value("vfs.usermount").unwrap() { +/// # return; +/// # }; +/// use nix::mount::{MntFlags, Nmount, unmount}; +/// use std::ffi::CString; +/// use tempfile::tempdir; +/// +/// let mountpoint = tempdir().unwrap(); +/// let target = tempdir().unwrap(); +/// +/// let fstype = CString::new("fstype").unwrap(); +/// let nullfs = CString::new("nullfs").unwrap(); +/// Nmount::new() +/// .str_opt(&fstype, &nullfs) +/// .str_opt_owned("fspath", mountpoint.path().to_str().unwrap()) +/// .str_opt_owned("target", target.path().to_str().unwrap()) +/// .nmount(MntFlags::empty()).unwrap(); +/// +/// unmount(mountpoint.path(), MntFlags::empty()).unwrap(); +/// ``` +/// +/// # See Also +/// * [`nmount(2)`](https://www.freebsd.org/cgi/man.cgi?query=nmount) +/// * [`nullfs(5)`](https://www.freebsd.org/cgi/man.cgi?query=nullfs) +#[cfg(target_os = "freebsd")] +#[derive(Debug, Default)] +pub struct Nmount<'a>{ + iov: Vec>, + is_owned: Vec, +} + +#[cfg(target_os = "freebsd")] +impl<'a> Nmount<'a> { + /// Add an opaque mount option. + /// + /// Some file systems take binary-valued mount options. They can be set + /// with this method. + /// + /// # Safety + /// + /// Unsafe because it will cause `Nmount::nmount` to dereference a raw + /// pointer. The user is responsible for ensuring that `val` is valid and + /// its lifetime outlives `self`! An easy way to do that is to give the + /// value a larger scope than `name` + /// + /// # Examples + /// ``` + /// use libc::c_void; + /// use nix::mount::Nmount; + /// use std::ffi::CString; + /// use std::mem; + /// + /// // Note that flags outlives name + /// let mut flags: u32 = 0xdeadbeef; + /// let name = CString::new("flags").unwrap(); + /// let p = &mut flags as *mut u32 as *mut c_void; + /// let len = mem::size_of_val(&flags); + /// let mut nmount = Nmount::new(); + /// unsafe { nmount.mut_ptr_opt(&name, p, len) }; + /// ``` + pub unsafe fn mut_ptr_opt( + &mut self, + name: &'a CStr, + val: *mut c_void, + len: usize + ) -> &mut Self + { + self.iov.push(IoVec::from_slice(name.to_bytes_with_nul())); + self.is_owned.push(false); + self.iov.push(IoVec::from_raw_parts(val, len)); + self.is_owned.push(false); + self + } + + /// Add a mount option that does not take a value. + /// + /// # Examples + /// ``` + /// use nix::mount::Nmount; + /// use std::ffi::CString; + /// + /// let read_only = CString::new("ro").unwrap(); + /// Nmount::new() + /// .null_opt(&read_only); + /// ``` + pub fn null_opt(&mut self, name: &'a CStr) -> &mut Self { + self.iov.push(IoVec::from_slice(name.to_bytes_with_nul())); + self.is_owned.push(false); + self.iov.push(IoVec::from_raw_parts(ptr::null_mut(), 0)); + self.is_owned.push(false); + self + } + + /// Add a mount option that does not take a value, but whose name must be + /// owned. + /// + /// + /// This has higher runtime cost than [`Nmount::null_opt`], but is useful + /// when the name's lifetime doesn't outlive the `Nmount`, or it's a + /// different string type than `CStr`. + /// + /// # Examples + /// ``` + /// use nix::mount::Nmount; + /// + /// let read_only = "ro"; + /// let mut nmount: Nmount<'static> = Nmount::new(); + /// nmount.null_opt_owned(read_only); + /// ``` + pub fn null_opt_owned(&mut self, name: &P) -> &mut Self + { + name.with_nix_path(|s| { + let len = s.to_bytes_with_nul().len(); + self.iov.push(IoVec::from_raw_parts( + // Must free it later + s.to_owned().into_raw() as *mut c_void, + len + )); + self.is_owned.push(true); + }).unwrap(); + self.iov.push(IoVec::from_raw_parts(ptr::null_mut(), 0)); + self.is_owned.push(false); + self + } + + /// Add a mount option as a [`CStr`]. + /// + /// # Examples + /// ``` + /// use nix::mount::Nmount; + /// use std::ffi::CString; + /// + /// let fstype = CString::new("fstype").unwrap(); + /// let nullfs = CString::new("nullfs").unwrap(); + /// Nmount::new() + /// .str_opt(&fstype, &nullfs); + /// ``` + pub fn str_opt( + &mut self, + name: &'a CStr, + val: &'a CStr + ) -> &mut Self + { + self.iov.push(IoVec::from_slice(name.to_bytes_with_nul())); + self.is_owned.push(false); + self.iov.push(IoVec::from_slice(val.to_bytes_with_nul())); + self.is_owned.push(false); + self + } + + /// Add a mount option as an owned string. + /// + /// This has higher runtime cost than [`Nmount::str_opt`], but is useful + /// when the value's lifetime doesn't outlive the `Nmount`, or it's a + /// different string type than `CStr`. + /// + /// # Examples + /// ``` + /// use nix::mount::Nmount; + /// use std::path::Path; + /// + /// let mountpoint = Path::new("/mnt"); + /// Nmount::new() + /// .str_opt_owned("fspath", mountpoint.to_str().unwrap()); + /// ``` + pub fn str_opt_owned(&mut self, name: &P1, val: &P2) -> &mut Self + where P1: ?Sized + NixPath, + P2: ?Sized + NixPath + { + name.with_nix_path(|s| { + let len = s.to_bytes_with_nul().len(); + self.iov.push(IoVec::from_raw_parts( + // Must free it later + s.to_owned().into_raw() as *mut c_void, + len + )); + self.is_owned.push(true); + }).unwrap(); + val.with_nix_path(|s| { + let len = s.to_bytes_with_nul().len(); + self.iov.push(IoVec::from_raw_parts( + // Must free it later + s.to_owned().into_raw() as *mut c_void, + len + )); + self.is_owned.push(true); + }).unwrap(); + self + } + + /// Create a new `Nmount` struct with no options + pub fn new() -> Self { + Self::default() + } + + /// Actually mount the file system. + pub fn nmount(&mut self, flags: MntFlags) -> NmountResult { + // nmount can return extra error information via a "errmsg" return + // argument. + const ERRMSG_NAME: &[u8] = b"errmsg\0"; + let mut errmsg = vec![0u8; 255]; + self.iov.push(IoVec::from_raw_parts( + ERRMSG_NAME.as_ptr() as *mut c_void, + ERRMSG_NAME.len() + )); + self.iov.push(IoVec::from_raw_parts( + errmsg.as_mut_ptr() as *mut c_void, + errmsg.len() + )); + + let niov = self.iov.len() as c_uint; + let iovp = self.iov.as_mut_ptr() as *mut libc::iovec; + let res = unsafe { + libc::nmount(iovp, niov, flags.bits) + }; + match Errno::result(res) { + Ok(_) => Ok(()), + Err(error) => { + let errmsg = match errmsg.iter().position(|&x| x == 0) { + None => None, + Some(0) => None, + Some(n) => { + let sl = &errmsg[0..n + 1]; + Some(CStr::from_bytes_with_nul(sl).unwrap()) + } + }; + Err(NmountError::new(error, errmsg)) + } + } + } +} + +#[cfg(target_os = "freebsd")] +impl<'a> Drop for Nmount<'a> { + fn drop(&mut self) { + for (iov, is_owned) in self.iov.iter().zip(self.is_owned.iter()) { + if *is_owned { + // Free the owned string. Safe because we recorded ownership, + // and Nmount does not implement Clone. + unsafe { + drop(CString::from_raw(iov.0.iov_base as *mut c_char)); + } + } + } + } +} + +/// Unmount the file system mounted at `mountpoint`. +/// +/// Useful flags include +/// * `MNT_FORCE` - Unmount even if still in use. +/// * `MNT_BYFSID` - `mountpoint` is not a path, but a file system ID +/// encoded as `FSID:val0:val1`, where `val0` and `val1` +/// are the contents of the `fsid_t val[]` array in decimal. +/// The file system that has the specified file system ID +/// will be unmounted. See +/// [`statfs`](crate::sys::statfs::statfs) to determine the +/// `fsid`. +pub fn unmount

(mountpoint: &P, flags: MntFlags) -> Result<()> + where P: ?Sized + NixPath +{ + let res = mountpoint.with_nix_path(|cstr| { + unsafe { libc::unmount(cstr.as_ptr(), flags.bits) } + })?; + + Errno::result(res).map(drop) +} diff --git a/vendor/nix-v0.23.1-patched/src/mount/linux.rs b/vendor/nix-v0.23.1-patched/src/mount/linux.rs new file mode 100644 index 000000000..4cb2fa549 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/mount/linux.rs @@ -0,0 +1,111 @@ +#![allow(missing_docs)] +use libc::{self, c_ulong, c_int}; +use crate::{Result, NixPath}; +use crate::errno::Errno; + +libc_bitflags!( + pub struct MsFlags: c_ulong { + /// Mount read-only + MS_RDONLY; + /// Ignore suid and sgid bits + MS_NOSUID; + /// Disallow access to device special files + MS_NODEV; + /// Disallow program execution + MS_NOEXEC; + /// Writes are synced at once + MS_SYNCHRONOUS; + /// Alter flags of a mounted FS + MS_REMOUNT; + /// Allow mandatory locks on a FS + MS_MANDLOCK; + /// Directory modifications are synchronous + MS_DIRSYNC; + /// Do not update access times + MS_NOATIME; + /// Do not update directory access times + MS_NODIRATIME; + /// Linux 2.4.0 - Bind directory at different place + MS_BIND; + MS_MOVE; + MS_REC; + MS_SILENT; + MS_POSIXACL; + MS_UNBINDABLE; + MS_PRIVATE; + MS_SLAVE; + MS_SHARED; + MS_RELATIME; + MS_KERNMOUNT; + MS_I_VERSION; + MS_STRICTATIME; + MS_LAZYTIME; + MS_ACTIVE; + MS_NOUSER; + MS_RMT_MASK; + MS_MGC_VAL; + MS_MGC_MSK; + } +); + +libc_bitflags!( + pub struct MntFlags: c_int { + MNT_FORCE; + MNT_DETACH; + MNT_EXPIRE; + } +); + +pub fn mount( + source: Option<&P1>, + target: &P2, + fstype: Option<&P3>, + flags: MsFlags, + data: Option<&P4>) -> Result<()> { + + fn with_opt_nix_path(p: Option<&P>, f: F) -> Result + where P: ?Sized + NixPath, + F: FnOnce(*const libc::c_char) -> T + { + match p { + Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())), + None => Ok(f(std::ptr::null())) + } + } + + let res = with_opt_nix_path(source, |s| { + target.with_nix_path(|t| { + with_opt_nix_path(fstype, |ty| { + with_opt_nix_path(data, |d| { + unsafe { + libc::mount( + s, + t.as_ptr(), + ty, + flags.bits, + d as *const libc::c_void + ) + } + }) + }) + }) + })????; + + Errno::result(res).map(drop) +} + +pub fn umount(target: &P) -> Result<()> { + let res = target.with_nix_path(|cstr| { + unsafe { libc::umount(cstr.as_ptr()) } + })?; + + Errno::result(res).map(drop) +} + +pub fn umount2(target: &P, flags: MntFlags) -> Result<()> { + let res = target.with_nix_path(|cstr| { + unsafe { libc::umount2(cstr.as_ptr(), flags.bits) } + })?; + + Errno::result(res).map(drop) +} diff --git a/vendor/nix-v0.23.1-patched/src/mount/mod.rs b/vendor/nix-v0.23.1-patched/src/mount/mod.rs new file mode 100644 index 000000000..14bf2a963 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/mount/mod.rs @@ -0,0 +1,21 @@ +//! Mount file systems +#[cfg(any(target_os = "android", target_os = "linux"))] +mod linux; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::linux::*; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +mod bsd; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] +pub use self::bsd::*; diff --git a/vendor/nix-v0.23.1-patched/src/mqueue.rs b/vendor/nix-v0.23.1-patched/src/mqueue.rs new file mode 100644 index 000000000..34fd80278 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/mqueue.rs @@ -0,0 +1,178 @@ +//! Posix Message Queue functions +//! +//! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html) + +use crate::Result; +use crate::errno::Errno; + +use libc::{self, c_char, mqd_t, size_t}; +use std::ffi::CString; +use crate::sys::stat::Mode; +use std::mem; + +libc_bitflags!{ + pub struct MQ_OFlag: libc::c_int { + O_RDONLY; + O_WRONLY; + O_RDWR; + O_CREAT; + O_EXCL; + O_NONBLOCK; + O_CLOEXEC; + } +} + +libc_bitflags!{ + pub struct FdFlag: libc::c_int { + FD_CLOEXEC; + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct MqAttr { + mq_attr: libc::mq_attr, +} + +// x32 compatibility +// See https://sourceware.org/bugzilla/show_bug.cgi?id=21279 +#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] +pub type mq_attr_member_t = i64; +#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] +pub type mq_attr_member_t = libc::c_long; + +impl MqAttr { + pub fn new(mq_flags: mq_attr_member_t, + mq_maxmsg: mq_attr_member_t, + mq_msgsize: mq_attr_member_t, + mq_curmsgs: mq_attr_member_t) + -> MqAttr + { + let mut attr = mem::MaybeUninit::::uninit(); + unsafe { + let p = attr.as_mut_ptr(); + (*p).mq_flags = mq_flags; + (*p).mq_maxmsg = mq_maxmsg; + (*p).mq_msgsize = mq_msgsize; + (*p).mq_curmsgs = mq_curmsgs; + MqAttr { mq_attr: attr.assume_init() } + } + } + + pub const fn flags(&self) -> mq_attr_member_t { + self.mq_attr.mq_flags + } +} + + +/// Open a message queue +/// +/// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html) +// The mode.bits cast is only lossless on some OSes +#[allow(clippy::cast_lossless)] +pub fn mq_open(name: &CString, + oflag: MQ_OFlag, + mode: Mode, + attr: Option<&MqAttr>) + -> Result { + let res = match attr { + Some(mq_attr) => unsafe { + libc::mq_open(name.as_ptr(), + oflag.bits(), + mode.bits() as libc::c_int, + &mq_attr.mq_attr as *const libc::mq_attr) + }, + None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) }, + }; + Errno::result(res) +} + +/// Remove a message queue +/// +/// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html) +pub fn mq_unlink(name: &CString) -> Result<()> { + let res = unsafe { libc::mq_unlink(name.as_ptr()) }; + Errno::result(res).map(drop) +} + +/// Close a message queue +/// +/// See also [`mq_close(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html) +pub fn mq_close(mqdes: mqd_t) -> Result<()> { + let res = unsafe { libc::mq_close(mqdes) }; + Errno::result(res).map(drop) +} + +/// Receive a message from a message queue +/// +/// See also [`mq_receive(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html) +pub fn mq_receive(mqdes: mqd_t, message: &mut [u8], msg_prio: &mut u32) -> Result { + let len = message.len() as size_t; + let res = unsafe { + libc::mq_receive(mqdes, + message.as_mut_ptr() as *mut c_char, + len, + msg_prio as *mut u32) + }; + Errno::result(res).map(|r| r as usize) +} + +/// Send a message to a message queue +/// +/// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html) +pub fn mq_send(mqdes: mqd_t, message: &[u8], msq_prio: u32) -> Result<()> { + let res = unsafe { + libc::mq_send(mqdes, + message.as_ptr() as *const c_char, + message.len(), + msq_prio) + }; + Errno::result(res).map(drop) +} + +/// Get message queue attributes +/// +/// See also [`mq_getattr(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html) +pub fn mq_getattr(mqd: mqd_t) -> Result { + let mut attr = mem::MaybeUninit::::uninit(); + let res = unsafe { libc::mq_getattr(mqd, attr.as_mut_ptr()) }; + Errno::result(res).map(|_| unsafe{MqAttr { mq_attr: attr.assume_init() }}) +} + +/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored +/// Returns the old attributes +/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html) +pub fn mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result { + let mut attr = mem::MaybeUninit::::uninit(); + let res = unsafe { + libc::mq_setattr(mqd, &newattr.mq_attr as *const libc::mq_attr, attr.as_mut_ptr()) + }; + Errno::result(res).map(|_| unsafe{ MqAttr { mq_attr: attr.assume_init() }}) +} + +/// Convenience function. +/// Sets the `O_NONBLOCK` attribute for a given message queue descriptor +/// Returns the old attributes +#[allow(clippy::useless_conversion)] // Not useless on all OSes +pub fn mq_set_nonblock(mqd: mqd_t) -> Result { + let oldattr = mq_getattr(mqd)?; + let newattr = MqAttr::new(mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()), + oldattr.mq_attr.mq_maxmsg, + oldattr.mq_attr.mq_msgsize, + oldattr.mq_attr.mq_curmsgs); + mq_setattr(mqd, &newattr) +} + +/// Convenience function. +/// Removes `O_NONBLOCK` attribute for a given message queue descriptor +/// Returns the old attributes +pub fn mq_remove_nonblock(mqd: mqd_t) -> Result { + let oldattr = mq_getattr(mqd)?; + let newattr = MqAttr::new(0, + oldattr.mq_attr.mq_maxmsg, + oldattr.mq_attr.mq_msgsize, + oldattr.mq_attr.mq_curmsgs); + mq_setattr(mqd, &newattr) +} diff --git a/vendor/nix-v0.23.1-patched/src/net/if_.rs b/vendor/nix-v0.23.1-patched/src/net/if_.rs new file mode 100644 index 000000000..bc00a4328 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/net/if_.rs @@ -0,0 +1,411 @@ +//! Network interface name resolution. +//! +//! Uses Linux and/or POSIX functions to resolve interface names like "eth0" +//! or "socan1" into device numbers. + +use crate::{Error, NixPath, Result}; +use libc::c_uint; + +/// Resolve an interface into a interface number. +pub fn if_nametoindex(name: &P) -> Result { + let if_index = name.with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?; + + if if_index == 0 { + Err(Error::last()) + } else { + Ok(if_index) + } +} + +libc_bitflags!( + /// Standard interface flags, used by `getifaddrs` + pub struct InterfaceFlags: libc::c_int { + /// Interface is running. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_UP; + /// Valid broadcast address set. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_BROADCAST; + /// Internal debugging flag. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_DEBUG; + /// Interface is a loopback interface. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_LOOPBACK; + /// Interface is a point-to-point link. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_POINTOPOINT; + /// Avoid use of trailers. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", + target_os = "fuchsia", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "illumos", + target_os = "solaris"))] + IFF_NOTRAILERS; + /// Interface manages own routes. + #[cfg(any(target_os = "dragonfly"))] + IFF_SMART; + /// Resources allocated. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris"))] + IFF_RUNNING; + /// No arp protocol, L2 destination address not set. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_NOARP; + /// Interface is in promiscuous mode. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_PROMISC; + /// Receive all multicast packets. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_ALLMULTI; + /// Master of a load balancing bundle. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_MASTER; + /// transmission in progress, tx hardware queue is full + #[cfg(any(target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "ios"))] + IFF_OACTIVE; + /// Protocol code on board. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_INTELLIGENT; + /// Slave of a load balancing bundle. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_SLAVE; + /// Can't hear own transmissions. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "osx"))] + IFF_SIMPLEX; + /// Supports multicast. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_MULTICAST; + /// Per link layer defined bit. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "ios"))] + IFF_LINK0; + /// Multicast using broadcast. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_MULTI_BCAST; + /// Is able to select media type via ifmap. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_PORTSEL; + /// Per link layer defined bit. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "ios"))] + IFF_LINK1; + /// Non-unique address. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_UNNUMBERED; + /// Auto media selection active. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_AUTOMEDIA; + /// Per link layer defined bit. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "ios"))] + IFF_LINK2; + /// Use alternate physical connection. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "ios"))] + IFF_ALTPHYS; + /// DHCP controls interface. + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + IFF_DHCPRUNNING; + /// The addresses are lost when the interface goes down. (see + /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_DYNAMIC; + /// Do not advertise. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_PRIVATE; + /// Driver signals L1 up. Volatile. + #[cfg(any(target_os = "fuchsia", target_os = "linux"))] + IFF_LOWER_UP; + /// Interface is in polling mode. + #[cfg(any(target_os = "dragonfly"))] + IFF_POLLING_COMPAT; + /// Unconfigurable using ioctl(2). + #[cfg(any(target_os = "freebsd"))] + IFF_CANTCONFIG; + /// Do not transmit packets. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_NOXMIT; + /// Driver signals dormant. Volatile. + #[cfg(any(target_os = "fuchsia", target_os = "linux"))] + IFF_DORMANT; + /// User-requested promisc mode. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + IFF_PPROMISC; + /// Just on-link subnet. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_NOLOCAL; + /// Echo sent packets. Volatile. + #[cfg(any(target_os = "fuchsia", target_os = "linux"))] + IFF_ECHO; + /// User-requested monitor mode. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + IFF_MONITOR; + /// Address is deprecated. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_DEPRECATED; + /// Static ARP. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + IFF_STATICARP; + /// Address from stateless addrconf. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_ADDRCONF; + /// Interface is in polling mode. + #[cfg(any(target_os = "dragonfly"))] + IFF_NPOLLING; + /// Router on interface. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_ROUTER; + /// Interface is in polling mode. + #[cfg(any(target_os = "dragonfly"))] + IFF_IDIRECT; + /// Interface is winding down + #[cfg(any(target_os = "freebsd"))] + IFF_DYING; + /// No NUD on interface. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_NONUD; + /// Interface is being renamed + #[cfg(any(target_os = "freebsd"))] + IFF_RENAMING; + /// Anycast address. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_ANYCAST; + /// Don't exchange routing info. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_NORTEXCH; + /// Do not provide packet information + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_NO_PI as libc::c_int; + /// TUN device (no Ethernet headers) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_TUN as libc::c_int; + /// TAP device + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_TAP as libc::c_int; + /// IPv4 interface. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_IPV4; + /// IPv6 interface. + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_IPV6; + /// in.mpathd test address + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_NOFAILOVER; + /// Interface has failed + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_FAILED; + /// Interface is a hot-spare + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_STANDBY; + /// Functioning but not used + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_INACTIVE; + /// Interface is offline + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + IFF_OFFLINE; + #[cfg(target_os = "solaris")] + IFF_COS_ENABLED; + /// Prefer as source addr. + #[cfg(target_os = "solaris")] + IFF_PREFERRED; + /// RFC3041 + #[cfg(target_os = "solaris")] + IFF_TEMPORARY; + /// MTU set with SIOCSLIFMTU + #[cfg(target_os = "solaris")] + IFF_FIXEDMTU; + /// Cannot send / receive packets + #[cfg(target_os = "solaris")] + IFF_VIRTUAL; + /// Local address in use + #[cfg(target_os = "solaris")] + IFF_DUPLICATE; + /// IPMP IP interface + #[cfg(target_os = "solaris")] + IFF_IPMP; + } +); + +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +))] +mod if_nameindex { + use super::*; + + use std::ffi::CStr; + use std::fmt; + use std::marker::PhantomData; + use std::ptr::NonNull; + + /// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index + /// (1, 2, 3, etc) that identifies it in the OS's networking stack. + #[allow(missing_copy_implementations)] + #[repr(transparent)] + pub struct Interface(libc::if_nameindex); + + impl Interface { + /// Obtain the index of this interface. + pub fn index(&self) -> c_uint { + self.0.if_index + } + + /// Obtain the name of this interface. + pub fn name(&self) -> &CStr { + unsafe { CStr::from_ptr(self.0.if_name) } + } + } + + impl fmt::Debug for Interface { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Interface") + .field("index", &self.index()) + .field("name", &self.name()) + .finish() + } + } + + /// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`]. + pub struct Interfaces { + ptr: NonNull, + } + + impl Interfaces { + /// Iterate over the interfaces in this list. + #[inline] + pub fn iter(&self) -> InterfacesIter<'_> { + self.into_iter() + } + + /// Convert this to a slice of interfaces. Note that the underlying interfaces list is + /// null-terminated, so calling this calculates the length. If random access isn't needed, + /// [`Interfaces::iter()`] should be used instead. + pub fn to_slice(&self) -> &[Interface] { + let ifs = self.ptr.as_ptr() as *const Interface; + let len = self.iter().count(); + unsafe { std::slice::from_raw_parts(ifs, len) } + } + } + + impl Drop for Interfaces { + fn drop(&mut self) { + unsafe { libc::if_freenameindex(self.ptr.as_ptr()) }; + } + } + + impl fmt::Debug for Interfaces { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.to_slice().fmt(f) + } + } + + impl<'a> IntoIterator for &'a Interfaces { + type IntoIter = InterfacesIter<'a>; + type Item = &'a Interface; + #[inline] + fn into_iter(self) -> Self::IntoIter { + InterfacesIter { + ptr: self.ptr.as_ptr(), + _marker: PhantomData, + } + } + } + + /// An iterator over the interfaces in an [`Interfaces`]. + #[derive(Debug)] + pub struct InterfacesIter<'a> { + ptr: *const libc::if_nameindex, + _marker: PhantomData<&'a Interfaces>, + } + + impl<'a> Iterator for InterfacesIter<'a> { + type Item = &'a Interface; + #[inline] + fn next(&mut self) -> Option { + unsafe { + if (*self.ptr).if_index == 0 { + None + } else { + let ret = &*(self.ptr as *const Interface); + self.ptr = self.ptr.add(1); + Some(ret) + } + } + } + } + + /// Retrieve a list of the network interfaces available on the local system. + /// + /// ``` + /// let interfaces = nix::net::if_::if_nameindex().unwrap(); + /// for iface in &interfaces { + /// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy()); + /// } + /// ``` + pub fn if_nameindex() -> Result { + unsafe { + let ifs = libc::if_nameindex(); + let ptr = NonNull::new(ifs).ok_or_else(Error::last)?; + Ok(Interfaces { ptr }) + } + } +} +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +))] +pub use if_nameindex::*; diff --git a/vendor/nix-v0.23.1-patched/src/net/mod.rs b/vendor/nix-v0.23.1-patched/src/net/mod.rs new file mode 100644 index 000000000..079fcfde6 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/net/mod.rs @@ -0,0 +1,4 @@ +//! Functionality involving network interfaces +// To avoid clashing with the keyword "if", we use "if_" as the module name. +// The original header is called "net/if.h". +pub mod if_; diff --git a/vendor/nix-v0.23.1-patched/src/poll.rs b/vendor/nix-v0.23.1-patched/src/poll.rs new file mode 100644 index 000000000..8556c1bb7 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/poll.rs @@ -0,0 +1,163 @@ +//! Wait for events to trigger on specific file descriptors +#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))] +use crate::sys::time::TimeSpec; +#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))] +use crate::sys::signal::SigSet; +use std::os::unix::io::{AsRawFd, RawFd}; + +use crate::Result; +use crate::errno::Errno; + +/// This is a wrapper around `libc::pollfd`. +/// +/// It's meant to be used as an argument to the [`poll`](fn.poll.html) and +/// [`ppoll`](fn.ppoll.html) functions to specify the events of interest +/// for a specific file descriptor. +/// +/// After a call to `poll` or `ppoll`, the events that occured can be +/// retrieved by calling [`revents()`](#method.revents) on the `PollFd`. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct PollFd { + pollfd: libc::pollfd, +} + +impl PollFd { + /// Creates a new `PollFd` specifying the events of interest + /// for a given file descriptor. + pub const fn new(fd: RawFd, events: PollFlags) -> PollFd { + PollFd { + pollfd: libc::pollfd { + fd, + events: events.bits(), + revents: PollFlags::empty().bits(), + }, + } + } + + /// Returns the events that occured in the last call to `poll` or `ppoll`. Will only return + /// `None` if the kernel provides status flags that Nix does not know about. + pub fn revents(self) -> Option { + PollFlags::from_bits(self.pollfd.revents) + } + + /// The events of interest for this `PollFd`. + pub fn events(self) -> PollFlags { + PollFlags::from_bits(self.pollfd.events).unwrap() + } + + /// Modify the events of interest for this `PollFd`. + pub fn set_events(&mut self, events: PollFlags) { + self.pollfd.events = events.bits(); + } +} + +impl AsRawFd for PollFd { + fn as_raw_fd(&self) -> RawFd { + self.pollfd.fd + } +} + +libc_bitflags! { + /// These flags define the different events that can be monitored by `poll` and `ppoll` + pub struct PollFlags: libc::c_short { + /// There is data to read. + POLLIN; + /// There is some exceptional condition on the file descriptor. + /// + /// Possibilities include: + /// + /// * There is out-of-band data on a TCP socket (see + /// [tcp(7)](https://man7.org/linux/man-pages/man7/tcp.7.html)). + /// * A pseudoterminal master in packet mode has seen a state + /// change on the slave (see + /// [ioctl_tty(2)](https://man7.org/linux/man-pages/man2/ioctl_tty.2.html)). + /// * A cgroup.events file has been modified (see + /// [cgroups(7)](https://man7.org/linux/man-pages/man7/cgroups.7.html)). + POLLPRI; + /// Writing is now possible, though a write larger that the + /// available space in a socket or pipe will still block (unless + /// `O_NONBLOCK` is set). + POLLOUT; + /// Equivalent to [`POLLIN`](constant.POLLIN.html) + #[cfg(not(target_os = "redox"))] + POLLRDNORM; + #[cfg(not(target_os = "redox"))] + /// Equivalent to [`POLLOUT`](constant.POLLOUT.html) + POLLWRNORM; + /// Priority band data can be read (generally unused on Linux). + #[cfg(not(target_os = "redox"))] + POLLRDBAND; + /// Priority data may be written. + #[cfg(not(target_os = "redox"))] + POLLWRBAND; + /// Error condition (only returned in + /// [`PollFd::revents`](struct.PollFd.html#method.revents); + /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)). + /// This bit is also set for a file descriptor referring to the + /// write end of a pipe when the read end has been closed. + POLLERR; + /// Hang up (only returned in [`PollFd::revents`](struct.PollFd.html#method.revents); + /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)). + /// Note that when reading from a channel such as a pipe or a stream + /// socket, this event merely indicates that the peer closed its + /// end of the channel. Subsequent reads from the channel will + /// return 0 (end of file) only after all outstanding data in the + /// channel has been consumed. + POLLHUP; + /// Invalid request: `fd` not open (only returned in + /// [`PollFd::revents`](struct.PollFd.html#method.revents); + /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)). + POLLNVAL; + } +} + +/// `poll` waits for one of a set of file descriptors to become ready to perform I/O. +/// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html)) +/// +/// `fds` contains all [`PollFd`](struct.PollFd.html) to poll. +/// The function will return as soon as any event occur for any of these `PollFd`s. +/// +/// The `timeout` argument specifies the number of milliseconds that `poll()` +/// should block waiting for a file descriptor to become ready. The call +/// will block until either: +/// +/// * a file descriptor becomes ready; +/// * the call is interrupted by a signal handler; or +/// * the timeout expires. +/// +/// Note that the timeout interval will be rounded up to the system clock +/// granularity, and kernel scheduling delays mean that the blocking +/// interval may overrun by a small amount. Specifying a negative value +/// in timeout means an infinite timeout. Specifying a timeout of zero +/// causes `poll()` to return immediately, even if no file descriptors are +/// ready. +pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result { + let res = unsafe { + libc::poll(fds.as_mut_ptr() as *mut libc::pollfd, + fds.len() as libc::nfds_t, + timeout) + }; + + Errno::result(res) +} + +/// `ppoll()` allows an application to safely wait until either a file +/// descriptor becomes ready or until a signal is caught. +/// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html)) +/// +/// `ppoll` behaves like `poll`, but let you specify what signals may interrupt it +/// with the `sigmask` argument. If you want `ppoll` to block indefinitely, +/// specify `None` as `timeout` (it is like `timeout = -1` for `poll`). +/// +#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))] +pub fn ppoll(fds: &mut [PollFd], timeout: Option, sigmask: SigSet) -> Result { + let timeout = timeout.as_ref().map_or(core::ptr::null(), |r| r.as_ref()); + let res = unsafe { + libc::ppoll(fds.as_mut_ptr() as *mut libc::pollfd, + fds.len() as libc::nfds_t, + timeout, + sigmask.as_ref()) + }; + Errno::result(res) +} diff --git a/vendor/nix-v0.23.1-patched/src/pty.rs b/vendor/nix-v0.23.1-patched/src/pty.rs new file mode 100644 index 000000000..facc9aaf4 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/pty.rs @@ -0,0 +1,348 @@ +//! Create master and slave virtual pseudo-terminals (PTYs) + +pub use libc::pid_t as SessionId; +pub use libc::winsize as Winsize; + +use std::ffi::CStr; +use std::io; +use std::mem; +use std::os::unix::prelude::*; + +use crate::sys::termios::Termios; +use crate::unistd::{self, ForkResult, Pid}; +use crate::{Result, fcntl}; +use crate::errno::Errno; + +/// Representation of a master/slave pty pair +/// +/// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user +/// must manually close the file descriptors. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct OpenptyResult { + /// The master port in a virtual pty pair + pub master: RawFd, + /// The slave port in a virtual pty pair + pub slave: RawFd, +} + +/// Representation of a master with a forked pty +/// +/// This is returned by `forkpty`. Note that this type does *not* implement `Drop`, so the user +/// must manually close the file descriptors. +#[derive(Clone, Copy, Debug)] +pub struct ForkptyResult { + /// The master port in a virtual pty pair + pub master: RawFd, + /// Metadata about forked process + pub fork_result: ForkResult, +} + + +/// Representation of the Master device in a master/slave pty pair +/// +/// While this datatype is a thin wrapper around `RawFd`, it enforces that the available PTY +/// functions are given the correct file descriptor. Additionally this type implements `Drop`, +/// so that when it's consumed or goes out of scope, it's automatically cleaned-up. +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct PtyMaster(RawFd); + +impl AsRawFd for PtyMaster { + fn as_raw_fd(&self) -> RawFd { + self.0 + } +} + +impl IntoRawFd for PtyMaster { + fn into_raw_fd(self) -> RawFd { + let fd = self.0; + mem::forget(self); + fd + } +} + +impl Drop for PtyMaster { + fn drop(&mut self) { + // On drop, we ignore errors like EINTR and EIO because there's no clear + // way to handle them, we can't return anything, and (on FreeBSD at + // least) the file descriptor is deallocated in these cases. However, + // we must panic on EBADF, because it is always an error to close an + // invalid file descriptor. That frequently indicates a double-close + // condition, which can cause confusing errors for future I/O + // operations. + let e = unistd::close(self.0); + if e == Err(Errno::EBADF) { + panic!("Closing an invalid file descriptor!"); + }; + } +} + +impl io::Read for PtyMaster { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + unistd::read(self.0, buf).map_err(io::Error::from) + } +} + +impl io::Write for PtyMaster { + fn write(&mut self, buf: &[u8]) -> io::Result { + unistd::write(self.0, buf).map_err(io::Error::from) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// Grant access to a slave pseudoterminal (see +/// [`grantpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html)) +/// +/// `grantpt()` changes the mode and owner of the slave pseudoterminal device corresponding to the +/// master pseudoterminal referred to by `fd`. This is a necessary step towards opening the slave. +#[inline] +pub fn grantpt(fd: &PtyMaster) -> Result<()> { + if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 { + return Err(Errno::last()); + } + + Ok(()) +} + +/// Open a pseudoterminal device (see +/// [`posix_openpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html)) +/// +/// `posix_openpt()` returns a file descriptor to an existing unused pseuterminal master device. +/// +/// # Examples +/// +/// A common use case with this function is to open both a master and slave PTY pair. This can be +/// done as follows: +/// +/// ``` +/// use std::path::Path; +/// use nix::fcntl::{OFlag, open}; +/// use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt}; +/// use nix::sys::stat::Mode; +/// +/// # #[allow(dead_code)] +/// # fn run() -> nix::Result<()> { +/// // Open a new PTY master +/// let master_fd = posix_openpt(OFlag::O_RDWR)?; +/// +/// // Allow a slave to be generated for it +/// grantpt(&master_fd)?; +/// unlockpt(&master_fd)?; +/// +/// // Get the name of the slave +/// let slave_name = unsafe { ptsname(&master_fd) }?; +/// +/// // Try to open the slave +/// let _slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, Mode::empty())?; +/// # Ok(()) +/// # } +/// ``` +#[inline] +pub fn posix_openpt(flags: fcntl::OFlag) -> Result { + let fd = unsafe { + libc::posix_openpt(flags.bits()) + }; + + if fd < 0 { + return Err(Errno::last()); + } + + Ok(PtyMaster(fd)) +} + +/// Get the name of the slave pseudoterminal (see +/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html)) +/// +/// `ptsname()` returns the name of the slave pseudoterminal device corresponding to the master +/// referred to by `fd`. +/// +/// This value is useful for opening the slave pty once the master has already been opened with +/// `posix_openpt()`. +/// +/// # Safety +/// +/// `ptsname()` mutates global variables and is *not* threadsafe. +/// Mutating global variables is always considered `unsafe` by Rust and this +/// function is marked as `unsafe` to reflect that. +/// +/// For a threadsafe and non-`unsafe` alternative on Linux, see `ptsname_r()`. +#[inline] +pub unsafe fn ptsname(fd: &PtyMaster) -> Result { + let name_ptr = libc::ptsname(fd.as_raw_fd()); + if name_ptr.is_null() { + return Err(Errno::last()); + } + + let name = CStr::from_ptr(name_ptr); + Ok(name.to_string_lossy().into_owned()) +} + +/// Get the name of the slave pseudoterminal (see +/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html)) +/// +/// `ptsname_r()` returns the name of the slave pseudoterminal device corresponding to the master +/// referred to by `fd`. This is the threadsafe version of `ptsname()`, but it is not part of the +/// POSIX standard and is instead a Linux-specific extension. +/// +/// This value is useful for opening the slave ptty once the master has already been opened with +/// `posix_openpt()`. +#[cfg(any(target_os = "android", target_os = "linux"))] +#[inline] +pub fn ptsname_r(fd: &PtyMaster) -> Result { + let mut name_buf = Vec::::with_capacity(64); + let name_buf_ptr = name_buf.as_mut_ptr(); + let cname = unsafe { + let cap = name_buf.capacity(); + if libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, cap) != 0 { + return Err(crate::Error::last()); + } + CStr::from_ptr(name_buf.as_ptr()) + }; + + let name = cname.to_string_lossy().into_owned(); + Ok(name) +} + +/// Unlock a pseudoterminal master/slave pseudoterminal pair (see +/// [`unlockpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html)) +/// +/// `unlockpt()` unlocks the slave pseudoterminal device corresponding to the master pseudoterminal +/// referred to by `fd`. This must be called before trying to open the slave side of a +/// pseuoterminal. +#[inline] +pub fn unlockpt(fd: &PtyMaster) -> Result<()> { + if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 { + return Err(Errno::last()); + } + + Ok(()) +} + + +/// Create a new pseudoterminal, returning the slave and master file descriptors +/// in `OpenptyResult` +/// (see [`openpty`](https://man7.org/linux/man-pages/man3/openpty.3.html)). +/// +/// If `winsize` is not `None`, the window size of the slave will be set to +/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's +/// terminal settings of the slave will be set to the values in `termios`. +#[inline] +pub fn openpty<'a, 'b, T: Into>, U: Into>>(winsize: T, termios: U) -> Result { + use std::ptr; + + let mut slave = mem::MaybeUninit::::uninit(); + let mut master = mem::MaybeUninit::::uninit(); + let ret = { + match (termios.into(), winsize.into()) { + (Some(termios), Some(winsize)) => { + let inner_termios = termios.get_libc_termios(); + unsafe { + libc::openpty( + master.as_mut_ptr(), + slave.as_mut_ptr(), + ptr::null_mut(), + &*inner_termios as *const libc::termios as *mut _, + winsize as *const Winsize as *mut _, + ) + } + } + (None, Some(winsize)) => { + unsafe { + libc::openpty( + master.as_mut_ptr(), + slave.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + winsize as *const Winsize as *mut _, + ) + } + } + (Some(termios), None) => { + let inner_termios = termios.get_libc_termios(); + unsafe { + libc::openpty( + master.as_mut_ptr(), + slave.as_mut_ptr(), + ptr::null_mut(), + &*inner_termios as *const libc::termios as *mut _, + ptr::null_mut(), + ) + } + } + (None, None) => { + unsafe { + libc::openpty( + master.as_mut_ptr(), + slave.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ) + } + } + } + }; + + Errno::result(ret)?; + + unsafe { + Ok(OpenptyResult { + master: master.assume_init(), + slave: slave.assume_init(), + }) + } +} + +/// Create a new pseudoterminal, returning the master file descriptor and forked pid. +/// in `ForkptyResult` +/// (see [`forkpty`](https://man7.org/linux/man-pages/man3/forkpty.3.html)). +/// +/// If `winsize` is not `None`, the window size of the slave will be set to +/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's +/// terminal settings of the slave will be set to the values in `termios`. +/// +/// # Safety +/// +/// In a multithreaded program, only [async-signal-safe] functions like `pause` +/// and `_exit` may be called by the child (the parent isn't restricted). Note +/// that memory allocation may **not** be async-signal-safe and thus must be +/// prevented. +/// +/// Those functions are only a small subset of your operating system's API, so +/// special care must be taken to only invoke code you can control and audit. +/// +/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html +pub unsafe fn forkpty<'a, 'b, T: Into>, U: Into>>( + winsize: T, + termios: U, +) -> Result { + use std::ptr; + + let mut master = mem::MaybeUninit::::uninit(); + + let term = match termios.into() { + Some(termios) => { + let inner_termios = termios.get_libc_termios(); + &*inner_termios as *const libc::termios as *mut _ + }, + None => ptr::null_mut(), + }; + + let win = winsize + .into() + .map(|ws| ws as *const Winsize as *mut _) + .unwrap_or(ptr::null_mut()); + + let res = libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win); + + let fork_result = Errno::result(res).map(|res| match res { + 0 => ForkResult::Child, + res => ForkResult::Parent { child: Pid::from_raw(res) }, + })?; + + Ok(ForkptyResult { + master: master.assume_init(), + fork_result, + }) +} diff --git a/vendor/nix-v0.23.1-patched/src/sched.rs b/vendor/nix-v0.23.1-patched/src/sched.rs new file mode 100644 index 000000000..c2dd7b84c --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sched.rs @@ -0,0 +1,282 @@ +//! Execution scheduling +//! +//! See Also +//! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html) +use crate::{Errno, Result}; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::sched_linux_like::*; + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod sched_linux_like { + use crate::errno::Errno; + use libc::{self, c_int, c_void}; + use std::mem; + use std::option::Option; + use std::os::unix::io::RawFd; + use crate::unistd::Pid; + use crate::Result; + + // For some functions taking with a parameter of type CloneFlags, + // only a subset of these flags have an effect. + libc_bitflags! { + /// Options for use with [`clone`] + pub struct CloneFlags: c_int { + /// The calling process and the child process run in the same + /// memory space. + CLONE_VM; + /// The caller and the child process share the same filesystem + /// information. + CLONE_FS; + /// The calling process and the child process share the same file + /// descriptor table. + CLONE_FILES; + /// The calling process and the child process share the same table + /// of signal handlers. + CLONE_SIGHAND; + /// If the calling process is being traced, then trace the child + /// also. + CLONE_PTRACE; + /// The execution of the calling process is suspended until the + /// child releases its virtual memory resources via a call to + /// execve(2) or _exit(2) (as with vfork(2)). + CLONE_VFORK; + /// The parent of the new child (as returned by getppid(2)) + /// will be the same as that of the calling process. + CLONE_PARENT; + /// The child is placed in the same thread group as the calling + /// process. + CLONE_THREAD; + /// The cloned child is started in a new mount namespace. + CLONE_NEWNS; + /// The child and the calling process share a single list of System + /// V semaphore adjustment values + CLONE_SYSVSEM; + // Not supported by Nix due to lack of varargs support in Rust FFI + // CLONE_SETTLS; + // Not supported by Nix due to lack of varargs support in Rust FFI + // CLONE_PARENT_SETTID; + // Not supported by Nix due to lack of varargs support in Rust FFI + // CLONE_CHILD_CLEARTID; + /// Unused since Linux 2.6.2 + #[deprecated(since = "0.23.0", note = "Deprecated by Linux 2.6.2")] + CLONE_DETACHED; + /// A tracing process cannot force `CLONE_PTRACE` on this child + /// process. + CLONE_UNTRACED; + // Not supported by Nix due to lack of varargs support in Rust FFI + // CLONE_CHILD_SETTID; + /// Create the process in a new cgroup namespace. + CLONE_NEWCGROUP; + /// Create the process in a new UTS namespace. + CLONE_NEWUTS; + /// Create the process in a new IPC namespace. + CLONE_NEWIPC; + /// Create the process in a new user namespace. + CLONE_NEWUSER; + /// Create the process in a new PID namespace. + CLONE_NEWPID; + /// Create the process in a new network namespace. + CLONE_NEWNET; + /// The new process shares an I/O context with the calling process. + CLONE_IO; + } + } + + /// Type for the function executed by [`clone`]. + pub type CloneCb<'a> = Box isize + 'a>; + + /// CpuSet represent a bit-mask of CPUs. + /// CpuSets are used by sched_setaffinity and + /// sched_getaffinity for example. + /// + /// This is a wrapper around `libc::cpu_set_t`. + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct CpuSet { + cpu_set: libc::cpu_set_t, + } + + impl CpuSet { + /// Create a new and empty CpuSet. + pub fn new() -> CpuSet { + CpuSet { + cpu_set: unsafe { mem::zeroed() }, + } + } + + /// Test to see if a CPU is in the CpuSet. + /// `field` is the CPU id to test + pub fn is_set(&self, field: usize) -> Result { + if field >= CpuSet::count() { + Err(Errno::EINVAL) + } else { + Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) }) + } + } + + /// Add a CPU to CpuSet. + /// `field` is the CPU id to add + pub fn set(&mut self, field: usize) -> Result<()> { + if field >= CpuSet::count() { + Err(Errno::EINVAL) + } else { + unsafe { libc::CPU_SET(field, &mut self.cpu_set); } + Ok(()) + } + } + + /// Remove a CPU from CpuSet. + /// `field` is the CPU id to remove + pub fn unset(&mut self, field: usize) -> Result<()> { + if field >= CpuSet::count() { + Err(Errno::EINVAL) + } else { + unsafe { libc::CPU_CLR(field, &mut self.cpu_set);} + Ok(()) + } + } + + /// Return the maximum number of CPU in CpuSet + pub const fn count() -> usize { + 8 * mem::size_of::() + } + } + + impl Default for CpuSet { + fn default() -> Self { + Self::new() + } + } + + /// `sched_setaffinity` set a thread's CPU affinity mask + /// ([`sched_setaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html)) + /// + /// `pid` is the thread ID to update. + /// If pid is zero, then the calling thread is updated. + /// + /// The `cpuset` argument specifies the set of CPUs on which the thread + /// will be eligible to run. + /// + /// # Example + /// + /// Binding the current thread to CPU 0 can be done as follows: + /// + /// ```rust,no_run + /// use nix::sched::{CpuSet, sched_setaffinity}; + /// use nix::unistd::Pid; + /// + /// let mut cpu_set = CpuSet::new(); + /// cpu_set.set(0); + /// sched_setaffinity(Pid::from_raw(0), &cpu_set); + /// ``` + pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> { + let res = unsafe { + libc::sched_setaffinity( + pid.into(), + mem::size_of::() as libc::size_t, + &cpuset.cpu_set, + ) + }; + + Errno::result(res).map(drop) + } + + /// `sched_getaffinity` get a thread's CPU affinity mask + /// ([`sched_getaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_getaffinity.2.html)) + /// + /// `pid` is the thread ID to check. + /// If pid is zero, then the calling thread is checked. + /// + /// Returned `cpuset` is the set of CPUs on which the thread + /// is eligible to run. + /// + /// # Example + /// + /// Checking if the current thread can run on CPU 0 can be done as follows: + /// + /// ```rust,no_run + /// use nix::sched::sched_getaffinity; + /// use nix::unistd::Pid; + /// + /// let cpu_set = sched_getaffinity(Pid::from_raw(0)).unwrap(); + /// if cpu_set.is_set(0).unwrap() { + /// println!("Current thread can run on CPU 0"); + /// } + /// ``` + pub fn sched_getaffinity(pid: Pid) -> Result { + let mut cpuset = CpuSet::new(); + let res = unsafe { + libc::sched_getaffinity( + pid.into(), + mem::size_of::() as libc::size_t, + &mut cpuset.cpu_set, + ) + }; + + Errno::result(res).and(Ok(cpuset)) + } + + /// `clone` create a child process + /// ([`clone(2)`](https://man7.org/linux/man-pages/man2/clone.2.html)) + /// + /// `stack` is a reference to an array which will hold the stack of the new + /// process. Unlike when calling `clone(2)` from C, the provided stack + /// address need not be the highest address of the region. Nix will take + /// care of that requirement. The user only needs to provide a reference to + /// a normally allocated buffer. + pub fn clone( + mut cb: CloneCb, + stack: &mut [u8], + flags: CloneFlags, + signal: Option, + ) -> Result { + extern "C" fn callback(data: *mut CloneCb) -> c_int { + let cb: &mut CloneCb = unsafe { &mut *data }; + (*cb)() as c_int + } + + let res = unsafe { + let combined = flags.bits() | signal.unwrap_or(0); + let ptr = stack.as_mut_ptr().add(stack.len()); + let ptr_aligned = ptr.sub(ptr as usize % 16); + libc::clone( + mem::transmute( + callback as extern "C" fn(*mut Box isize>) -> i32, + ), + ptr_aligned as *mut c_void, + combined, + &mut cb as *mut _ as *mut c_void, + ) + }; + + Errno::result(res).map(Pid::from_raw) + } + + /// disassociate parts of the process execution context + /// + /// See also [unshare(2)](https://man7.org/linux/man-pages/man2/unshare.2.html) + pub fn unshare(flags: CloneFlags) -> Result<()> { + let res = unsafe { libc::unshare(flags.bits()) }; + + Errno::result(res).map(drop) + } + + /// reassociate thread with a namespace + /// + /// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html) + pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> { + let res = unsafe { libc::setns(fd, nstype.bits()) }; + + Errno::result(res).map(drop) + } +} + +/// Explicitly yield the processor to other threads. +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html) +pub fn sched_yield() -> Result<()> { + let res = unsafe { libc::sched_yield() }; + + Errno::result(res).map(drop) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/aio.rs b/vendor/nix-v0.23.1-patched/src/sys/aio.rs new file mode 100644 index 000000000..e64a2a823 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/aio.rs @@ -0,0 +1,1122 @@ +// vim: tw=80 +//! POSIX Asynchronous I/O +//! +//! The POSIX AIO interface is used for asynchronous I/O on files and disk-like +//! devices. It supports [`read`](struct.AioCb.html#method.read), +//! [`write`](struct.AioCb.html#method.write), and +//! [`fsync`](struct.AioCb.html#method.fsync) operations. Completion +//! notifications can optionally be delivered via +//! [signals](../signal/enum.SigevNotify.html#variant.SigevSignal), via the +//! [`aio_suspend`](fn.aio_suspend.html) function, or via polling. Some +//! platforms support other completion +//! notifications, such as +//! [kevent](../signal/enum.SigevNotify.html#variant.SigevKevent). +//! +//! Multiple operations may be submitted in a batch with +//! [`lio_listio`](fn.lio_listio.html), though the standard does not guarantee +//! that they will be executed atomically. +//! +//! Outstanding operations may be cancelled with +//! [`cancel`](struct.AioCb.html#method.cancel) or +//! [`aio_cancel_all`](fn.aio_cancel_all.html), though the operating system may +//! not support this for all filesystems and devices. + +use crate::Result; +use crate::errno::Errno; +use std::os::unix::io::RawFd; +use libc::{c_void, off_t, size_t}; +use std::fmt; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::mem; +use std::pin::Pin; +use std::ptr::{null, null_mut}; +use crate::sys::signal::*; +use std::thread; +use crate::sys::time::TimeSpec; + +libc_enum! { + /// Mode for `AioCb::fsync`. Controls whether only data or both data and + /// metadata are synced. + #[repr(i32)] + #[non_exhaustive] + pub enum AioFsyncMode { + /// do it like `fsync` + O_SYNC, + /// on supported operating systems only, do it like `fdatasync` + #[cfg(any(target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + O_DSYNC + } +} + +libc_enum! { + /// When used with [`lio_listio`](fn.lio_listio.html), determines whether a + /// given `aiocb` should be used for a read operation, a write operation, or + /// ignored. Has no effect for any other aio functions. + #[repr(i32)] + #[non_exhaustive] + pub enum LioOpcode { + /// No operation + LIO_NOP, + /// Write data as if by a call to [`AioCb::write`] + LIO_WRITE, + /// Write data as if by a call to [`AioCb::read`] + LIO_READ, + } +} + +libc_enum! { + /// Mode for [`lio_listio`](fn.lio_listio.html) + #[repr(i32)] + pub enum LioMode { + /// Requests that [`lio_listio`](fn.lio_listio.html) block until all + /// requested operations have been completed + LIO_WAIT, + /// Requests that [`lio_listio`](fn.lio_listio.html) return immediately + LIO_NOWAIT, + } +} + +/// Return values for [`AioCb::cancel`](struct.AioCb.html#method.cancel) and +/// [`aio_cancel_all`](fn.aio_cancel_all.html) +#[repr(i32)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum AioCancelStat { + /// All outstanding requests were canceled + AioCanceled = libc::AIO_CANCELED, + /// Some requests were not canceled. Their status should be checked with + /// `AioCb::error` + AioNotCanceled = libc::AIO_NOTCANCELED, + /// All of the requests have already finished + AioAllDone = libc::AIO_ALLDONE, +} + +/// Newtype that adds Send and Sync to libc::aiocb, which contains raw pointers +#[repr(transparent)] +struct LibcAiocb(libc::aiocb); + +unsafe impl Send for LibcAiocb {} +unsafe impl Sync for LibcAiocb {} + +/// AIO Control Block. +/// +/// The basic structure used by all aio functions. Each `AioCb` represents one +/// I/O request. +pub struct AioCb<'a> { + aiocb: LibcAiocb, + /// Tracks whether the buffer pointed to by `libc::aiocb.aio_buf` is mutable + mutable: bool, + /// Could this `AioCb` potentially have any in-kernel state? + in_progress: bool, + _buffer: std::marker::PhantomData<&'a [u8]>, + _pin: std::marker::PhantomPinned +} + +impl<'a> AioCb<'a> { + /// Returns the underlying file descriptor associated with the `AioCb` + pub fn fd(&self) -> RawFd { + self.aiocb.0.aio_fildes + } + + /// Constructs a new `AioCb` with no associated buffer. + /// + /// The resulting `AioCb` structure is suitable for use with `AioCb::fsync`. + /// + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio`. + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// + /// # Examples + /// + /// Create an `AioCb` from a raw file descriptor and use it for an + /// [`fsync`](#method.fsync) operation. + /// + /// ``` + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify::SigevNone; + /// # use std::{thread, time}; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// let f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_fd( f.as_raw_fd(), 0, SigevNone); + /// aiocb.fsync(AioFsyncMode::O_SYNC).expect("aio_fsync failed early"); + /// while (aiocb.error() == Err(Errno::EINPROGRESS)) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// aiocb.aio_return().expect("aio_fsync failed late"); + /// ``` + pub fn from_fd(fd: RawFd, prio: libc::c_int, + sigev_notify: SigevNotify) -> Pin>> { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.0.aio_offset = 0; + a.0.aio_nbytes = 0; + a.0.aio_buf = null_mut(); + + Box::pin(AioCb { + aiocb: a, + mutable: false, + in_progress: false, + _buffer: PhantomData, + _pin: std::marker::PhantomPinned + }) + } + + // Private helper + #[cfg(not(any(target_os = "ios", target_os = "macos")))] + fn from_mut_slice_unpinned(fd: RawFd, offs: off_t, buf: &'a mut [u8], + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> AioCb<'a> + { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.0.aio_offset = offs; + a.0.aio_nbytes = buf.len() as size_t; + a.0.aio_buf = buf.as_ptr() as *mut c_void; + a.0.aio_lio_opcode = opcode as libc::c_int; + + AioCb { + aiocb: a, + mutable: true, + in_progress: false, + _buffer: PhantomData, + _pin: std::marker::PhantomPinned + } + } + + /// Constructs a new `AioCb` from a mutable slice. + /// + /// The resulting `AioCb` will be suitable for both read and write + /// operations, but only if the borrow checker can guarantee that the slice + /// will outlive the `AioCb`. That will usually be the case if the `AioCb` + /// is stack-allocated. + /// + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: A memory buffer + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb + /// + /// # Examples + /// + /// Create an `AioCb` from a mutable slice and read into it. + /// + /// ``` + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::io::Write; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// const INITIAL: &[u8] = b"abcdef123456"; + /// const LEN: usize = 4; + /// let mut rbuf = vec![0; LEN]; + /// let mut f = tempfile().unwrap(); + /// f.write_all(INITIAL).unwrap(); + /// { + /// let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(), + /// 2, //offset + /// &mut rbuf, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.read().unwrap(); + /// while (aiocb.error() == Err(Errno::EINPROGRESS)) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, LEN); + /// } + /// assert_eq!(rbuf, b"cdef"); + /// ``` + pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8], + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> Pin>> { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.0.aio_offset = offs; + a.0.aio_nbytes = buf.len() as size_t; + a.0.aio_buf = buf.as_ptr() as *mut c_void; + a.0.aio_lio_opcode = opcode as libc::c_int; + + Box::pin(AioCb { + aiocb: a, + mutable: true, + in_progress: false, + _buffer: PhantomData, + _pin: std::marker::PhantomPinned + }) + } + + /// Constructs a new `AioCb` from a mutable raw pointer + /// + /// Unlike `from_mut_slice`, this method returns a structure suitable for + /// placement on the heap. It may be used for both reads and writes. Due + /// to its unsafety, this method is not recommended. It is most useful when + /// heap allocation is required. + /// + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: Pointer to the memory buffer + /// * `len`: Length of the buffer pointed to by `buf` + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb + /// + /// # Safety + /// + /// The caller must ensure that the storage pointed to by `buf` outlives the + /// `AioCb`. The lifetime checker can't help here. + pub unsafe fn from_mut_ptr(fd: RawFd, offs: off_t, + buf: *mut c_void, len: usize, + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> Pin>> { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.0.aio_offset = offs; + a.0.aio_nbytes = len; + a.0.aio_buf = buf; + a.0.aio_lio_opcode = opcode as libc::c_int; + + Box::pin(AioCb { + aiocb: a, + mutable: true, + in_progress: false, + _buffer: PhantomData, + _pin: std::marker::PhantomPinned, + }) + } + + /// Constructs a new `AioCb` from a raw pointer. + /// + /// Unlike `from_slice`, this method returns a structure suitable for + /// placement on the heap. Due to its unsafety, this method is not + /// recommended. It is most useful when heap allocation is required. + /// + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: Pointer to the memory buffer + /// * `len`: Length of the buffer pointed to by `buf` + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb + /// + /// # Safety + /// + /// The caller must ensure that the storage pointed to by `buf` outlives the + /// `AioCb`. The lifetime checker can't help here. + pub unsafe fn from_ptr(fd: RawFd, offs: off_t, + buf: *const c_void, len: usize, + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> Pin>> { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.0.aio_offset = offs; + a.0.aio_nbytes = len; + // casting a const ptr to a mutable ptr here is ok, because we set the + // AioCb's mutable field to false + a.0.aio_buf = buf as *mut c_void; + a.0.aio_lio_opcode = opcode as libc::c_int; + + Box::pin(AioCb { + aiocb: a, + mutable: false, + in_progress: false, + _buffer: PhantomData, + _pin: std::marker::PhantomPinned + }) + } + + // Private helper + fn from_slice_unpinned(fd: RawFd, offs: off_t, buf: &'a [u8], + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> AioCb + { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.0.aio_offset = offs; + a.0.aio_nbytes = buf.len() as size_t; + // casting an immutable buffer to a mutable pointer looks unsafe, + // but technically its only unsafe to dereference it, not to create + // it. + a.0.aio_buf = buf.as_ptr() as *mut c_void; + assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer"); + a.0.aio_lio_opcode = opcode as libc::c_int; + + AioCb { + aiocb: a, + mutable: false, + in_progress: false, + _buffer: PhantomData, + _pin: std::marker::PhantomPinned + } + } + + /// Like [`AioCb::from_mut_slice`], but works on constant slices rather than + /// mutable slices. + /// + /// An `AioCb` created this way cannot be used with `read`, and its + /// `LioOpcode` cannot be set to `LIO_READ`. This method is useful when + /// writing a const buffer with `AioCb::write`, since `from_mut_slice` can't + /// work with const buffers. + /// + /// # Examples + /// + /// Construct an `AioCb` from a slice and use it for writing. + /// + /// ``` + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// const WBUF: &[u8] = b"abcdef123456"; + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + /// 2, //offset + /// WBUF, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// while (aiocb.error() == Err(Errno::EINPROGRESS)) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); + /// ``` + // Note: another solution to the problem of writing const buffers would be + // to genericize AioCb for both &mut [u8] and &[u8] buffers. AioCb::read + // could take the former and AioCb::write could take the latter. However, + // then lio_listio wouldn't work, because that function needs a slice of + // AioCb, and they must all be of the same type. + pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8], + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> Pin> + { + Box::pin(AioCb::from_slice_unpinned(fd, offs, buf, prio, sigev_notify, + opcode)) + } + + fn common_init(fd: RawFd, prio: libc::c_int, + sigev_notify: SigevNotify) -> LibcAiocb { + // Use mem::zeroed instead of explicitly zeroing each field, because the + // number and name of reserved fields is OS-dependent. On some OSes, + // some reserved fields are used the kernel for state, and must be + // explicitly zeroed when allocated. + let mut a = unsafe { mem::zeroed::()}; + a.aio_fildes = fd; + a.aio_reqprio = prio; + a.aio_sigevent = SigEvent::new(sigev_notify).sigevent(); + LibcAiocb(a) + } + + /// Update the notification settings for an existing `aiocb` + pub fn set_sigev_notify(self: &mut Pin>, + sigev_notify: SigevNotify) + { + // Safe because we don't move any of the data + let selfp = unsafe { + self.as_mut().get_unchecked_mut() + }; + selfp.aiocb.0.aio_sigevent = SigEvent::new(sigev_notify).sigevent(); + } + + /// Cancels an outstanding AIO request. + /// + /// The operating system is not required to implement cancellation for all + /// file and device types. Even if it does, there is no guarantee that the + /// operation has not already completed. So the caller must check the + /// result and handle operations that were not canceled or that have already + /// completed. + /// + /// # Examples + /// + /// Cancel an outstanding aio operation. Note that we must still call + /// `aio_return` to free resources, even though we don't care about the + /// result. + /// + /// ``` + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::io::Write; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// let wbuf = b"CDEF"; + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + /// 2, //offset + /// &wbuf[..], + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// let cs = aiocb.cancel().unwrap(); + /// if cs == AioCancelStat::AioNotCanceled { + /// while (aiocb.error() == Err(Errno::EINPROGRESS)) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// } + /// // Must call `aio_return`, but ignore the result + /// let _ = aiocb.aio_return(); + /// ``` + /// + /// # References + /// + /// [aio_cancel](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html) + pub fn cancel(self: &mut Pin>) -> Result { + let r = unsafe { + let selfp = self.as_mut().get_unchecked_mut(); + libc::aio_cancel(selfp.aiocb.0.aio_fildes, &mut selfp.aiocb.0) + }; + match r { + libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled), + libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled), + libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone), + -1 => Err(Errno::last()), + _ => panic!("unknown aio_cancel return value") + } + } + + fn error_unpinned(&mut self) -> Result<()> { + let r = unsafe { + libc::aio_error(&mut self.aiocb.0 as *mut libc::aiocb) + }; + match r { + 0 => Ok(()), + num if num > 0 => Err(Errno::from_i32(num)), + -1 => Err(Errno::last()), + num => panic!("unknown aio_error return value {:?}", num) + } + } + + /// Retrieve error status of an asynchronous operation. + /// + /// If the request has not yet completed, returns `EINPROGRESS`. Otherwise, + /// returns `Ok` or any other error. + /// + /// # Examples + /// + /// Issue an aio operation and use `error` to poll for completion. Polling + /// is an alternative to `aio_suspend`, used by most of the other examples. + /// + /// ``` + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// const WBUF: &[u8] = b"abcdef123456"; + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + /// 2, //offset + /// WBUF, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// while (aiocb.error() == Err(Errno::EINPROGRESS)) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); + /// ``` + /// + /// # References + /// + /// [aio_error](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_error.html) + pub fn error(self: &mut Pin>) -> Result<()> { + // Safe because error_unpinned doesn't move the data + let selfp = unsafe { + self.as_mut().get_unchecked_mut() + }; + selfp.error_unpinned() + } + + /// An asynchronous version of `fsync(2)`. + /// + /// # References + /// + /// [aio_fsync](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_fsync.html) + pub fn fsync(self: &mut Pin>, mode: AioFsyncMode) -> Result<()> { + // Safe because we don't move the libc::aiocb + unsafe { + let selfp = self.as_mut().get_unchecked_mut(); + Errno::result({ + let p: *mut libc::aiocb = &mut selfp.aiocb.0; + libc::aio_fsync(mode as libc::c_int, p) + }).map(|_| { + selfp.in_progress = true; + }) + } + } + + /// Returns the `aiocb`'s `LioOpcode` field + /// + /// If the value cannot be represented as an `LioOpcode`, returns `None` + /// instead. + pub fn lio_opcode(&self) -> Option { + match self.aiocb.0.aio_lio_opcode { + libc::LIO_READ => Some(LioOpcode::LIO_READ), + libc::LIO_WRITE => Some(LioOpcode::LIO_WRITE), + libc::LIO_NOP => Some(LioOpcode::LIO_NOP), + _ => None + } + } + + /// Returns the requested length of the aio operation in bytes + /// + /// This method returns the *requested* length of the operation. To get the + /// number of bytes actually read or written by a completed operation, use + /// `aio_return` instead. + pub fn nbytes(&self) -> usize { + self.aiocb.0.aio_nbytes + } + + /// Returns the file offset stored in the `AioCb` + pub fn offset(&self) -> off_t { + self.aiocb.0.aio_offset + } + + /// Returns the priority of the `AioCb` + pub fn priority(&self) -> libc::c_int { + self.aiocb.0.aio_reqprio + } + + /// Asynchronously reads from a file descriptor into a buffer + /// + /// # References + /// + /// [aio_read](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_read.html) + pub fn read(self: &mut Pin>) -> Result<()> { + assert!(self.mutable, "Can't read into an immutable buffer"); + // Safe because we don't move anything + let selfp = unsafe { + self.as_mut().get_unchecked_mut() + }; + Errno::result({ + let p: *mut libc::aiocb = &mut selfp.aiocb.0; + unsafe { libc::aio_read(p) } + }).map(|_| { + selfp.in_progress = true; + }) + } + + /// Returns the `SigEvent` stored in the `AioCb` + pub fn sigevent(&self) -> SigEvent { + SigEvent::from(&self.aiocb.0.aio_sigevent) + } + + fn aio_return_unpinned(&mut self) -> Result { + unsafe { + let p: *mut libc::aiocb = &mut self.aiocb.0; + self.in_progress = false; + Errno::result(libc::aio_return(p)) + } + } + + /// Retrieve return status of an asynchronous operation. + /// + /// Should only be called once for each `AioCb`, after `AioCb::error` + /// indicates that it has completed. The result is the same as for the + /// synchronous `read(2)`, `write(2)`, of `fsync(2)` functions. + /// + /// # References + /// + /// [aio_return](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html) + // Note: this should be just `return`, but that's a reserved word + pub fn aio_return(self: &mut Pin>) -> Result { + // Safe because aio_return_unpinned does not move the data + let selfp = unsafe { + self.as_mut().get_unchecked_mut() + }; + selfp.aio_return_unpinned() + } + + /// Asynchronously writes from a buffer to a file descriptor + /// + /// # References + /// + /// [aio_write](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_write.html) + pub fn write(self: &mut Pin>) -> Result<()> { + // Safe because we don't move anything + let selfp = unsafe { + self.as_mut().get_unchecked_mut() + }; + Errno::result({ + let p: *mut libc::aiocb = &mut selfp.aiocb.0; + unsafe{ libc::aio_write(p) } + }).map(|_| { + selfp.in_progress = true; + }) + } +} + +/// Cancels outstanding AIO requests for a given file descriptor. +/// +/// # Examples +/// +/// Issue an aio operation, then cancel all outstanding operations on that file +/// descriptor. +/// +/// ``` +/// # use nix::errno::Errno; +/// # use nix::Error; +/// # use nix::sys::aio::*; +/// # use nix::sys::signal::SigevNotify; +/// # use std::{thread, time}; +/// # use std::io::Write; +/// # use std::os::unix::io::AsRawFd; +/// # use tempfile::tempfile; +/// let wbuf = b"CDEF"; +/// let mut f = tempfile().unwrap(); +/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), +/// 2, //offset +/// &wbuf[..], +/// 0, //priority +/// SigevNotify::SigevNone, +/// LioOpcode::LIO_NOP); +/// aiocb.write().unwrap(); +/// let cs = aio_cancel_all(f.as_raw_fd()).unwrap(); +/// if cs == AioCancelStat::AioNotCanceled { +/// while (aiocb.error() == Err(Errno::EINPROGRESS)) { +/// thread::sleep(time::Duration::from_millis(10)); +/// } +/// } +/// // Must call `aio_return`, but ignore the result +/// let _ = aiocb.aio_return(); +/// ``` +/// +/// # References +/// +/// [`aio_cancel`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html) +pub fn aio_cancel_all(fd: RawFd) -> Result { + match unsafe { libc::aio_cancel(fd, null_mut()) } { + libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled), + libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled), + libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone), + -1 => Err(Errno::last()), + _ => panic!("unknown aio_cancel return value") + } +} + +/// Suspends the calling process until at least one of the specified `AioCb`s +/// has completed, a signal is delivered, or the timeout has passed. +/// +/// If `timeout` is `None`, `aio_suspend` will block indefinitely. +/// +/// # Examples +/// +/// Use `aio_suspend` to block until an aio operation completes. +/// +/// ``` +/// # use nix::sys::aio::*; +/// # use nix::sys::signal::SigevNotify; +/// # use std::os::unix::io::AsRawFd; +/// # use tempfile::tempfile; +/// const WBUF: &[u8] = b"abcdef123456"; +/// let mut f = tempfile().unwrap(); +/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), +/// 2, //offset +/// WBUF, +/// 0, //priority +/// SigevNotify::SigevNone, +/// LioOpcode::LIO_NOP); +/// aiocb.write().unwrap(); +/// aio_suspend(&[aiocb.as_ref()], None).expect("aio_suspend failed"); +/// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); +/// ``` +/// # References +/// +/// [`aio_suspend`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_suspend.html) +pub fn aio_suspend(list: &[Pin<&AioCb>], timeout: Option) -> Result<()> { + let plist = list as *const [Pin<&AioCb>] as *const [*const libc::aiocb]; + let p = plist as *const *const libc::aiocb; + let timep = match timeout { + None => null::(), + Some(x) => x.as_ref() as *const libc::timespec + }; + Errno::result(unsafe { + libc::aio_suspend(p, list.len() as i32, timep) + }).map(drop) +} + +impl<'a> Debug for AioCb<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("AioCb") + .field("aiocb", &self.aiocb.0) + .field("mutable", &self.mutable) + .field("in_progress", &self.in_progress) + .finish() + } +} + +impl<'a> Drop for AioCb<'a> { + /// If the `AioCb` has no remaining state in the kernel, just drop it. + /// Otherwise, dropping constitutes a resource leak, which is an error + fn drop(&mut self) { + assert!(thread::panicking() || !self.in_progress, + "Dropped an in-progress AioCb"); + } +} + +/// LIO Control Block. +/// +/// The basic structure used to issue multiple AIO operations simultaneously. +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +pub struct LioCb<'a> { + /// A collection of [`AioCb`]s. All of these will be issued simultaneously + /// by the [`listio`] method. + /// + /// [`AioCb`]: struct.AioCb.html + /// [`listio`]: #method.listio + // Their locations in memory must be fixed once they are passed to the + // kernel. So this field must be non-public so the user can't swap. + aiocbs: Box<[AioCb<'a>]>, + + /// The actual list passed to `libc::lio_listio`. + /// + /// It must live for as long as any of the operations are still being + /// processesed, because the aio subsystem uses its address as a unique + /// identifier. + list: Vec<*mut libc::aiocb>, + + /// A partial set of results. This field will get populated by + /// `listio_resubmit` when an `LioCb` is resubmitted after an error + results: Vec>> +} + +/// LioCb can't automatically impl Send and Sync just because of the raw +/// pointers in list. But that's stupid. There's no reason that raw pointers +/// should automatically be non-Send +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +unsafe impl<'a> Send for LioCb<'a> {} +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +unsafe impl<'a> Sync for LioCb<'a> {} + +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +impl<'a> LioCb<'a> { + /// Are no [`AioCb`]s contained? + pub fn is_empty(&self) -> bool { + self.aiocbs.is_empty() + } + + /// Return the number of individual [`AioCb`]s contained. + pub fn len(&self) -> usize { + self.aiocbs.len() + } + + /// Submits multiple asynchronous I/O requests with a single system call. + /// + /// They are not guaranteed to complete atomically, and the order in which + /// the requests are carried out is not specified. Reads, writes, and + /// fsyncs may be freely mixed. + /// + /// This function is useful for reducing the context-switch overhead of + /// submitting many AIO operations. It can also be used with + /// `LioMode::LIO_WAIT` to block on the result of several independent + /// operations. Used that way, it is often useful in programs that + /// otherwise make little use of AIO. + /// + /// # Examples + /// + /// Use `listio` to submit an aio operation and wait for its completion. In + /// this case, there is no need to use [`aio_suspend`] to wait or + /// [`AioCb::error`] to poll. + /// + /// ``` + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// const WBUF: &[u8] = b"abcdef123456"; + /// let mut f = tempfile().unwrap(); + /// let mut liocb = LioCbBuilder::with_capacity(1) + /// .emplace_slice( + /// f.as_raw_fd(), + /// 2, //offset + /// WBUF, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_WRITE + /// ).finish(); + /// liocb.listio(LioMode::LIO_WAIT, + /// SigevNotify::SigevNone).unwrap(); + /// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len()); + /// ``` + /// + /// # References + /// + /// [`lio_listio`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html) + /// + /// [`aio_suspend`]: fn.aio_suspend.html + /// [`AioCb::error`]: struct.AioCb.html#method.error + pub fn listio(&mut self, mode: LioMode, + sigev_notify: SigevNotify) -> Result<()> { + let sigev = SigEvent::new(sigev_notify); + let sigevp = &mut sigev.sigevent() as *mut libc::sigevent; + self.list.clear(); + for a in &mut self.aiocbs.iter_mut() { + a.in_progress = true; + self.list.push(a as *mut AioCb<'a> + as *mut libc::aiocb); + } + let p = self.list.as_ptr(); + Errno::result(unsafe { + libc::lio_listio(mode as i32, p, self.list.len() as i32, sigevp) + }).map(drop) + } + + /// Resubmits any incomplete operations with [`lio_listio`]. + /// + /// Sometimes, due to system resource limitations, an `lio_listio` call will + /// return `EIO`, or `EAGAIN`. Or, if a signal is received, it may return + /// `EINTR`. In any of these cases, only a subset of its constituent + /// operations will actually have been initiated. `listio_resubmit` will + /// resubmit any operations that are still uninitiated. + /// + /// After calling `listio_resubmit`, results should be collected by + /// [`LioCb::aio_return`]. + /// + /// # Examples + /// ```no_run + /// # use nix::Error; + /// # use nix::errno::Errno; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::os::unix::io::AsRawFd; + /// # use std::{thread, time}; + /// # use tempfile::tempfile; + /// const WBUF: &[u8] = b"abcdef123456"; + /// let mut f = tempfile().unwrap(); + /// let mut liocb = LioCbBuilder::with_capacity(1) + /// .emplace_slice( + /// f.as_raw_fd(), + /// 2, //offset + /// WBUF, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_WRITE + /// ).finish(); + /// let mut err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone); + /// while err == Err(Errno::EIO) || + /// err == Err(Errno::EAGAIN) { + /// thread::sleep(time::Duration::from_millis(10)); + /// err = liocb.listio_resubmit(LioMode::LIO_WAIT, SigevNotify::SigevNone); + /// } + /// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len()); + /// ``` + /// + /// # References + /// + /// [`lio_listio`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html) + /// + /// [`lio_listio`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html + /// [`LioCb::aio_return`]: struct.LioCb.html#method.aio_return + // Note: the addresses of any EINPROGRESS or EOK aiocbs _must_ not be + // changed by this method, because the kernel relies on their addresses + // being stable. + // Note: aiocbs that are Ok(()) must be finalized by aio_return, or else the + // sigev_notify will immediately refire. + pub fn listio_resubmit(&mut self, mode:LioMode, + sigev_notify: SigevNotify) -> Result<()> { + let sigev = SigEvent::new(sigev_notify); + let sigevp = &mut sigev.sigevent() as *mut libc::sigevent; + self.list.clear(); + + while self.results.len() < self.aiocbs.len() { + self.results.push(None); + } + + for (i, a) in self.aiocbs.iter_mut().enumerate() { + if self.results[i].is_some() { + // Already collected final status for this operation + continue; + } + match a.error_unpinned() { + Ok(()) => { + // aiocb is complete; collect its status and don't resubmit + self.results[i] = Some(a.aio_return_unpinned()); + }, + Err(Errno::EAGAIN) => { + self.list.push(a as *mut AioCb<'a> as *mut libc::aiocb); + }, + Err(Errno::EINPROGRESS) => { + // aiocb is was successfully queued; no need to do anything + }, + Err(Errno::EINVAL) => panic!( + "AioCb was never submitted, or already finalized"), + _ => unreachable!() + } + } + let p = self.list.as_ptr(); + Errno::result(unsafe { + libc::lio_listio(mode as i32, p, self.list.len() as i32, sigevp) + }).map(drop) + } + + /// Collect final status for an individual `AioCb` submitted as part of an + /// `LioCb`. + /// + /// This is just like [`AioCb::aio_return`], except it takes into account + /// operations that were restarted by [`LioCb::listio_resubmit`] + /// + /// [`AioCb::aio_return`]: struct.AioCb.html#method.aio_return + /// [`LioCb::listio_resubmit`]: #method.listio_resubmit + pub fn aio_return(&mut self, i: usize) -> Result { + if i >= self.results.len() || self.results[i].is_none() { + self.aiocbs[i].aio_return_unpinned() + } else { + self.results[i].unwrap() + } + } + + /// Retrieve error status of an individual `AioCb` submitted as part of an + /// `LioCb`. + /// + /// This is just like [`AioCb::error`], except it takes into account + /// operations that were restarted by [`LioCb::listio_resubmit`] + /// + /// [`AioCb::error`]: struct.AioCb.html#method.error + /// [`LioCb::listio_resubmit`]: #method.listio_resubmit + pub fn error(&mut self, i: usize) -> Result<()> { + if i >= self.results.len() || self.results[i].is_none() { + self.aiocbs[i].error_unpinned() + } else { + Ok(()) + } + } +} + +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +impl<'a> Debug for LioCb<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("LioCb") + .field("aiocbs", &self.aiocbs) + .finish() + } +} + +/// Used to construct `LioCb` +// This must be a separate class from LioCb due to pinning constraints. LioCb +// must use a boxed slice of AioCbs so they will have stable storage, but +// LioCbBuilder must use a Vec to make construction possible when the final size +// is unknown. +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[derive(Debug)] +pub struct LioCbBuilder<'a> { + /// A collection of [`AioCb`]s. + /// + /// [`AioCb`]: struct.AioCb.html + pub aiocbs: Vec>, +} + +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +impl<'a> LioCbBuilder<'a> { + /// Initialize an empty `LioCb` + pub fn with_capacity(capacity: usize) -> LioCbBuilder<'a> { + LioCbBuilder { + aiocbs: Vec::with_capacity(capacity), + } + } + + /// Add a new operation on an immutable slice to the [`LioCb`] under + /// construction. + /// + /// Arguments are the same as for [`AioCb::from_slice`] + /// + /// [`LioCb`]: struct.LioCb.html + /// [`AioCb::from_slice`]: struct.AioCb.html#method.from_slice + pub fn emplace_slice(mut self, fd: RawFd, offs: off_t, buf: &'a [u8], + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> Self + { + self.aiocbs.push(AioCb::from_slice_unpinned(fd, offs, buf, prio, + sigev_notify, opcode)); + self + } + + /// Add a new operation on a mutable slice to the [`LioCb`] under + /// construction. + /// + /// Arguments are the same as for [`AioCb::from_mut_slice`] + /// + /// [`LioCb`]: struct.LioCb.html + /// [`AioCb::from_mut_slice`]: struct.AioCb.html#method.from_mut_slice + pub fn emplace_mut_slice(mut self, fd: RawFd, offs: off_t, + buf: &'a mut [u8], prio: libc::c_int, + sigev_notify: SigevNotify, opcode: LioOpcode) + -> Self + { + self.aiocbs.push(AioCb::from_mut_slice_unpinned(fd, offs, buf, prio, + sigev_notify, opcode)); + self + } + + /// Finalize this [`LioCb`]. + /// + /// Afterwards it will be possible to issue the operations with + /// [`LioCb::listio`]. Conversely, it will no longer be possible to add new + /// operations with [`LioCbBuilder::emplace_slice`] or + /// [`LioCbBuilder::emplace_mut_slice`]. + /// + /// [`LioCb::listio`]: struct.LioCb.html#method.listio + /// [`LioCb::from_mut_slice`]: struct.LioCb.html#method.from_mut_slice + /// [`LioCb::from_slice`]: struct.LioCb.html#method.from_slice + pub fn finish(self) -> LioCb<'a> { + let len = self.aiocbs.len(); + LioCb { + aiocbs: self.aiocbs.into(), + list: Vec::with_capacity(len), + results: Vec::with_capacity(len) + } + } +} + +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[cfg(test)] +mod t { + use super::*; + + // It's important that `LioCb` be `UnPin`. The tokio-file crate relies on + // it. + #[test] + fn liocb_is_unpin() { + use assert_impl::assert_impl; + + assert_impl!(Unpin: LioCb); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/epoll.rs b/vendor/nix-v0.23.1-patched/src/sys/epoll.rs new file mode 100644 index 000000000..6bc2a2539 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/epoll.rs @@ -0,0 +1,109 @@ +use crate::Result; +use crate::errno::Errno; +use libc::{self, c_int}; +use std::os::unix::io::RawFd; +use std::ptr; +use std::mem; + +libc_bitflags!( + pub struct EpollFlags: c_int { + EPOLLIN; + EPOLLPRI; + EPOLLOUT; + EPOLLRDNORM; + EPOLLRDBAND; + EPOLLWRNORM; + EPOLLWRBAND; + EPOLLMSG; + EPOLLERR; + EPOLLHUP; + EPOLLRDHUP; + #[cfg(target_os = "linux")] // Added in 4.5; not in Android. + EPOLLEXCLUSIVE; + #[cfg(not(target_arch = "mips"))] + EPOLLWAKEUP; + EPOLLONESHOT; + EPOLLET; + } +); + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(i32)] +#[non_exhaustive] +pub enum EpollOp { + EpollCtlAdd = libc::EPOLL_CTL_ADD, + EpollCtlDel = libc::EPOLL_CTL_DEL, + EpollCtlMod = libc::EPOLL_CTL_MOD, +} + +libc_bitflags!{ + pub struct EpollCreateFlags: c_int { + EPOLL_CLOEXEC; + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(transparent)] +pub struct EpollEvent { + event: libc::epoll_event, +} + +impl EpollEvent { + pub fn new(events: EpollFlags, data: u64) -> Self { + EpollEvent { event: libc::epoll_event { events: events.bits() as u32, u64: data } } + } + + pub fn empty() -> Self { + unsafe { mem::zeroed::() } + } + + pub fn events(&self) -> EpollFlags { + EpollFlags::from_bits(self.event.events as c_int).unwrap() + } + + pub fn data(&self) -> u64 { + self.event.u64 + } +} + +#[inline] +pub fn epoll_create() -> Result { + let res = unsafe { libc::epoll_create(1024) }; + + Errno::result(res) +} + +#[inline] +pub fn epoll_create1(flags: EpollCreateFlags) -> Result { + let res = unsafe { libc::epoll_create1(flags.bits()) }; + + Errno::result(res) +} + +#[inline] +pub fn epoll_ctl<'a, T>(epfd: RawFd, op: EpollOp, fd: RawFd, event: T) -> Result<()> + where T: Into> +{ + let mut event: Option<&mut EpollEvent> = event.into(); + if event.is_none() && op != EpollOp::EpollCtlDel { + Err(Errno::EINVAL) + } else { + let res = unsafe { + if let Some(ref mut event) = event { + libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event) + } else { + libc::epoll_ctl(epfd, op as c_int, fd, ptr::null_mut()) + } + }; + Errno::result(res).map(drop) + } +} + +#[inline] +pub fn epoll_wait(epfd: RawFd, events: &mut [EpollEvent], timeout_ms: isize) -> Result { + let res = unsafe { + libc::epoll_wait(epfd, events.as_mut_ptr() as *mut libc::epoll_event, events.len() as c_int, timeout_ms as c_int) + }; + + Errno::result(res).map(|r| r as usize) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/event.rs b/vendor/nix-v0.23.1-patched/src/sys/event.rs new file mode 100644 index 000000000..c648f5ebc --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/event.rs @@ -0,0 +1,348 @@ +/* TOOD: Implement for other kqueue based systems + */ + +use crate::{Errno, Result}; +#[cfg(not(target_os = "netbsd"))] +use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t}; +#[cfg(target_os = "netbsd")] +use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t}; +use std::convert::TryInto; +use std::os::unix::io::RawFd; +use std::ptr; + +// Redefine kevent in terms of programmer-friendly enums and bitfields. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct KEvent { + kevent: libc::kevent, +} + +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd"))] +type type_of_udata = *mut libc::c_void; +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos"))] +type type_of_data = intptr_t; +#[cfg(any(target_os = "netbsd"))] +type type_of_udata = intptr_t; +#[cfg(any(target_os = "netbsd", target_os = "openbsd"))] +type type_of_data = i64; + +#[cfg(target_os = "netbsd")] +type type_of_event_filter = u32; +#[cfg(not(target_os = "netbsd"))] +type type_of_event_filter = i16; +libc_enum! { + #[cfg_attr(target_os = "netbsd", repr(u32))] + #[cfg_attr(not(target_os = "netbsd"), repr(i16))] + #[non_exhaustive] + pub enum EventFilter { + EVFILT_AIO, + /// Returns whenever there is no remaining data in the write buffer + #[cfg(target_os = "freebsd")] + EVFILT_EMPTY, + #[cfg(target_os = "dragonfly")] + EVFILT_EXCEPT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos"))] + EVFILT_FS, + #[cfg(target_os = "freebsd")] + EVFILT_LIO, + #[cfg(any(target_os = "ios", target_os = "macos"))] + EVFILT_MACHPORT, + EVFILT_PROC, + /// Returns events associated with the process referenced by a given + /// process descriptor, created by `pdfork()`. The events to monitor are: + /// + /// - NOTE_EXIT: the process has exited. The exit status will be stored in data. + #[cfg(target_os = "freebsd")] + EVFILT_PROCDESC, + EVFILT_READ, + /// Returns whenever an asynchronous `sendfile()` call completes. + #[cfg(target_os = "freebsd")] + EVFILT_SENDFILE, + EVFILT_SIGNAL, + EVFILT_TIMER, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos"))] + EVFILT_USER, + #[cfg(any(target_os = "ios", target_os = "macos"))] + EVFILT_VM, + EVFILT_VNODE, + EVFILT_WRITE, + } + impl TryFrom +} + +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd"))] +pub type type_of_event_flag = u16; +#[cfg(any(target_os = "netbsd"))] +pub type type_of_event_flag = u32; +libc_bitflags!{ + pub struct EventFlag: type_of_event_flag { + EV_ADD; + EV_CLEAR; + EV_DELETE; + EV_DISABLE; + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "netbsd", target_os = "openbsd"))] + EV_DISPATCH; + #[cfg(target_os = "freebsd")] + EV_DROP; + EV_ENABLE; + EV_EOF; + EV_ERROR; + #[cfg(any(target_os = "macos", target_os = "ios"))] + EV_FLAG0; + EV_FLAG1; + #[cfg(target_os = "dragonfly")] + EV_NODATA; + EV_ONESHOT; + #[cfg(any(target_os = "macos", target_os = "ios"))] + EV_OOBAND; + #[cfg(any(target_os = "macos", target_os = "ios"))] + EV_POLL; + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "netbsd", target_os = "openbsd"))] + EV_RECEIPT; + EV_SYSFLAGS; + } +} + +libc_bitflags!( + pub struct FilterFlag: u32 { + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_ABSOLUTE; + NOTE_ATTRIB; + NOTE_CHILD; + NOTE_DELETE; + #[cfg(target_os = "openbsd")] + NOTE_EOF; + NOTE_EXEC; + NOTE_EXIT; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_EXITSTATUS; + NOTE_EXTEND; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFAND; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFCOPY; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFCTRLMASK; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFLAGSMASK; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFNOP; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFOR; + NOTE_FORK; + NOTE_LINK; + NOTE_LOWAT; + #[cfg(target_os = "freebsd")] + NOTE_MSECONDS; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_NONE; + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))] + NOTE_NSECONDS; + #[cfg(target_os = "dragonfly")] + NOTE_OOB; + NOTE_PCTRLMASK; + NOTE_PDATAMASK; + NOTE_RENAME; + NOTE_REVOKE; + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))] + NOTE_SECONDS; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_SIGNAL; + NOTE_TRACK; + NOTE_TRACKERR; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_TRIGGER; + #[cfg(target_os = "openbsd")] + NOTE_TRUNCATE; + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))] + NOTE_USECONDS; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_VM_ERROR; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_VM_PRESSURE; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_VM_PRESSURE_SUDDEN_TERMINATE; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_VM_PRESSURE_TERMINATE; + NOTE_WRITE; + } +); + +pub fn kqueue() -> Result { + let res = unsafe { libc::kqueue() }; + + Errno::result(res) +} + + +// KEvent can't derive Send because on some operating systems, udata is defined +// as a void*. However, KEvent's public API always treats udata as an intptr_t, +// which is safe to Send. +unsafe impl Send for KEvent { +} + +impl KEvent { + pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag, + fflags:FilterFlag, data: intptr_t, udata: intptr_t) -> KEvent { + KEvent { kevent: libc::kevent { + ident, + filter: filter as type_of_event_filter, + flags: flags.bits(), + fflags: fflags.bits(), + data: data as type_of_data, + udata: udata as type_of_udata + } } + } + + pub fn ident(&self) -> uintptr_t { + self.kevent.ident + } + + pub fn filter(&self) -> Result { + self.kevent.filter.try_into() + } + + pub fn flags(&self) -> EventFlag { + EventFlag::from_bits(self.kevent.flags).unwrap() + } + + pub fn fflags(&self) -> FilterFlag { + FilterFlag::from_bits(self.kevent.fflags).unwrap() + } + + pub fn data(&self) -> intptr_t { + self.kevent.data as intptr_t + } + + pub fn udata(&self) -> intptr_t { + self.kevent.udata as intptr_t + } +} + +pub fn kevent(kq: RawFd, + changelist: &[KEvent], + eventlist: &mut [KEvent], + timeout_ms: usize) -> Result { + + // Convert ms to timespec + let timeout = timespec { + tv_sec: (timeout_ms / 1000) as time_t, + tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long + }; + + kevent_ts(kq, changelist, eventlist, Some(timeout)) +} + +#[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd"))] +type type_of_nchanges = c_int; +#[cfg(target_os = "netbsd")] +type type_of_nchanges = size_t; + +pub fn kevent_ts(kq: RawFd, + changelist: &[KEvent], + eventlist: &mut [KEvent], + timeout_opt: Option) -> Result { + + let res = unsafe { + libc::kevent( + kq, + changelist.as_ptr() as *const libc::kevent, + changelist.len() as type_of_nchanges, + eventlist.as_mut_ptr() as *mut libc::kevent, + eventlist.len() as type_of_nchanges, + if let Some(ref timeout) = timeout_opt {timeout as *const timespec} else {ptr::null()}) + }; + + Errno::result(res).map(|r| r as usize) +} + +#[inline] +pub fn ev_set(ev: &mut KEvent, + ident: usize, + filter: EventFilter, + flags: EventFlag, + fflags: FilterFlag, + udata: intptr_t) { + + ev.kevent.ident = ident as uintptr_t; + ev.kevent.filter = filter as type_of_event_filter; + ev.kevent.flags = flags.bits(); + ev.kevent.fflags = fflags.bits(); + ev.kevent.data = 0; + ev.kevent.udata = udata as type_of_udata; +} + +#[test] +fn test_struct_kevent() { + use std::mem; + + let udata : intptr_t = 12345; + + let actual = KEvent::new(0xdead_beef, + EventFilter::EVFILT_READ, + EventFlag::EV_ONESHOT | EventFlag::EV_ADD, + FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT, + 0x1337, + udata); + assert_eq!(0xdead_beef, actual.ident()); + let filter = actual.kevent.filter; + assert_eq!(libc::EVFILT_READ, filter); + assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits()); + assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits()); + assert_eq!(0x1337, actual.data() as type_of_data); + assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata); + assert_eq!(mem::size_of::(), mem::size_of::()); +} + +#[test] +fn test_kevent_filter() { + let udata : intptr_t = 12345; + + let actual = KEvent::new(0xdead_beef, + EventFilter::EVFILT_READ, + EventFlag::EV_ONESHOT | EventFlag::EV_ADD, + FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT, + 0x1337, + udata); + assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap()); +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/eventfd.rs b/vendor/nix-v0.23.1-patched/src/sys/eventfd.rs new file mode 100644 index 000000000..c54f952f0 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/eventfd.rs @@ -0,0 +1,17 @@ +use std::os::unix::io::RawFd; +use crate::Result; +use crate::errno::Errno; + +libc_bitflags! { + pub struct EfdFlags: libc::c_int { + EFD_CLOEXEC; // Since Linux 2.6.27 + EFD_NONBLOCK; // Since Linux 2.6.27 + EFD_SEMAPHORE; // Since Linux 2.6.30 + } +} + +pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result { + let res = unsafe { libc::eventfd(initval, flags.bits()) }; + + Errno::result(res).map(|r| r as RawFd) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/inotify.rs b/vendor/nix-v0.23.1-patched/src/sys/inotify.rs new file mode 100644 index 000000000..3f5ae22ab --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/inotify.rs @@ -0,0 +1,233 @@ +//! Monitoring API for filesystem events. +//! +//! Inotify is a Linux-only API to monitor filesystems events. +//! +//! For more documentation, please read [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html). +//! +//! # Examples +//! +//! Monitor all events happening in directory "test": +//! ```no_run +//! # use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify}; +//! # +//! // We create a new inotify instance. +//! let instance = Inotify::init(InitFlags::empty()).unwrap(); +//! +//! // We add a new watch on directory "test" for all events. +//! let wd = instance.add_watch("test", AddWatchFlags::IN_ALL_EVENTS).unwrap(); +//! +//! loop { +//! // We read from our inotify instance for events. +//! let events = instance.read_events().unwrap(); +//! println!("Events: {:?}", events); +//! } +//! ``` + +use libc::{ + c_char, + c_int, +}; +use std::ffi::{OsString,OsStr,CStr}; +use std::os::unix::ffi::OsStrExt; +use std::mem::{MaybeUninit, size_of}; +use std::os::unix::io::{RawFd,AsRawFd,FromRawFd}; +use std::ptr; +use crate::unistd::read; +use crate::Result; +use crate::NixPath; +use crate::errno::Errno; + +libc_bitflags! { + /// Configuration options for [`inotify_add_watch`](fn.inotify_add_watch.html). + pub struct AddWatchFlags: u32 { + IN_ACCESS; + IN_MODIFY; + IN_ATTRIB; + IN_CLOSE_WRITE; + IN_CLOSE_NOWRITE; + IN_OPEN; + IN_MOVED_FROM; + IN_MOVED_TO; + IN_CREATE; + IN_DELETE; + IN_DELETE_SELF; + IN_MOVE_SELF; + + IN_UNMOUNT; + IN_Q_OVERFLOW; + IN_IGNORED; + + IN_CLOSE; + IN_MOVE; + + IN_ONLYDIR; + IN_DONT_FOLLOW; + + IN_ISDIR; + IN_ONESHOT; + IN_ALL_EVENTS; + } +} + +libc_bitflags! { + /// Configuration options for [`inotify_init1`](fn.inotify_init1.html). + pub struct InitFlags: c_int { + IN_CLOEXEC; + IN_NONBLOCK; + } +} + +/// An inotify instance. This is also a file descriptor, you can feed it to +/// other interfaces consuming file descriptors, epoll for example. +#[derive(Debug, Clone, Copy)] +pub struct Inotify { + fd: RawFd +} + +/// This object is returned when you create a new watch on an inotify instance. +/// It is then returned as part of an event once triggered. It allows you to +/// know which watch triggered which event. +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)] +pub struct WatchDescriptor { + wd: i32 +} + +/// A single inotify event. +/// +/// For more documentation see, [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html). +#[derive(Debug)] +pub struct InotifyEvent { + /// Watch descriptor. This field corresponds to the watch descriptor you + /// were issued when calling add_watch. It allows you to know which watch + /// this event comes from. + pub wd: WatchDescriptor, + /// Event mask. This field is a bitfield describing the exact event that + /// occured. + pub mask: AddWatchFlags, + /// This cookie is a number that allows you to connect related events. For + /// now only IN_MOVED_FROM and IN_MOVED_TO can be connected. + pub cookie: u32, + /// Filename. This field exists only if the event was triggered for a file + /// inside the watched directory. + pub name: Option +} + +impl Inotify { + /// Initialize a new inotify instance. + /// + /// Returns a Result containing an inotify instance. + /// + /// For more information see, [inotify_init(2)](https://man7.org/linux/man-pages/man2/inotify_init.2.html). + pub fn init(flags: InitFlags) -> Result { + let res = Errno::result(unsafe { + libc::inotify_init1(flags.bits()) + }); + + res.map(|fd| Inotify { fd }) + } + + /// Adds a new watch on the target file or directory. + /// + /// Returns a watch descriptor. This is not a File Descriptor! + /// + /// For more information see, [inotify_add_watch(2)](https://man7.org/linux/man-pages/man2/inotify_add_watch.2.html). + pub fn add_watch(self, + path: &P, + mask: AddWatchFlags) + -> Result + { + let res = path.with_nix_path(|cstr| { + unsafe { + libc::inotify_add_watch(self.fd, cstr.as_ptr(), mask.bits()) + } + })?; + + Errno::result(res).map(|wd| WatchDescriptor { wd }) + } + + /// Removes an existing watch using the watch descriptor returned by + /// inotify_add_watch. + /// + /// Returns an EINVAL error if the watch descriptor is invalid. + /// + /// For more information see, [inotify_rm_watch(2)](https://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html). + #[cfg(target_os = "linux")] + pub fn rm_watch(self, wd: WatchDescriptor) -> Result<()> { + let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd) }; + + Errno::result(res).map(drop) + } + + #[cfg(target_os = "android")] + pub fn rm_watch(self, wd: WatchDescriptor) -> Result<()> { + let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd as u32) }; + + Errno::result(res).map(drop) + } + + /// Reads a collection of events from the inotify file descriptor. This call + /// can either be blocking or non blocking depending on whether IN_NONBLOCK + /// was set at initialization. + /// + /// Returns as many events as available. If the call was non blocking and no + /// events could be read then the EAGAIN error is returned. + pub fn read_events(self) -> Result> { + let header_size = size_of::(); + const BUFSIZ: usize = 4096; + let mut buffer = [0u8; BUFSIZ]; + let mut events = Vec::new(); + let mut offset = 0; + + let nread = read(self.fd, &mut buffer)?; + + while (nread - offset) >= header_size { + let event = unsafe { + let mut event = MaybeUninit::::uninit(); + ptr::copy_nonoverlapping( + buffer.as_ptr().add(offset), + event.as_mut_ptr() as *mut u8, + (BUFSIZ - offset).min(header_size) + ); + event.assume_init() + }; + + let name = match event.len { + 0 => None, + _ => { + let ptr = unsafe { + buffer + .as_ptr() + .add(offset + header_size) + as *const c_char + }; + let cstr = unsafe { CStr::from_ptr(ptr) }; + + Some(OsStr::from_bytes(cstr.to_bytes()).to_owned()) + } + }; + + events.push(InotifyEvent { + wd: WatchDescriptor { wd: event.wd }, + mask: AddWatchFlags::from_bits_truncate(event.mask), + cookie: event.cookie, + name + }); + + offset += header_size + event.len as usize; + } + + Ok(events) + } +} + +impl AsRawFd for Inotify { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +impl FromRawFd for Inotify { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Inotify { fd } + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/ioctl/bsd.rs b/vendor/nix-v0.23.1-patched/src/sys/ioctl/bsd.rs new file mode 100644 index 000000000..4ce4d332a --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/ioctl/bsd.rs @@ -0,0 +1,109 @@ +/// The datatype used for the ioctl number +#[doc(hidden)] +#[cfg(not(target_os = "illumos"))] +pub type ioctl_num_type = ::libc::c_ulong; + +#[doc(hidden)] +#[cfg(target_os = "illumos")] +pub type ioctl_num_type = ::libc::c_int; + +/// The datatype used for the 3rd argument +#[doc(hidden)] +pub type ioctl_param_type = ::libc::c_int; + +mod consts { + use crate::sys::ioctl::ioctl_num_type; + #[doc(hidden)] + pub const VOID: ioctl_num_type = 0x2000_0000; + #[doc(hidden)] + pub const OUT: ioctl_num_type = 0x4000_0000; + #[doc(hidden)] + #[allow(overflowing_literals)] + pub const IN: ioctl_num_type = 0x8000_0000; + #[doc(hidden)] + pub const INOUT: ioctl_num_type = IN|OUT; + #[doc(hidden)] + pub const IOCPARM_MASK: ioctl_num_type = 0x1fff; +} + +pub use self::consts::*; + +#[macro_export] +#[doc(hidden)] +macro_rules! ioc { + ($inout:expr, $group:expr, $num:expr, $len:expr) => ( + $inout | (($len as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as $crate::sys::ioctl::ioctl_num_type) << 8) | ($num as $crate::sys::ioctl::ioctl_num_type) + ) +} + +/// Generate an ioctl request code for a command that passes no data. +/// +/// This is equivalent to the `_IO()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_none!()` directly. +/// +/// # Example +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// const KVMIO: u8 = 0xAE; +/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03)); +/// # fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! request_code_none { + ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, 0)) +} + +/// Generate an ioctl request code for a command that passes an integer +/// +/// This is equivalent to the `_IOWINT()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_write_int!()` directly. +#[macro_export(local_inner_macros)] +macro_rules! request_code_write_int { + ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, ::std::mem::size_of::<$crate::libc::c_int>())) +} + +/// Generate an ioctl request code for a command that reads. +/// +/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_read!()` directly. +/// +/// The read/write direction is relative to userland, so this +/// command would be userland is reading and the kernel is +/// writing. +#[macro_export(local_inner_macros)] +macro_rules! request_code_read { + ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::OUT, $g, $n, $len)) +} + +/// Generate an ioctl request code for a command that writes. +/// +/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_write!()` directly. +/// +/// The read/write direction is relative to userland, so this +/// command would be userland is writing and the kernel is +/// reading. +#[macro_export(local_inner_macros)] +macro_rules! request_code_write { + ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::IN, $g, $n, $len)) +} + +/// Generate an ioctl request code for a command that reads and writes. +/// +/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_readwrite!()` directly. +#[macro_export(local_inner_macros)] +macro_rules! request_code_readwrite { + ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::INOUT, $g, $n, $len)) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/ioctl/linux.rs b/vendor/nix-v0.23.1-patched/src/sys/ioctl/linux.rs new file mode 100644 index 000000000..68ebaba9b --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/ioctl/linux.rs @@ -0,0 +1,141 @@ +/// The datatype used for the ioctl number +#[cfg(any(target_os = "android", target_env = "musl"))] +#[doc(hidden)] +pub type ioctl_num_type = ::libc::c_int; +#[cfg(not(any(target_os = "android", target_env = "musl")))] +#[doc(hidden)] +pub type ioctl_num_type = ::libc::c_ulong; +/// The datatype used for the 3rd argument +#[doc(hidden)] +pub type ioctl_param_type = ::libc::c_ulong; + +#[doc(hidden)] +pub const NRBITS: ioctl_num_type = 8; +#[doc(hidden)] +pub const TYPEBITS: ioctl_num_type = 8; + +#[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "sparc64"))] +mod consts { + #[doc(hidden)] + pub const NONE: u8 = 1; + #[doc(hidden)] + pub const READ: u8 = 2; + #[doc(hidden)] + pub const WRITE: u8 = 4; + #[doc(hidden)] + pub const SIZEBITS: u8 = 13; + #[doc(hidden)] + pub const DIRBITS: u8 = 3; +} + +// "Generic" ioctl protocol +#[cfg(any(target_arch = "x86", + target_arch = "arm", + target_arch = "s390x", + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "riscv64"))] +mod consts { + #[doc(hidden)] + pub const NONE: u8 = 0; + #[doc(hidden)] + pub const READ: u8 = 2; + #[doc(hidden)] + pub const WRITE: u8 = 1; + #[doc(hidden)] + pub const SIZEBITS: u8 = 14; + #[doc(hidden)] + pub const DIRBITS: u8 = 2; +} + +pub use self::consts::*; + +#[doc(hidden)] +pub const NRSHIFT: ioctl_num_type = 0; +#[doc(hidden)] +pub const TYPESHIFT: ioctl_num_type = NRSHIFT + NRBITS as ioctl_num_type; +#[doc(hidden)] +pub const SIZESHIFT: ioctl_num_type = TYPESHIFT + TYPEBITS as ioctl_num_type; +#[doc(hidden)] +pub const DIRSHIFT: ioctl_num_type = SIZESHIFT + SIZEBITS as ioctl_num_type; + +#[doc(hidden)] +pub const NRMASK: ioctl_num_type = (1 << NRBITS) - 1; +#[doc(hidden)] +pub const TYPEMASK: ioctl_num_type = (1 << TYPEBITS) - 1; +#[doc(hidden)] +pub const SIZEMASK: ioctl_num_type = (1 << SIZEBITS) - 1; +#[doc(hidden)] +pub const DIRMASK: ioctl_num_type = (1 << DIRBITS) - 1; + +/// Encode an ioctl command. +#[macro_export] +#[doc(hidden)] +macro_rules! ioc { + ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => ( + (($dir as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::DIRMASK) << $crate::sys::ioctl::DIRSHIFT) | + (($ty as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::TYPEMASK) << $crate::sys::ioctl::TYPESHIFT) | + (($nr as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::NRMASK) << $crate::sys::ioctl::NRSHIFT) | + (($sz as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::SIZEMASK) << $crate::sys::ioctl::SIZESHIFT)) +} + +/// Generate an ioctl request code for a command that passes no data. +/// +/// This is equivalent to the `_IO()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_none!()` directly. +/// +/// # Example +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// const KVMIO: u8 = 0xAE; +/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03)); +/// # fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! request_code_none { + ($ty:expr, $nr:expr) => (ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0)) +} + +/// Generate an ioctl request code for a command that reads. +/// +/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_read!()` directly. +/// +/// The read/write direction is relative to userland, so this +/// command would be userland is reading and the kernel is +/// writing. +#[macro_export(local_inner_macros)] +macro_rules! request_code_read { + ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz)) +} + +/// Generate an ioctl request code for a command that writes. +/// +/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_write!()` directly. +/// +/// The read/write direction is relative to userland, so this +/// command would be userland is writing and the kernel is +/// reading. +#[macro_export(local_inner_macros)] +macro_rules! request_code_write { + ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz)) +} + +/// Generate an ioctl request code for a command that reads and writes. +/// +/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_readwrite!()` directly. +#[macro_export(local_inner_macros)] +macro_rules! request_code_readwrite { + ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz)) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/ioctl/mod.rs b/vendor/nix-v0.23.1-patched/src/sys/ioctl/mod.rs new file mode 100644 index 000000000..203b7d06f --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/ioctl/mod.rs @@ -0,0 +1,778 @@ +//! Provide helpers for making ioctl system calls. +//! +//! This library is pretty low-level and messy. `ioctl` is not fun. +//! +//! What is an `ioctl`? +//! =================== +//! +//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want to add a new +//! syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, and the commands that can be +//! sent with it. `ioctl` stands for "IO control", and the commands are always sent to a file +//! descriptor. +//! +//! It is common to see `ioctl`s used for the following purposes: +//! +//! * Provide read/write access to out-of-band data related to a device such as configuration +//! (for instance, setting serial port options) +//! * Provide a mechanism for performing full-duplex data transfers (for instance, xfer on SPI +//! devices). +//! * Provide access to control functions on a device (for example, on Linux you can send +//! commands like pause, resume, and eject to the CDROM device. +//! * Do whatever else the device driver creator thought made most sense. +//! +//! `ioctl`s are synchronous system calls and are similar to read and write calls in that regard. +//! They operate on file descriptors and have an identifier that specifies what the ioctl is. +//! Additionally they may read or write data and therefore need to pass along a data pointer. +//! Besides the semantics of the ioctls being confusing, the generation of this identifer can also +//! be difficult. +//! +//! Historically `ioctl` numbers were arbitrary hard-coded values. In Linux (before 2.6) and some +//! unices this has changed to a more-ordered system where the ioctl numbers are partitioned into +//! subcomponents (For linux this is documented in +//! [`Documentation/ioctl/ioctl-number.rst`](https://elixir.bootlin.com/linux/latest/source/Documentation/userspace-api/ioctl/ioctl-number.rst)): +//! +//! * Number: The actual ioctl ID +//! * Type: A grouping of ioctls for a common purpose or driver +//! * Size: The size in bytes of the data that will be transferred +//! * Direction: Whether there is any data and if it's read, write, or both +//! +//! Newer drivers should not generate complete integer identifiers for their `ioctl`s instead +//! preferring to use the 4 components above to generate the final ioctl identifier. Because of +//! how old `ioctl`s are, however, there are many hard-coded `ioctl` identifiers. These are +//! commonly referred to as "bad" in `ioctl` documentation. +//! +//! Defining `ioctl`s +//! ================= +//! +//! This library provides several `ioctl_*!` macros for binding `ioctl`s. These generate public +//! unsafe functions that can then be used for calling the ioctl. This macro has a few different +//! ways it can be used depending on the specific ioctl you're working with. +//! +//! A simple `ioctl` is `SPI_IOC_RD_MODE`. This ioctl works with the SPI interface on Linux. This +//! specific `ioctl` reads the mode of the SPI device as a `u8`. It's declared in +//! `/include/uapi/linux/spi/spidev.h` as `_IOR(SPI_IOC_MAGIC, 1, __u8)`. Since it uses the `_IOR` +//! macro, we know it's a `read` ioctl and can use the `ioctl_read!` macro as follows: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! const SPI_IOC_TYPE_MODE: u8 = 1; +//! ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8); +//! # fn main() {} +//! ``` +//! +//! This generates the function: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! # use std::mem; +//! # use nix::{libc, Result}; +//! # use nix::errno::Errno; +//! # use nix::libc::c_int as c_int; +//! # const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! # const SPI_IOC_TYPE_MODE: u8 = 1; +//! pub unsafe fn spi_read_mode(fd: c_int, data: *mut u8) -> Result { +//! let res = libc::ioctl(fd, request_code_read!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::()), data); +//! Errno::result(res) +//! } +//! # fn main() {} +//! ``` +//! +//! The return value for the wrapper functions generated by the `ioctl_*!` macros are `nix::Error`s. +//! These are generated by assuming the return value of the ioctl is `-1` on error and everything +//! else is a valid return value. If this is not the case, `Result::map` can be used to map some +//! of the range of "good" values (-Inf..-2, 0..Inf) into a smaller range in a helper function. +//! +//! Writing `ioctl`s generally use pointers as their data source and these should use the +//! `ioctl_write_ptr!`. But in some cases an `int` is passed directly. For these `ioctl`s use the +//! `ioctl_write_int!` macro. This variant does not take a type as the last argument: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! const HCI_IOC_MAGIC: u8 = b'k'; +//! const HCI_IOC_HCIDEVUP: u8 = 1; +//! ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); +//! # fn main() {} +//! ``` +//! +//! Some `ioctl`s don't transfer any data, and those should use `ioctl_none!`. This macro +//! doesn't take a type and so it is declared similar to the `write_int` variant shown above. +//! +//! The mode for a given `ioctl` should be clear from the documentation if it has good +//! documentation. Otherwise it will be clear based on the macro used to generate the `ioctl` +//! number where `_IO`, `_IOR`, `_IOW`, and `_IOWR` map to "none", "read", "write_*", and "readwrite" +//! respectively. To determine the specific `write_` variant to use you'll need to find +//! what the argument type is supposed to be. If it's an `int`, then `write_int` should be used, +//! otherwise it should be a pointer and `write_ptr` should be used. On Linux the +//! [`ioctl_list` man page](https://man7.org/linux/man-pages/man2/ioctl_list.2.html) describes a +//! large number of `ioctl`s and describes their argument data type. +//! +//! Using "bad" `ioctl`s +//! -------------------- +//! +//! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of +//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the +//! `ioctl_*_bad!` macros. This naming comes from the Linux kernel which refers to these +//! `ioctl`s as "bad". These are a different variant as they bypass calling the macro that generates +//! the ioctl number and instead use the defined value directly. +//! +//! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor. +//! It's defined as `0x5401` in `ioctls.h` on Linux and can be implemented as: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! # use nix::libc::TCGETS as TCGETS; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! # use nix::libc::termios as termios; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! ioctl_read_bad!(tcgets, TCGETS, termios); +//! # fn main() {} +//! ``` +//! +//! The generated function has the same form as that generated by `ioctl_read!`: +//! +//! ```text +//! pub unsafe fn tcgets(fd: c_int, data: *mut termios) -> Result; +//! ``` +//! +//! Working with Arrays +//! ------------------- +//! +//! Some `ioctl`s work with entire arrays of elements. These are supported by the `ioctl_*_buf` +//! family of macros: `ioctl_read_buf`, `ioctl_write_buf`, and `ioctl_readwrite_buf`. Note that +//! there are no "bad" versions for working with buffers. The generated functions include a `len` +//! argument to specify the number of elements (where the type of each element is specified in the +//! macro). +//! +//! Again looking to the SPI `ioctl`s on Linux for an example, there is a `SPI_IOC_MESSAGE` `ioctl` +//! that queues up multiple SPI messages by writing an entire array of `spi_ioc_transfer` structs. +//! `linux/spi/spidev.h` defines a macro to calculate the `ioctl` number like: +//! +//! ```C +//! #define SPI_IOC_MAGIC 'k' +//! #define SPI_MSGSIZE(N) ... +//! #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) +//! ``` +//! +//! The `SPI_MSGSIZE(N)` calculation is already handled by the `ioctl_*!` macros, so all that's +//! needed to define this `ioctl` is: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! const SPI_IOC_TYPE_MESSAGE: u8 = 0; +//! # pub struct spi_ioc_transfer(u64); +//! ioctl_write_buf!(spi_transfer, SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, spi_ioc_transfer); +//! # fn main() {} +//! ``` +//! +//! This generates a function like: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! # use std::mem; +//! # use nix::{libc, Result}; +//! # use nix::errno::Errno; +//! # use nix::libc::c_int as c_int; +//! # const SPI_IOC_MAGIC: u8 = b'k'; +//! # const SPI_IOC_TYPE_MESSAGE: u8 = 0; +//! # pub struct spi_ioc_transfer(u64); +//! pub unsafe fn spi_message(fd: c_int, data: &mut [spi_ioc_transfer]) -> Result { +//! let res = libc::ioctl(fd, +//! request_code_write!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, data.len() * mem::size_of::()), +//! data); +//! Errno::result(res) +//! } +//! # fn main() {} +//! ``` +//! +//! Finding `ioctl` Documentation +//! ----------------------------- +//! +//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot +//! of lines defining macros which use `_IO`, `_IOR`, `_IOW`, `_IOC`, and `_IOWR`. Some `ioctl`s are +//! documented directly in the headers defining their constants, but others have more extensive +//! documentation in man pages (like termios' `ioctl`s which are in `tty_ioctl(4)`). +//! +//! Documenting the Generated Functions +//! =================================== +//! +//! In many cases, users will wish for the functions generated by the `ioctl` +//! macro to be public and documented. For this reason, the generated functions +//! are public by default. If you wish to hide the ioctl, you will need to put +//! them in a private module. +//! +//! For documentation, it is possible to use doc comments inside the `ioctl_*!` macros. Here is an +//! example : +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! # use nix::libc::c_int; +//! ioctl_read! { +//! /// Make the given terminal the controlling terminal of the calling process. The calling +//! /// process must be a session leader and not have a controlling terminal already. If the +//! /// terminal is already the controlling terminal of a different session group then the +//! /// ioctl will fail with **EPERM**, unless the caller is root (more precisely: has the +//! /// **CAP_SYS_ADMIN** capability) and arg equals 1, in which case the terminal is stolen +//! /// and all processes that had it as controlling terminal lose it. +//! tiocsctty, b't', 19, c_int +//! } +//! +//! # fn main() {} +//! ``` +use cfg_if::cfg_if; + +#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))] +#[macro_use] +mod linux; + +#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))] +pub use self::linux::*; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +#[macro_use] +mod bsd; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +pub use self::bsd::*; + +/// Convert raw ioctl return value to a Nix result +#[macro_export] +#[doc(hidden)] +macro_rules! convert_ioctl_res { + ($w:expr) => ( + { + $crate::errno::Errno::result($w) + } + ); +} + +/// Generates a wrapper function for an ioctl that passes no data to the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl identifier +/// * The ioctl sequence number +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +/// +/// # Example +/// +/// The `videodev2` driver on Linux defines the `log_status` `ioctl` as: +/// +/// ```C +/// #define VIDIOC_LOG_STATUS _IO('V', 70) +/// ``` +/// +/// This can be implemented in Rust like: +/// +/// ```no_run +/// # #[macro_use] extern crate nix; +/// ioctl_none!(log_status, b'V', 70); +/// fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! ioctl_none { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_none!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type)) + } + ) +} + +/// Generates a wrapper function for a "bad" ioctl that passes no data to the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl request code +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +/// +/// # Example +/// +/// ```no_run +/// # #[macro_use] extern crate nix; +/// # use libc::TIOCNXCL; +/// # use std::fs::File; +/// # use std::os::unix::io::AsRawFd; +/// ioctl_none_bad!(tiocnxcl, TIOCNXCL); +/// fn main() { +/// let file = File::open("/dev/ttyUSB0").unwrap(); +/// unsafe { tiocnxcl(file.as_raw_fd()) }.unwrap(); +/// } +/// ``` +// TODO: add an example using request_code_*!() +#[macro_export(local_inner_macros)] +macro_rules! ioctl_none_bad { + ($(#[$attr:meta])* $name:ident, $nr:expr) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type)) + } + ) +} + +/// Generates a wrapper function for an ioctl that reads data from the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl identifier +/// * The ioctl sequence number +/// * The data type passed by this ioctl +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +/// +/// # Example +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +/// const SPI_IOC_TYPE_MODE: u8 = 1; +/// ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8); +/// # fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! ioctl_read { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *mut $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +/// Generates a wrapper function for a "bad" ioctl that reads data from the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl request code +/// * The data type passed by this ioctl +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +/// +/// # Example +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// # #[cfg(any(target_os = "android", target_os = "linux"))] +/// ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios); +/// # fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! ioctl_read_bad { + ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *mut $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +/// Generates a wrapper function for an ioctl that writes data through a pointer to the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl identifier +/// * The ioctl sequence number +/// * The data type passed by this ioctl +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *const DATA_TYPE) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +/// +/// # Example +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// # pub struct v4l2_audio {} +/// ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio); +/// # fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! ioctl_write_ptr { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *const $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +/// Generates a wrapper function for a "bad" ioctl that writes data through a pointer to the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl request code +/// * The data type passed by this ioctl +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *const DATA_TYPE) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +/// +/// # Example +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// # #[cfg(any(target_os = "android", target_os = "linux"))] +/// ioctl_write_ptr_bad!(tcsets, libc::TCSETS, libc::termios); +/// # fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! ioctl_write_ptr_bad { + ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *const $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +cfg_if!{ + if #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] { + /// Generates a wrapper function for a ioctl that writes an integer to the kernel. + /// + /// The arguments to this macro are: + /// + /// * The function name + /// * The ioctl identifier + /// * The ioctl sequence number + /// + /// The generated function has the following signature: + /// + /// ```rust,ignore + /// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: nix::sys::ioctl::ioctl_param_type) -> Result + /// ``` + /// + /// `nix::sys::ioctl::ioctl_param_type` depends on the OS: + /// * BSD - `libc::c_int` + /// * Linux - `libc::c_ulong` + /// + /// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate nix; + /// ioctl_write_int!(vt_activate, b'v', 4); + /// # fn main() {} + /// ``` + #[macro_export(local_inner_macros)] + macro_rules! ioctl_write_int { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: $crate::sys::ioctl::ioctl_param_type) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write_int!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) + } + } else { + /// Generates a wrapper function for a ioctl that writes an integer to the kernel. + /// + /// The arguments to this macro are: + /// + /// * The function name + /// * The ioctl identifier + /// * The ioctl sequence number + /// + /// The generated function has the following signature: + /// + /// ```rust,ignore + /// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: nix::sys::ioctl::ioctl_param_type) -> Result + /// ``` + /// + /// `nix::sys::ioctl::ioctl_param_type` depends on the OS: + /// * BSD - `libc::c_int` + /// * Linux - `libc::c_ulong` + /// + /// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate nix; + /// const HCI_IOC_MAGIC: u8 = b'k'; + /// const HCI_IOC_HCIDEVUP: u8 = 1; + /// ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); + /// # fn main() {} + /// ``` + #[macro_export(local_inner_macros)] + macro_rules! ioctl_write_int { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: $crate::sys::ioctl::ioctl_param_type) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) + } + } +} + +/// Generates a wrapper function for a "bad" ioctl that writes an integer to the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl request code +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: libc::c_int) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// # #[cfg(any(target_os = "android", target_os = "linux"))] +/// ioctl_write_int_bad!(tcsbrk, libc::TCSBRK); +/// # fn main() {} +/// ``` +/// +/// ```rust +/// # #[macro_use] extern crate nix; +/// const KVMIO: u8 = 0xAE; +/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03)); +/// # fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! ioctl_write_int_bad { + ($(#[$attr:meta])* $name:ident, $nr:expr) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +/// Generates a wrapper function for an ioctl that reads and writes data to the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl identifier +/// * The ioctl sequence number +/// * The data type passed by this ioctl +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +/// +/// # Example +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// # pub struct v4l2_audio {} +/// ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio); +/// # fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! ioctl_readwrite { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *mut $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +/// Generates a wrapper function for a "bad" ioctl that reads and writes data to the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl request code +/// * The data type passed by this ioctl +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +// TODO: Find an example for ioctl_readwrite_bad +#[macro_export(local_inner_macros)] +macro_rules! ioctl_readwrite_bad { + ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *mut $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +/// Generates a wrapper function for an ioctl that reads an array of elements from the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl identifier +/// * The ioctl sequence number +/// * The data type passed by this ioctl +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &mut [DATA_TYPE]) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +// TODO: Find an example for ioctl_read_buf +#[macro_export(local_inner_macros)] +macro_rules! ioctl_read_buf { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: &mut [$ty]) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +/// Generates a wrapper function for an ioctl that writes an array of elements to the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl identifier +/// * The ioctl sequence number +/// * The data type passed by this ioctl +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &[DATA_TYPE]) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +/// const SPI_IOC_TYPE_MESSAGE: u8 = 0; +/// # pub struct spi_ioc_transfer(u64); +/// ioctl_write_buf!(spi_transfer, SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, spi_ioc_transfer); +/// # fn main() {} +/// ``` +#[macro_export(local_inner_macros)] +macro_rules! ioctl_write_buf { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: &[$ty]) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +/// Generates a wrapper function for an ioctl that reads and writes an array of elements to the kernel. +/// +/// The arguments to this macro are: +/// +/// * The function name +/// * The ioctl identifier +/// * The ioctl sequence number +/// * The data type passed by this ioctl +/// +/// The generated function has the following signature: +/// +/// ```rust,ignore +/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &mut [DATA_TYPE]) -> Result +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +// TODO: Find an example for readwrite_buf +#[macro_export(local_inner_macros)] +macro_rules! ioctl_readwrite_buf { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int, + data: &mut [$ty]) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/memfd.rs b/vendor/nix-v0.23.1-patched/src/sys/memfd.rs new file mode 100644 index 000000000..0236eef63 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/memfd.rs @@ -0,0 +1,19 @@ +use std::os::unix::io::RawFd; +use crate::Result; +use crate::errno::Errno; +use std::ffi::CStr; + +libc_bitflags!( + pub struct MemFdCreateFlag: libc::c_uint { + MFD_CLOEXEC; + MFD_ALLOW_SEALING; + } +); + +pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result { + let res = unsafe { + libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags.bits()) + }; + + Errno::result(res).map(|r| r as RawFd) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/mman.rs b/vendor/nix-v0.23.1-patched/src/sys/mman.rs new file mode 100644 index 000000000..882a2b944 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/mman.rs @@ -0,0 +1,464 @@ +use crate::Result; +#[cfg(not(target_os = "android"))] +use crate::NixPath; +use crate::errno::Errno; +#[cfg(not(target_os = "android"))] +use crate::fcntl::OFlag; +use libc::{self, c_int, c_void, size_t, off_t}; +#[cfg(not(target_os = "android"))] +use crate::sys::stat::Mode; +use std::os::unix::io::RawFd; + +libc_bitflags!{ + /// Desired memory protection of a memory mapping. + pub struct ProtFlags: c_int { + /// Pages cannot be accessed. + PROT_NONE; + /// Pages can be read. + PROT_READ; + /// Pages can be written. + PROT_WRITE; + /// Pages can be executed + PROT_EXEC; + /// Apply protection up to the end of a mapping that grows upwards. + #[cfg(any(target_os = "android", target_os = "linux"))] + PROT_GROWSDOWN; + /// Apply protection down to the beginning of a mapping that grows downwards. + #[cfg(any(target_os = "android", target_os = "linux"))] + PROT_GROWSUP; + } +} + +libc_bitflags!{ + /// Additional parameters for `mmap()`. + pub struct MapFlags: c_int { + /// Compatibility flag. Ignored. + MAP_FILE; + /// Share this mapping. Mutually exclusive with `MAP_PRIVATE`. + MAP_SHARED; + /// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`. + MAP_PRIVATE; + /// Place the mapping at exactly the address specified in `addr`. + MAP_FIXED; + /// To be used with `MAP_FIXED`, to forbid the system + /// to select a different address than the one specified. + #[cfg(target_os = "freebsd")] + MAP_EXCL; + /// Synonym for `MAP_ANONYMOUS`. + MAP_ANON; + /// The mapping is not backed by any file. + MAP_ANONYMOUS; + /// Put the mapping into the first 2GB of the process address space. + #[cfg(any(all(any(target_os = "android", target_os = "linux"), + any(target_arch = "x86", target_arch = "x86_64")), + all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "x86_64")), + all(target_os = "freebsd", target_pointer_width = "64")))] + MAP_32BIT; + /// Used for stacks; indicates to the kernel that the mapping should extend downward in memory. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_GROWSDOWN; + /// Compatibility flag. Ignored. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_DENYWRITE; + /// Compatibility flag. Ignored. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_EXECUTABLE; + /// Mark the mmaped region to be locked in the same way as `mlock(2)`. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_LOCKED; + /// Do not reserve swap space for this mapping. + /// + /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD. + #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))] + MAP_NORESERVE; + /// Populate page tables for a mapping. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_POPULATE; + /// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_NONBLOCK; + /// Allocate the mapping using "huge pages." + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_HUGETLB; + /// Make use of 64KB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_64KB; + /// Make use of 512KB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_512KB; + /// Make use of 1MB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_1MB; + /// Make use of 2MB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_2MB; + /// Make use of 8MB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_8MB; + /// Make use of 16MB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_16MB; + /// Make use of 32MB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_32MB; + /// Make use of 256MB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_256MB; + /// Make use of 512MB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_512MB; + /// Make use of 1GB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_1GB; + /// Make use of 2GB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_2GB; + /// Make use of 16GB huge page (must be supported by the system) + #[cfg(target_os = "linux")] + MAP_HUGE_16GB; + + /// Lock the mapped region into memory as with `mlock(2)`. + #[cfg(target_os = "netbsd")] + MAP_WIRED; + /// Causes dirtied data in the specified range to be flushed to disk only when necessary. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MAP_NOSYNC; + /// Rename private pages to a file. + /// + /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD. + #[cfg(any(target_os = "netbsd", target_os = "openbsd"))] + MAP_RENAME; + /// Region may contain semaphores. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] + MAP_HASSEMAPHORE; + /// Region grows down, like a stack. + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))] + MAP_STACK; + /// Pages in this mapping are not retained in the kernel's memory cache. + #[cfg(any(target_os = "ios", target_os = "macos"))] + MAP_NOCACHE; + /// Allows the W/X bit on the page, it's necessary on aarch64 architecture. + #[cfg(any(target_os = "ios", target_os = "macos"))] + MAP_JIT; + /// Allows to use large pages, underlying alignment based on size. + #[cfg(target_os = "freesd")] + MAP_ALIGNED_SUPER; + /// Pages will be discarded in the core dumps. + #[cfg(target_os = "openbsd")] + MAP_CONCEAL; + } +} + +#[cfg(any(target_os = "linux", target_os = "netbsd"))] +libc_bitflags!{ + /// Options for `mremap()`. + pub struct MRemapFlags: c_int { + /// Permit the kernel to relocate the mapping to a new virtual address, if necessary. + #[cfg(target_os = "linux")] + MREMAP_MAYMOVE; + /// Place the mapping at exactly the address specified in `new_address`. + #[cfg(target_os = "linux")] + MREMAP_FIXED; + /// Permits to use the old and new address as hints to relocate the mapping. + #[cfg(target_os = "netbsd")] + MAP_FIXED; + /// Allows to duplicate the mapping to be able to apply different flags on the copy. + #[cfg(target_os = "netbsd")] + MAP_REMAPDUP; + } +} + +libc_enum!{ + /// Usage information for a range of memory to allow for performance optimizations by the kernel. + /// + /// Used by [`madvise`](./fn.madvise.html). + #[repr(i32)] + #[non_exhaustive] + pub enum MmapAdvise { + /// No further special treatment. This is the default. + MADV_NORMAL, + /// Expect random page references. + MADV_RANDOM, + /// Expect sequential page references. + MADV_SEQUENTIAL, + /// Expect access in the near future. + MADV_WILLNEED, + /// Do not expect access in the near future. + MADV_DONTNEED, + /// Free up a given range of pages and its associated backing store. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_REMOVE, + /// Do not make pages in this range available to the child after a `fork(2)`. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_DONTFORK, + /// Undo the effect of `MADV_DONTFORK`. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_DOFORK, + /// Poison the given pages. + /// + /// Subsequent references to those pages are treated like hardware memory corruption. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_HWPOISON, + /// Enable Kernel Samepage Merging (KSM) for the given pages. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_MERGEABLE, + /// Undo the effect of `MADV_MERGEABLE` + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_UNMERGEABLE, + /// Preserve the memory of each page but offline the original page. + #[cfg(any(target_os = "android", + all(target_os = "linux", any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "x86", + target_arch = "x86_64", + target_arch = "sparc64"))))] + MADV_SOFT_OFFLINE, + /// Enable Transparent Huge Pages (THP) for pages in the given range. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_HUGEPAGE, + /// Undo the effect of `MADV_HUGEPAGE`. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_NOHUGEPAGE, + /// Exclude the given range from a core dump. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_DONTDUMP, + /// Undo the effect of an earlier `MADV_DONTDUMP`. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_DODUMP, + /// Specify that the application no longer needs the pages in the given range. + MADV_FREE, + /// Request that the system not flush the current range to disk unless it needs to. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MADV_NOSYNC, + /// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MADV_AUTOSYNC, + /// Region is not included in a core file. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MADV_NOCORE, + /// Include region in a core file + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MADV_CORE, + #[cfg(any(target_os = "freebsd"))] + MADV_PROTECT, + /// Invalidate the hardware page table for the given region. + #[cfg(target_os = "dragonfly")] + MADV_INVAL, + /// Set the offset of the page directory page to `value` for the virtual page table. + #[cfg(target_os = "dragonfly")] + MADV_SETMAP, + /// Indicates that the application will not need the data in the given range. + #[cfg(any(target_os = "ios", target_os = "macos"))] + MADV_ZERO_WIRED_PAGES, + #[cfg(any(target_os = "ios", target_os = "macos"))] + MADV_FREE_REUSABLE, + #[cfg(any(target_os = "ios", target_os = "macos"))] + MADV_FREE_REUSE, + #[cfg(any(target_os = "ios", target_os = "macos"))] + MADV_CAN_REUSE, + } +} + +libc_bitflags!{ + /// Configuration flags for `msync`. + pub struct MsFlags: c_int { + /// Schedule an update but return immediately. + MS_ASYNC; + /// Invalidate all cached data. + MS_INVALIDATE; + /// Invalidate pages, but leave them mapped. + #[cfg(any(target_os = "ios", target_os = "macos"))] + MS_KILLPAGES; + /// Deactivate pages, but leave them mapped. + #[cfg(any(target_os = "ios", target_os = "macos"))] + MS_DEACTIVATE; + /// Perform an update and wait for it to complete. + MS_SYNC; + } +} + +libc_bitflags!{ + /// Flags for `mlockall`. + pub struct MlockAllFlags: c_int { + /// Lock pages that are currently mapped into the address space of the process. + MCL_CURRENT; + /// Lock pages which will become mapped into the address space of the process in the future. + MCL_FUTURE; + } +} + +/// Locks all memory pages that contain part of the address range with `length` +/// bytes starting at `addr`. +/// +/// Locked pages never move to the swap area. +/// +/// # Safety +/// +/// `addr` must meet all the requirements described in the `mlock(2)` man page. +pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> { + Errno::result(libc::mlock(addr, length)).map(drop) +} + +/// Unlocks all memory pages that contain part of the address range with +/// `length` bytes starting at `addr`. +/// +/// # Safety +/// +/// `addr` must meet all the requirements described in the `munlock(2)` man +/// page. +pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> { + Errno::result(libc::munlock(addr, length)).map(drop) +} + +/// Locks all memory pages mapped into this process' address space. +/// +/// Locked pages never move to the swap area. +/// +/// # Safety +/// +/// `addr` must meet all the requirements described in the `mlockall(2)` man +/// page. +pub fn mlockall(flags: MlockAllFlags) -> Result<()> { + unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop) +} + +/// Unlocks all memory pages mapped into this process' address space. +pub fn munlockall() -> Result<()> { + unsafe { Errno::result(libc::munlockall()) }.map(drop) +} + +/// allocate memory, or map files or devices into memory +/// +/// # Safety +/// +/// See the `mmap(2)` man page for detailed requirements. +pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> { + let ret = libc::mmap(addr, length, prot.bits(), flags.bits(), fd, offset); + + if ret == libc::MAP_FAILED { + Err(Errno::last()) + } else { + Ok(ret) + } +} + +/// Expands (or shrinks) an existing memory mapping, potentially moving it at +/// the same time. +/// +/// # Safety +/// +/// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for +/// detailed requirements. +#[cfg(any(target_os = "linux", target_os = "netbsd"))] +pub unsafe fn mremap( + addr: *mut c_void, + old_size: size_t, + new_size: size_t, + flags: MRemapFlags, + new_address: Option<* mut c_void>, +) -> Result<*mut c_void> { + #[cfg(target_os = "linux")] + let ret = libc::mremap(addr, old_size, new_size, flags.bits(), new_address.unwrap_or(std::ptr::null_mut())); + #[cfg(target_os = "netbsd")] + let ret = libc::mremap( + addr, + old_size, + new_address.unwrap_or(std::ptr::null_mut()), + new_size, + flags.bits(), + ); + + if ret == libc::MAP_FAILED { + Err(Errno::last()) + } else { + Ok(ret) + } +} + +/// remove a mapping +/// +/// # Safety +/// +/// `addr` must meet all the requirements described in the `munmap(2)` man +/// page. +pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> { + Errno::result(libc::munmap(addr, len)).map(drop) +} + +/// give advice about use of memory +/// +/// # Safety +/// +/// See the `madvise(2)` man page. Take special care when using +/// `MmapAdvise::MADV_FREE`. +pub unsafe fn madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()> { + Errno::result(libc::madvise(addr, length, advise as i32)).map(drop) +} + +/// Set protection of memory mapping. +/// +/// See [`mprotect(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html) for +/// details. +/// +/// # Safety +/// +/// Calls to `mprotect` are inherently unsafe, as changes to memory protections can lead to +/// SIGSEGVs. +/// +/// ``` +/// # use nix::libc::size_t; +/// # use nix::sys::mman::{mmap, mprotect, MapFlags, ProtFlags}; +/// # use std::ptr; +/// const ONE_K: size_t = 1024; +/// let mut slice: &mut [u8] = unsafe { +/// let mem = mmap(ptr::null_mut(), ONE_K, ProtFlags::PROT_NONE, +/// MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE, -1, 0).unwrap(); +/// mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap(); +/// std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K) +/// }; +/// assert_eq!(slice[0], 0x00); +/// slice[0] = 0xFF; +/// assert_eq!(slice[0], 0xFF); +/// ``` +pub unsafe fn mprotect(addr: *mut c_void, length: size_t, prot: ProtFlags) -> Result<()> { + Errno::result(libc::mprotect(addr, length, prot.bits())).map(drop) +} + +/// synchronize a mapped region +/// +/// # Safety +/// +/// `addr` must meet all the requirements described in the `msync(2)` man +/// page. +pub unsafe fn msync(addr: *mut c_void, length: size_t, flags: MsFlags) -> Result<()> { + Errno::result(libc::msync(addr, length, flags.bits())).map(drop) +} + +#[cfg(not(target_os = "android"))] +pub fn shm_open(name: &P, flag: OFlag, mode: Mode) -> Result { + let ret = name.with_nix_path(|cstr| { + #[cfg(any(target_os = "macos", target_os = "ios"))] + unsafe { + libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint) + } + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + unsafe { + libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t) + } + })?; + + Errno::result(ret) +} + +#[cfg(not(target_os = "android"))] +pub fn shm_unlink(name: &P) -> Result<()> { + let ret = name.with_nix_path(|cstr| { + unsafe { libc::shm_unlink(cstr.as_ptr()) } + })?; + + Errno::result(ret).map(drop) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/mod.rs b/vendor/nix-v0.23.1-patched/src/sys/mod.rs new file mode 100644 index 000000000..a87de55b3 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/mod.rs @@ -0,0 +1,131 @@ +//! Mostly platform-specific functionality +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd"))] +pub mod aio; + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[allow(missing_docs)] +pub mod epoll; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +#[allow(missing_docs)] +pub mod event; + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[allow(missing_docs)] +pub mod eventfd; + +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "redox", + target_os = "macos", + target_os = "netbsd", + target_os = "illumos", + target_os = "openbsd"))] +#[macro_use] +pub mod ioctl; + +#[cfg(target_os = "linux")] +#[allow(missing_docs)] +pub mod memfd; + +#[cfg(not(target_os = "redox"))] +#[allow(missing_docs)] +pub mod mman; + +#[cfg(target_os = "linux")] +#[allow(missing_docs)] +pub mod personality; + +pub mod pthread; + +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +#[allow(missing_docs)] +pub mod ptrace; + +#[cfg(target_os = "linux")] +pub mod quota; + +#[cfg(any(target_os = "linux"))] +#[allow(missing_docs)] +pub mod reboot; + +#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))] +pub mod resource; + +#[cfg(not(target_os = "redox"))] +pub mod select; + +#[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] +pub mod sendfile; + +pub mod signal; + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[allow(missing_docs)] +pub mod signalfd; + +#[cfg(not(target_os = "redox"))] +#[allow(missing_docs)] +pub mod socket; + +#[allow(missing_docs)] +pub mod stat; + +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" +))] +pub mod statfs; + +pub mod statvfs; + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[allow(missing_docs)] +pub mod sysinfo; + +#[allow(missing_docs)] +pub mod termios; + +#[allow(missing_docs)] +pub mod time; + +pub mod uio; + +pub mod utsname; + +pub mod wait; + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[allow(missing_docs)] +pub mod inotify; + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[allow(missing_docs)] +pub mod timerfd; diff --git a/vendor/nix-v0.23.1-patched/src/sys/personality.rs b/vendor/nix-v0.23.1-patched/src/sys/personality.rs new file mode 100644 index 000000000..b15956c46 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/personality.rs @@ -0,0 +1,70 @@ +use crate::Result; +use crate::errno::Errno; + +use libc::{self, c_int, c_ulong}; + +libc_bitflags! { + /// Flags used and returned by [`get()`](fn.get.html) and + /// [`set()`](fn.set.html). + pub struct Persona: c_int { + ADDR_COMPAT_LAYOUT; + ADDR_NO_RANDOMIZE; + ADDR_LIMIT_32BIT; + ADDR_LIMIT_3GB; + #[cfg(not(target_env = "musl"))] + FDPIC_FUNCPTRS; + MMAP_PAGE_ZERO; + READ_IMPLIES_EXEC; + SHORT_INODE; + STICKY_TIMEOUTS; + #[cfg(not(target_env = "musl"))] + UNAME26; + WHOLE_SECONDS; + } +} + +/// Retrieve the current process personality. +/// +/// Returns a Result containing a Persona instance. +/// +/// Example: +/// +/// ``` +/// # use nix::sys::personality::{self, Persona}; +/// let pers = personality::get().unwrap(); +/// assert!(!pers.contains(Persona::WHOLE_SECONDS)); +/// ``` +pub fn get() -> Result { + let res = unsafe { + libc::personality(0xFFFFFFFF) + }; + + Errno::result(res).map(Persona::from_bits_truncate) +} + +/// Set the current process personality. +/// +/// Returns a Result containing the *previous* personality for the +/// process, as a Persona. +/// +/// For more information, see [personality(2)](https://man7.org/linux/man-pages/man2/personality.2.html) +/// +/// **NOTE**: This call **replaces** the current personality entirely. +/// To **update** the personality, first call `get()` and then `set()` +/// with the modified persona. +/// +/// Example: +/// +/// ``` +/// # use nix::sys::personality::{self, Persona}; +/// let mut pers = personality::get().unwrap(); +/// assert!(!pers.contains(Persona::ADDR_NO_RANDOMIZE)); +/// personality::set(pers | Persona::ADDR_NO_RANDOMIZE); +/// ``` +pub fn set(persona: Persona) -> Result { + let res = unsafe { + libc::personality(persona.bits() as c_ulong) + }; + + Errno::result(res).map(Persona::from_bits_truncate) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/pthread.rs b/vendor/nix-v0.23.1-patched/src/sys/pthread.rs new file mode 100644 index 000000000..d42e45d13 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/pthread.rs @@ -0,0 +1,38 @@ +//! Low level threading primitives + +#[cfg(not(target_os = "redox"))] +use crate::errno::Errno; +#[cfg(not(target_os = "redox"))] +use crate::Result; +#[cfg(not(target_os = "redox"))] +use crate::sys::signal::Signal; +use libc::{self, pthread_t}; + +/// Identifies an individual thread. +pub type Pthread = pthread_t; + +/// Obtain ID of the calling thread (see +/// [`pthread_self(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_self.html) +/// +/// The thread ID returned by `pthread_self()` is not the same thing as +/// the kernel thread ID returned by a call to `gettid(2)`. +#[inline] +pub fn pthread_self() -> Pthread { + unsafe { libc::pthread_self() } +} + +/// Send a signal to a thread (see [`pthread_kill(3)`]). +/// +/// If `signal` is `None`, `pthread_kill` will only preform error checking and +/// won't send any signal. +/// +/// [`pthread_kill(3)`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html +#[cfg(not(target_os = "redox"))] +pub fn pthread_kill>>(thread: Pthread, signal: T) -> Result<()> { + let sig = match signal.into() { + Some(s) => s as libc::c_int, + None => 0, + }; + let res = unsafe { libc::pthread_kill(thread, sig) }; + Errno::result(res).map(drop) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/ptrace/bsd.rs b/vendor/nix-v0.23.1-patched/src/sys/ptrace/bsd.rs new file mode 100644 index 000000000..a62881ef3 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/ptrace/bsd.rs @@ -0,0 +1,176 @@ +use cfg_if::cfg_if; +use crate::errno::Errno; +use libc::{self, c_int}; +use std::ptr; +use crate::sys::signal::Signal; +use crate::unistd::Pid; +use crate::Result; + +pub type RequestType = c_int; + +cfg_if! { + if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "openbsd"))] { + #[doc(hidden)] + pub type AddressType = *mut ::libc::c_char; + } else { + #[doc(hidden)] + pub type AddressType = *mut ::libc::c_void; + } +} + +libc_enum! { + #[repr(i32)] + /// Ptrace Request enum defining the action to be taken. + #[non_exhaustive] + pub enum Request { + PT_TRACE_ME, + PT_READ_I, + PT_READ_D, + #[cfg(target_os = "macos")] + PT_READ_U, + PT_WRITE_I, + PT_WRITE_D, + #[cfg(target_os = "macos")] + PT_WRITE_U, + PT_CONTINUE, + PT_KILL, + #[cfg(any(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos"), + all(target_os = "openbsd", target_arch = "x86_64"), + all(target_os = "netbsd", any(target_arch = "x86_64", + target_arch = "powerpc"))))] + PT_STEP, + PT_ATTACH, + PT_DETACH, + #[cfg(target_os = "macos")] + PT_SIGEXC, + #[cfg(target_os = "macos")] + PT_THUPDATE, + #[cfg(target_os = "macos")] + PT_ATTACHEXC + } +} + +unsafe fn ptrace_other( + request: Request, + pid: Pid, + addr: AddressType, + data: c_int, +) -> Result { + Errno::result(libc::ptrace( + request as RequestType, + libc::pid_t::from(pid), + addr, + data, + )).map(|_| 0) +} + +/// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)` +/// +/// Indicates that this process is to be traced by its parent. +/// This is the only ptrace request to be issued by the tracee. +pub fn traceme() -> Result<()> { + unsafe { ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0).map(drop) } +} + +/// Attach to a running process, as with `ptrace(PT_ATTACH, ...)` +/// +/// Attaches to the process specified by `pid`, making it a tracee of the calling process. +pub fn attach(pid: Pid) -> Result<()> { + unsafe { ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop) } +} + +/// Detaches the current running process, as with `ptrace(PT_DETACH, ...)` +/// +/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a +/// signal specified by `sig`. +pub fn detach>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as c_int, + None => 0, + }; + unsafe { + ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), data).map(drop) + } +} + +/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)` +/// +/// Continues the execution of the process with PID `pid`, optionally +/// delivering a signal specified by `sig`. +pub fn cont>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as c_int, + None => 0, + }; + unsafe { + // Ignore the useless return value + ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data).map(drop) + } +} + +/// Issues a kill request as with `ptrace(PT_KILL, ...)` +/// +/// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);` +pub fn kill(pid: Pid) -> Result<()> { + unsafe { + ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop) + } +} + +/// Move the stopped tracee process forward by a single step as with +/// `ptrace(PT_STEP, ...)` +/// +/// Advances the execution of the process with PID `pid` by a single step optionally delivering a +/// signal specified by `sig`. +/// +/// # Example +/// ```rust +/// use nix::sys::ptrace::step; +/// use nix::unistd::Pid; +/// use nix::sys::signal::Signal; +/// use nix::sys::wait::*; +/// // If a process changes state to the stopped state because of a SIGUSR1 +/// // signal, this will step the process forward and forward the user +/// // signal to the stopped process +/// match waitpid(Pid::from_raw(-1), None) { +/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => { +/// let _ = step(pid, Signal::SIGUSR1); +/// } +/// _ => {}, +/// } +/// ``` +#[cfg( + any( + any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"), + all(target_os = "openbsd", target_arch = "x86_64"), + all(target_os = "netbsd", + any(target_arch = "x86_64", target_arch = "powerpc") + ) + ) +)] +pub fn step>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as c_int, + None => 0, + }; + unsafe { ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop) } +} + +/// Reads a word from a processes memory at the given address +pub fn read(pid: Pid, addr: AddressType) -> Result { + unsafe { + // Traditionally there was a difference between reading data or + // instruction memory but not in modern systems. + ptrace_other(Request::PT_READ_D, pid, addr, 0) + } +} + +/// Writes a word into the processes memory at the given address +pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> { + unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/ptrace/linux.rs b/vendor/nix-v0.23.1-patched/src/sys/ptrace/linux.rs new file mode 100644 index 000000000..37236790b --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/ptrace/linux.rs @@ -0,0 +1,479 @@ +//! For detailed description of the ptrace requests, consult `man ptrace`. + +use cfg_if::cfg_if; +use std::{mem, ptr}; +use crate::Result; +use crate::errno::Errno; +use libc::{self, c_void, c_long, siginfo_t}; +use crate::unistd::Pid; +use crate::sys::signal::Signal; + +pub type AddressType = *mut ::libc::c_void; + +#[cfg(all( + target_os = "linux", + any(all(target_arch = "x86_64", + any(target_env = "gnu", target_env = "musl")), + all(target_arch = "x86", target_env = "gnu")) +))] +use libc::user_regs_struct; + +cfg_if! { + if #[cfg(any(all(target_os = "linux", target_arch = "s390x"), + all(target_os = "linux", target_env = "gnu")))] { + #[doc(hidden)] + pub type RequestType = ::libc::c_uint; + } else { + #[doc(hidden)] + pub type RequestType = ::libc::c_int; + } +} + +libc_enum!{ + #[cfg_attr(not(any(target_env = "musl", target_os = "android")), repr(u32))] + #[cfg_attr(any(target_env = "musl", target_os = "android"), repr(i32))] + /// Ptrace Request enum defining the action to be taken. + #[non_exhaustive] + pub enum Request { + PTRACE_TRACEME, + PTRACE_PEEKTEXT, + PTRACE_PEEKDATA, + PTRACE_PEEKUSER, + PTRACE_POKETEXT, + PTRACE_POKEDATA, + PTRACE_POKEUSER, + PTRACE_CONT, + PTRACE_KILL, + PTRACE_SINGLESTEP, + #[cfg(any(all(target_os = "android", target_pointer_width = "32"), + all(target_os = "linux", any(target_env = "musl", + target_arch = "mips", + target_arch = "mips64", + target_arch = "x86_64", + target_pointer_width = "32"))))] + PTRACE_GETREGS, + #[cfg(any(all(target_os = "android", target_pointer_width = "32"), + all(target_os = "linux", any(target_env = "musl", + target_arch = "mips", + target_arch = "mips64", + target_arch = "x86_64", + target_pointer_width = "32"))))] + PTRACE_SETREGS, + #[cfg(any(all(target_os = "android", target_pointer_width = "32"), + all(target_os = "linux", any(target_env = "musl", + target_arch = "mips", + target_arch = "mips64", + target_arch = "x86_64", + target_pointer_width = "32"))))] + PTRACE_GETFPREGS, + #[cfg(any(all(target_os = "android", target_pointer_width = "32"), + all(target_os = "linux", any(target_env = "musl", + target_arch = "mips", + target_arch = "mips64", + target_arch = "x86_64", + target_pointer_width = "32"))))] + PTRACE_SETFPREGS, + PTRACE_ATTACH, + PTRACE_DETACH, + #[cfg(all(target_os = "linux", any(target_env = "musl", + target_arch = "mips", + target_arch = "mips64", + target_arch = "x86", + target_arch = "x86_64")))] + PTRACE_GETFPXREGS, + #[cfg(all(target_os = "linux", any(target_env = "musl", + target_arch = "mips", + target_arch = "mips64", + target_arch = "x86", + target_arch = "x86_64")))] + PTRACE_SETFPXREGS, + PTRACE_SYSCALL, + PTRACE_SETOPTIONS, + PTRACE_GETEVENTMSG, + PTRACE_GETSIGINFO, + PTRACE_SETSIGINFO, + #[cfg(all(target_os = "linux", not(any(target_arch = "mips", + target_arch = "mips64"))))] + PTRACE_GETREGSET, + #[cfg(all(target_os = "linux", not(any(target_arch = "mips", + target_arch = "mips64"))))] + PTRACE_SETREGSET, + #[cfg(target_os = "linux")] + PTRACE_SEIZE, + #[cfg(target_os = "linux")] + PTRACE_INTERRUPT, + #[cfg(all(target_os = "linux", not(any(target_arch = "mips", + target_arch = "mips64"))))] + PTRACE_LISTEN, + #[cfg(all(target_os = "linux", not(any(target_arch = "mips", + target_arch = "mips64"))))] + PTRACE_PEEKSIGINFO, + #[cfg(all(target_os = "linux", target_env = "gnu", + any(target_arch = "x86", target_arch = "x86_64")))] + PTRACE_SYSEMU, + #[cfg(all(target_os = "linux", target_env = "gnu", + any(target_arch = "x86", target_arch = "x86_64")))] + PTRACE_SYSEMU_SINGLESTEP, + } +} + +libc_enum!{ + #[repr(i32)] + /// Using the ptrace options the tracer can configure the tracee to stop + /// at certain events. This enum is used to define those events as defined + /// in `man ptrace`. + #[non_exhaustive] + pub enum Event { + /// Event that stops before a return from fork or clone. + PTRACE_EVENT_FORK, + /// Event that stops before a return from vfork or clone. + PTRACE_EVENT_VFORK, + /// Event that stops before a return from clone. + PTRACE_EVENT_CLONE, + /// Event that stops before a return from execve. + PTRACE_EVENT_EXEC, + /// Event for a return from vfork. + PTRACE_EVENT_VFORK_DONE, + /// Event for a stop before an exit. Unlike the waitpid Exit status program. + /// registers can still be examined + PTRACE_EVENT_EXIT, + /// Stop triggered by a seccomp rule on a tracee. + PTRACE_EVENT_SECCOMP, + /// Stop triggered by the `INTERRUPT` syscall, or a group stop, + /// or when a new child is attached. + PTRACE_EVENT_STOP, + } +} + +libc_bitflags! { + /// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request. + /// See `man ptrace` for more details. + pub struct Options: libc::c_int { + /// When delivering system call traps set a bit to allow tracer to + /// distinguish between normal stops or syscall stops. May not work on + /// all systems. + PTRACE_O_TRACESYSGOOD; + /// Stop tracee at next fork and start tracing the forked process. + PTRACE_O_TRACEFORK; + /// Stop tracee at next vfork call and trace the vforked process. + PTRACE_O_TRACEVFORK; + /// Stop tracee at next clone call and trace the cloned process. + PTRACE_O_TRACECLONE; + /// Stop tracee at next execve call. + PTRACE_O_TRACEEXEC; + /// Stop tracee at vfork completion. + PTRACE_O_TRACEVFORKDONE; + /// Stop tracee at next exit call. Stops before exit commences allowing + /// tracer to see location of exit and register states. + PTRACE_O_TRACEEXIT; + /// Stop tracee when a SECCOMP_RET_TRACE rule is triggered. See `man seccomp` for more + /// details. + PTRACE_O_TRACESECCOMP; + /// Send a SIGKILL to the tracee if the tracer exits. This is useful + /// for ptrace jailers to prevent tracees from escaping their control. + #[cfg(any(target_os = "android", target_os = "linux"))] + PTRACE_O_EXITKILL; + } +} + +fn ptrace_peek(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result { + let ret = unsafe { + Errno::clear(); + libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data) + }; + match Errno::result(ret) { + Ok(..) | Err(Errno::UnknownErrno) => Ok(ret), + err @ Err(..) => err, + } +} + +/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)` +#[cfg(all( + target_os = "linux", + any(all(target_arch = "x86_64", + any(target_env = "gnu", target_env = "musl")), + all(target_arch = "x86", target_env = "gnu")) +))] +pub fn getregs(pid: Pid) -> Result { + ptrace_get_data::(Request::PTRACE_GETREGS, pid) +} + +/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)` +#[cfg(all( + target_os = "linux", + any(all(target_arch = "x86_64", + any(target_env = "gnu", target_env = "musl")), + all(target_arch = "x86", target_env = "gnu")) +))] +pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> { + let res = unsafe { + libc::ptrace(Request::PTRACE_SETREGS as RequestType, + libc::pid_t::from(pid), + ptr::null_mut::(), + ®s as *const _ as *const c_void) + }; + Errno::result(res).map(drop) +} + +/// Function for ptrace requests that return values from the data field. +/// Some ptrace get requests populate structs or larger elements than `c_long` +/// and therefore use the data field to return values. This function handles these +/// requests. +fn ptrace_get_data(request: Request, pid: Pid) -> Result { + let mut data = mem::MaybeUninit::uninit(); + let res = unsafe { + libc::ptrace(request as RequestType, + libc::pid_t::from(pid), + ptr::null_mut::(), + data.as_mut_ptr() as *const _ as *const c_void) + }; + Errno::result(res)?; + Ok(unsafe{ data.assume_init() }) +} + +unsafe fn ptrace_other(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result { + Errno::result(libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)).map(|_| 0) +} + +/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`. +pub fn setoptions(pid: Pid, options: Options) -> Result<()> { + let res = unsafe { + libc::ptrace(Request::PTRACE_SETOPTIONS as RequestType, + libc::pid_t::from(pid), + ptr::null_mut::(), + options.bits() as *mut c_void) + }; + Errno::result(res).map(drop) +} + +/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)` +pub fn getevent(pid: Pid) -> Result { + ptrace_get_data::(Request::PTRACE_GETEVENTMSG, pid) +} + +/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)` +pub fn getsiginfo(pid: Pid) -> Result { + ptrace_get_data::(Request::PTRACE_GETSIGINFO, pid) +} + +/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)` +pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> { + let ret = unsafe{ + Errno::clear(); + libc::ptrace(Request::PTRACE_SETSIGINFO as RequestType, + libc::pid_t::from(pid), + ptr::null_mut::(), + sig as *const _ as *const c_void) + }; + match Errno::result(ret) { + Ok(_) => Ok(()), + Err(e) => Err(e), + } +} + +/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)` +/// +/// Indicates that this process is to be traced by its parent. +/// This is the only ptrace request to be issued by the tracee. +pub fn traceme() -> Result<()> { + unsafe { + ptrace_other( + Request::PTRACE_TRACEME, + Pid::from_raw(0), + ptr::null_mut(), + ptr::null_mut(), + ).map(drop) // ignore the useless return value + } +} + +/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSCALL, ...)` +/// +/// Arranges for the tracee to be stopped at the next entry to or exit from a system call, +/// optionally delivering a signal specified by `sig`. +pub fn syscall>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as i32 as *mut c_void, + None => ptr::null_mut(), + }; + unsafe { + ptrace_other( + Request::PTRACE_SYSCALL, + pid, + ptr::null_mut(), + data, + ).map(drop) // ignore the useless return value + } +} + +/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSEMU, ...)` +/// +/// In contrast to the `syscall` function, the syscall stopped at will not be executed. +/// Thus the the tracee will only be stopped once per syscall, +/// optionally delivering a signal specified by `sig`. +#[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))] +pub fn sysemu>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as i32 as *mut c_void, + None => ptr::null_mut(), + }; + unsafe { + ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data).map(drop) + // ignore the useless return value + } +} + +/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)` +/// +/// Attaches to the process specified by `pid`, making it a tracee of the calling process. +pub fn attach(pid: Pid) -> Result<()> { + unsafe { + ptrace_other( + Request::PTRACE_ATTACH, + pid, + ptr::null_mut(), + ptr::null_mut(), + ).map(drop) // ignore the useless return value + } +} + +/// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)` +/// +/// Attaches to the process specified in pid, making it a tracee of the calling process. +#[cfg(target_os = "linux")] +pub fn seize(pid: Pid, options: Options) -> Result<()> { + unsafe { + ptrace_other( + Request::PTRACE_SEIZE, + pid, + ptr::null_mut(), + options.bits() as *mut c_void, + ).map(drop) // ignore the useless return value + } +} + +/// Detaches the current running process, as with `ptrace(PTRACE_DETACH, ...)` +/// +/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a +/// signal specified by `sig`. +pub fn detach>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as i32 as *mut c_void, + None => ptr::null_mut(), + }; + unsafe { + ptrace_other( + Request::PTRACE_DETACH, + pid, + ptr::null_mut(), + data + ).map(drop) + } +} + +/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)` +/// +/// Continues the execution of the process with PID `pid`, optionally +/// delivering a signal specified by `sig`. +pub fn cont>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as i32 as *mut c_void, + None => ptr::null_mut(), + }; + unsafe { + ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop) // ignore the useless return value + } +} + +/// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)` +/// +/// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)` +#[cfg(target_os = "linux")] +pub fn interrupt(pid: Pid) -> Result<()> { + unsafe { + ptrace_other(Request::PTRACE_INTERRUPT, pid, ptr::null_mut(), ptr::null_mut()).map(drop) + } +} + +/// Issues a kill request as with `ptrace(PTRACE_KILL, ...)` +/// +/// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);` +pub fn kill(pid: Pid) -> Result<()> { + unsafe { + ptrace_other(Request::PTRACE_KILL, pid, ptr::null_mut(), ptr::null_mut()).map(drop) + } +} + +/// Move the stopped tracee process forward by a single step as with +/// `ptrace(PTRACE_SINGLESTEP, ...)` +/// +/// Advances the execution of the process with PID `pid` by a single step optionally delivering a +/// signal specified by `sig`. +/// +/// # Example +/// ```rust +/// use nix::sys::ptrace::step; +/// use nix::unistd::Pid; +/// use nix::sys::signal::Signal; +/// use nix::sys::wait::*; +/// +/// // If a process changes state to the stopped state because of a SIGUSR1 +/// // signal, this will step the process forward and forward the user +/// // signal to the stopped process +/// match waitpid(Pid::from_raw(-1), None) { +/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => { +/// let _ = step(pid, Signal::SIGUSR1); +/// } +/// _ => {}, +/// } +/// ``` +pub fn step>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as i32 as *mut c_void, + None => ptr::null_mut(), + }; + unsafe { + ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data).map(drop) + } +} + +/// Move the stopped tracee process forward by a single step or stop at the next syscall +/// as with `ptrace(PTRACE_SYSEMU_SINGLESTEP, ...)` +/// +/// Advances the execution by a single step or until the next syscall. +/// In case the tracee is stopped at a syscall, the syscall will not be executed. +/// Optionally, the signal specified by `sig` is delivered to the tracee upon continuation. +#[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))] +pub fn sysemu_step>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as i32 as *mut c_void, + None => ptr::null_mut(), + }; + unsafe { + ptrace_other( + Request::PTRACE_SYSEMU_SINGLESTEP, + pid, + ptr::null_mut(), + data, + ) + .map(drop) // ignore the useless return value + } +} + +/// Reads a word from a processes memory at the given address +pub fn read(pid: Pid, addr: AddressType) -> Result { + ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut()) +} + +/// Writes a word into the processes memory at the given address +/// +/// # Safety +/// +/// The `data` argument is passed directly to `ptrace(2)`. Read that man page +/// for guidance. +pub unsafe fn write( + pid: Pid, + addr: AddressType, + data: *mut c_void) -> Result<()> +{ + ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/ptrace/mod.rs b/vendor/nix-v0.23.1-patched/src/sys/ptrace/mod.rs new file mode 100644 index 000000000..782c30409 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/ptrace/mod.rs @@ -0,0 +1,22 @@ +///! Provides helpers for making ptrace system calls + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod linux; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::linux::*; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +mod bsd; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] +pub use self::bsd::*; diff --git a/vendor/nix-v0.23.1-patched/src/sys/quota.rs b/vendor/nix-v0.23.1-patched/src/sys/quota.rs new file mode 100644 index 000000000..6e34e38d2 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/quota.rs @@ -0,0 +1,277 @@ +//! Set and configure disk quotas for users, groups, or projects. +//! +//! # Examples +//! +//! Enabling and setting a quota: +//! +//! ```rust,no_run +//! # use nix::sys::quota::{Dqblk, quotactl_on, quotactl_set, QuotaFmt, QuotaType, QuotaValidFlags}; +//! quotactl_on(QuotaType::USRQUOTA, "/dev/sda1", QuotaFmt::QFMT_VFS_V1, "aquota.user"); +//! let mut dqblk: Dqblk = Default::default(); +//! dqblk.set_blocks_hard_limit(10000); +//! dqblk.set_blocks_soft_limit(8000); +//! quotactl_set(QuotaType::USRQUOTA, "/dev/sda1", 50, &dqblk, QuotaValidFlags::QIF_BLIMITS); +//! ``` +use std::default::Default; +use std::{mem, ptr}; +use libc::{self, c_int, c_char}; +use crate::{Result, NixPath}; +use crate::errno::Errno; + +struct QuotaCmd(QuotaSubCmd, QuotaType); + +impl QuotaCmd { + #[allow(unused_unsafe)] + fn as_int(&self) -> c_int { + unsafe { libc::QCMD(self.0 as i32, self.1 as i32) } + } +} + +// linux quota version >= 2 +libc_enum!{ + #[repr(i32)] + enum QuotaSubCmd { + Q_SYNC, + Q_QUOTAON, + Q_QUOTAOFF, + Q_GETQUOTA, + Q_SETQUOTA, + } +} + +libc_enum!{ + /// The scope of the quota. + #[repr(i32)] + #[non_exhaustive] + pub enum QuotaType { + /// Specify a user quota + USRQUOTA, + /// Specify a group quota + GRPQUOTA, + } +} + +libc_enum!{ + /// The type of quota format to use. + #[repr(i32)] + #[non_exhaustive] + pub enum QuotaFmt { + /// Use the original quota format. + QFMT_VFS_OLD, + /// Use the standard VFS v0 quota format. + /// + /// Handles 32-bit UIDs/GIDs and quota limits up to 232 bytes/232 inodes. + QFMT_VFS_V0, + /// Use the VFS v1 quota format. + /// + /// Handles 32-bit UIDs/GIDs and quota limits of 264 bytes/264 inodes. + QFMT_VFS_V1, + } +} + +libc_bitflags!( + /// Indicates the quota fields that are valid to read from. + #[derive(Default)] + pub struct QuotaValidFlags: u32 { + /// The block hard & soft limit fields. + QIF_BLIMITS; + /// The current space field. + QIF_SPACE; + /// The inode hard & soft limit fields. + QIF_ILIMITS; + /// The current inodes field. + QIF_INODES; + /// The disk use time limit field. + QIF_BTIME; + /// The file quote time limit field. + QIF_ITIME; + /// All block & inode limits. + QIF_LIMITS; + /// The space & inodes usage fields. + QIF_USAGE; + /// The time limit fields. + QIF_TIMES; + /// All fields. + QIF_ALL; + } +); + +/// Wrapper type for `if_dqblk` +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Dqblk(libc::dqblk); + +impl Default for Dqblk { + fn default() -> Dqblk { + Dqblk(libc::dqblk { + dqb_bhardlimit: 0, + dqb_bsoftlimit: 0, + dqb_curspace: 0, + dqb_ihardlimit: 0, + dqb_isoftlimit: 0, + dqb_curinodes: 0, + dqb_btime: 0, + dqb_itime: 0, + dqb_valid: 0, + }) + } +} + +impl Dqblk { + /// The absolute limit on disk quota blocks allocated. + pub fn blocks_hard_limit(&self) -> Option { + let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) { + Some(self.0.dqb_bhardlimit) + } else { + None + } + } + + /// Set the absolute limit on disk quota blocks allocated. + pub fn set_blocks_hard_limit(&mut self, limit: u64) { + self.0.dqb_bhardlimit = limit; + } + + /// Preferred limit on disk quota blocks + pub fn blocks_soft_limit(&self) -> Option { + let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) { + Some(self.0.dqb_bsoftlimit) + } else { + None + } + } + + /// Set the preferred limit on disk quota blocks allocated. + pub fn set_blocks_soft_limit(&mut self, limit: u64) { + self.0.dqb_bsoftlimit = limit; + } + + /// Current occupied space (bytes). + pub fn occupied_space(&self) -> Option { + let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_SPACE) { + Some(self.0.dqb_curspace) + } else { + None + } + } + + /// Maximum number of allocated inodes. + pub fn inodes_hard_limit(&self) -> Option { + let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) { + Some(self.0.dqb_ihardlimit) + } else { + None + } + } + + /// Set the maximum number of allocated inodes. + pub fn set_inodes_hard_limit(&mut self, limit: u64) { + self.0.dqb_ihardlimit = limit; + } + + /// Preferred inode limit + pub fn inodes_soft_limit(&self) -> Option { + let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) { + Some(self.0.dqb_isoftlimit) + } else { + None + } + } + + /// Set the preferred limit of allocated inodes. + pub fn set_inodes_soft_limit(&mut self, limit: u64) { + self.0.dqb_isoftlimit = limit; + } + + /// Current number of allocated inodes. + pub fn allocated_inodes(&self) -> Option { + let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_INODES) { + Some(self.0.dqb_curinodes) + } else { + None + } + } + + /// Time limit for excessive disk use. + pub fn block_time_limit(&self) -> Option { + let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_BTIME) { + Some(self.0.dqb_btime) + } else { + None + } + } + + /// Set the time limit for excessive disk use. + pub fn set_block_time_limit(&mut self, limit: u64) { + self.0.dqb_btime = limit; + } + + /// Time limit for excessive files. + pub fn inode_time_limit(&self) -> Option { + let valid_fields = QuotaValidFlags::from_bits_truncate(self.0.dqb_valid); + if valid_fields.contains(QuotaValidFlags::QIF_ITIME) { + Some(self.0.dqb_itime) + } else { + None + } + } + + /// Set the time limit for excessive files. + pub fn set_inode_time_limit(&mut self, limit: u64) { + self.0.dqb_itime = limit; + } +} + +fn quotactl(cmd: QuotaCmd, special: Option<&P>, id: c_int, addr: *mut c_char) -> Result<()> { + unsafe { + Errno::clear(); + let res = match special { + Some(dev) => dev.with_nix_path(|path| libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr)), + None => Ok(libc::quotactl(cmd.as_int(), ptr::null(), id, addr)), + }?; + + Errno::result(res).map(drop) + } +} + +/// Turn on disk quotas for a block device. +pub fn quotactl_on(which: QuotaType, special: &P, format: QuotaFmt, quota_file: &P) -> Result<()> { + quota_file.with_nix_path(|path| { + let mut path_copy = path.to_bytes_with_nul().to_owned(); + let p: *mut c_char = path_copy.as_mut_ptr() as *mut c_char; + quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAON, which), Some(special), format as c_int, p) + })? +} + +/// Disable disk quotas for a block device. +pub fn quotactl_off(which: QuotaType, special: &P) -> Result<()> { + quotactl(QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which), Some(special), 0, ptr::null_mut()) +} + +/// Update the on-disk copy of quota usages for a filesystem. +/// +/// If `special` is `None`, then all file systems with active quotas are sync'd. +pub fn quotactl_sync(which: QuotaType, special: Option<&P>) -> Result<()> { + quotactl(QuotaCmd(QuotaSubCmd::Q_SYNC, which), special, 0, ptr::null_mut()) +} + +/// Get disk quota limits and current usage for the given user/group id. +pub fn quotactl_get(which: QuotaType, special: &P, id: c_int) -> Result { + let mut dqblk = mem::MaybeUninit::uninit(); + quotactl(QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which), Some(special), id, dqblk.as_mut_ptr() as *mut c_char)?; + Ok(unsafe{ Dqblk(dqblk.assume_init())}) +} + +/// Configure quota values for the specified fields for a given user/group id. +pub fn quotactl_set(which: QuotaType, special: &P, id: c_int, dqblk: &Dqblk, fields: QuotaValidFlags) -> Result<()> { + let mut dqblk_copy = *dqblk; + dqblk_copy.0.dqb_valid = fields.bits(); + quotactl(QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which), Some(special), id, &mut dqblk_copy as *mut _ as *mut c_char) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/reboot.rs b/vendor/nix-v0.23.1-patched/src/sys/reboot.rs new file mode 100644 index 000000000..46ab68b63 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/reboot.rs @@ -0,0 +1,45 @@ +//! Reboot/shutdown or enable/disable Ctrl-Alt-Delete. + +use crate::Result; +use crate::errno::Errno; +use std::convert::Infallible; +use std::mem::drop; + +libc_enum! { + /// How exactly should the system be rebooted. + /// + /// See [`set_cad_enabled()`](fn.set_cad_enabled.html) for + /// enabling/disabling Ctrl-Alt-Delete. + #[repr(i32)] + #[non_exhaustive] + pub enum RebootMode { + RB_HALT_SYSTEM, + RB_KEXEC, + RB_POWER_OFF, + RB_AUTOBOOT, + // we do not support Restart2, + RB_SW_SUSPEND, + } +} + +pub fn reboot(how: RebootMode) -> Result { + unsafe { + libc::reboot(how as libc::c_int) + }; + Err(Errno::last()) +} + +/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete). +/// +/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C. +pub fn set_cad_enabled(enable: bool) -> Result<()> { + let cmd = if enable { + libc::RB_ENABLE_CAD + } else { + libc::RB_DISABLE_CAD + }; + let res = unsafe { + libc::reboot(cmd) + }; + Errno::result(res).map(drop) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/resource.rs b/vendor/nix-v0.23.1-patched/src/sys/resource.rs new file mode 100644 index 000000000..f3bfb6719 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/resource.rs @@ -0,0 +1,233 @@ +//! Configure the process resource limits. +use cfg_if::cfg_if; + +use crate::errno::Errno; +use crate::Result; +pub use libc::rlim_t; +use std::mem; + +cfg_if! { + if #[cfg(all(target_os = "linux", target_env = "gnu"))]{ + use libc::{__rlimit_resource_t, rlimit, RLIM_INFINITY}; + }else if #[cfg(any( + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "macos", + target_os = "ios", + target_os = "android", + target_os = "dragonfly", + all(target_os = "linux", not(target_env = "gnu")) + ))]{ + use libc::{c_int, rlimit, RLIM_INFINITY}; + } +} + +libc_enum! { + /// The Resource enum is platform dependent. Check different platform + /// manuals for more details. Some platform links has been provided for + /// earier reference (non-exhaustive). + /// + /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html) + /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit) + + // linux-gnu uses u_int as resource enum, which is implemented in libc as + // well. + // + // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html + // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs + #[cfg_attr(all(target_os = "linux", target_env = "gnu"), repr(u32))] + #[cfg_attr(any( + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "macos", + target_os = "ios", + target_os = "android", + target_os = "dragonfly", + all(target_os = "linux", not(target_env = "gnu")) + ), repr(i32))] + #[non_exhaustive] + pub enum Resource { + #[cfg(not(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + )))] + /// The maximum amount (in bytes) of virtual memory the process is + /// allowed to map. + RLIMIT_AS, + /// The largest size (in bytes) core(5) file that may be created. + RLIMIT_CORE, + /// The maximum amount of cpu time (in seconds) to be used by each + /// process. + RLIMIT_CPU, + /// The maximum size (in bytes) of the data segment for a process + RLIMIT_DATA, + /// The largest size (in bytes) file that may be created. + RLIMIT_FSIZE, + /// The maximum number of open files for this process. + RLIMIT_NOFILE, + /// The maximum size (in bytes) of the stack segment for a process. + RLIMIT_STACK, + + #[cfg(target_os = "freebsd")] + /// The maximum number of kqueues this user id is allowed to create. + RLIMIT_KQUEUES, + + #[cfg(any(target_os = "android", target_os = "linux"))] + /// A limit on the combined number of flock locks and fcntl leases that + /// this process may establish. + RLIMIT_LOCKS, + + #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))] + /// The maximum size (in bytes) which a process may lock into memory + /// using the mlock(2) system call. + RLIMIT_MEMLOCK, + + #[cfg(any(target_os = "android", target_os = "linux"))] + /// A limit on the number of bytes that can be allocated for POSIX + /// message queues for the real user ID of the calling process. + RLIMIT_MSGQUEUE, + + #[cfg(any(target_os = "android", target_os = "linux"))] + /// A ceiling to which the process's nice value can be raised using + /// setpriority or nice. + RLIMIT_NICE, + + #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))] + /// The maximum number of simultaneous processes for this user id. + RLIMIT_NPROC, + + #[cfg(target_os = "freebsd")] + /// The maximum number of pseudo-terminals this user id is allowed to + /// create. + RLIMIT_NPTS, + + #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))] + /// When there is memory pressure and swap is available, prioritize + /// eviction of a process' resident pages beyond this amount (in bytes). + RLIMIT_RSS, + + #[cfg(any(target_os = "android", target_os = "linux"))] + /// A ceiling on the real-time priority that may be set for this process + /// using sched_setscheduler and sched_set‐ param. + RLIMIT_RTPRIO, + + #[cfg(any(target_os = "linux"))] + /// A limit (in microseconds) on the amount of CPU time that a process + /// scheduled under a real-time scheduling policy may con‐ sume without + /// making a blocking system call. + RLIMIT_RTTIME, + + #[cfg(any(target_os = "android", target_os = "linux"))] + /// A limit on the number of signals that may be queued for the real + /// user ID of the calling process. + RLIMIT_SIGPENDING, + + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + /// The maximum size (in bytes) of socket buffer usage for this user. + RLIMIT_SBSIZE, + + #[cfg(target_os = "freebsd")] + /// The maximum size (in bytes) of the swap space that may be reserved + /// or used by all of this user id's processes. + RLIMIT_SWAP, + + #[cfg(target_os = "freebsd")] + /// An alias for RLIMIT_AS. + RLIMIT_VMEM, + } +} + +/// Get the current processes resource limits +/// +/// A value of `None` indicates the value equals to `RLIM_INFINITY` which means +/// there is no limit. +/// +/// # Parameters +/// +/// * `resource`: The [`Resource`] that we want to get the limits of. +/// +/// # Examples +/// +/// ``` +/// # use nix::sys::resource::{getrlimit, Resource}; +/// +/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap(); +/// println!("current soft_limit: {:?}", soft_limit); +/// println!("current hard_limit: {:?}", hard_limit); +/// ``` +/// +/// # References +/// +/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215) +/// +/// [`Resource`]: enum.Resource.html +pub fn getrlimit(resource: Resource) -> Result<(Option, Option)> { + let mut old_rlim = mem::MaybeUninit::::uninit(); + + cfg_if! { + if #[cfg(all(target_os = "linux", target_env = "gnu"))]{ + let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) }; + }else{ + let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) }; + } + } + + Errno::result(res).map(|_| { + let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() }; + (Some(rlim_cur), Some(rlim_max)) + }) +} + +/// Set the current processes resource limits +/// +/// # Parameters +/// +/// * `resource`: The [`Resource`] that we want to set the limits of. +/// * `soft_limit`: The value that the kernel enforces for the corresponding +/// resource. Note: `None` input will be replaced by constant `RLIM_INFINITY`. +/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to +/// the current hard limit for non-root users. Note: `None` input will be +/// replaced by constant `RLIM_INFINITY`. +/// +/// > Note: for some os (linux_gnu), setting hard_limit to `RLIM_INFINITY` can +/// > results `EPERM` Error. So you will need to set the number explicitly. +/// +/// # Examples +/// +/// ``` +/// # use nix::sys::resource::{setrlimit, Resource}; +/// +/// let soft_limit = Some(512); +/// let hard_limit = Some(1024); +/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap(); +/// ``` +/// +/// # References +/// +/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215) +/// +/// [`Resource`]: enum.Resource.html +/// +/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`. +pub fn setrlimit( + resource: Resource, + soft_limit: Option, + hard_limit: Option, +) -> Result<()> { + let new_rlim = rlimit { + rlim_cur: soft_limit.unwrap_or(RLIM_INFINITY), + rlim_max: hard_limit.unwrap_or(RLIM_INFINITY), + }; + cfg_if! { + if #[cfg(all(target_os = "linux", target_env = "gnu"))]{ + let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) }; + }else{ + let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) }; + } + } + + Errno::result(res).map(drop) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/select.rs b/vendor/nix-v0.23.1-patched/src/sys/select.rs new file mode 100644 index 000000000..4d7576a58 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/select.rs @@ -0,0 +1,430 @@ +//! Portably monitor a group of file descriptors for readiness. +use std::convert::TryFrom; +use std::iter::FusedIterator; +use std::mem; +use std::ops::Range; +use std::os::unix::io::RawFd; +use std::ptr::{null, null_mut}; +use libc::{self, c_int}; +use crate::Result; +use crate::errno::Errno; +use crate::sys::signal::SigSet; +use crate::sys::time::{TimeSpec, TimeVal}; + +pub use libc::FD_SETSIZE; + +/// Contains a set of file descriptors used by [`select`] +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct FdSet(libc::fd_set); + +fn assert_fd_valid(fd: RawFd) { + assert!( + usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE), + "fd must be in the range 0..FD_SETSIZE", + ); +} + +impl FdSet { + /// Create an empty `FdSet` + pub fn new() -> FdSet { + let mut fdset = mem::MaybeUninit::uninit(); + unsafe { + libc::FD_ZERO(fdset.as_mut_ptr()); + FdSet(fdset.assume_init()) + } + } + + /// Add a file descriptor to an `FdSet` + pub fn insert(&mut self, fd: RawFd) { + assert_fd_valid(fd); + unsafe { libc::FD_SET(fd, &mut self.0) }; + } + + /// Remove a file descriptor from an `FdSet` + pub fn remove(&mut self, fd: RawFd) { + assert_fd_valid(fd); + unsafe { libc::FD_CLR(fd, &mut self.0) }; + } + + /// Test an `FdSet` for the presence of a certain file descriptor. + pub fn contains(&self, fd: RawFd) -> bool { + assert_fd_valid(fd); + unsafe { libc::FD_ISSET(fd, &self.0) } + } + + /// Remove all file descriptors from this `FdSet`. + pub fn clear(&mut self) { + unsafe { libc::FD_ZERO(&mut self.0) }; + } + + /// Finds the highest file descriptor in the set. + /// + /// Returns `None` if the set is empty. + /// + /// This can be used to calculate the `nfds` parameter of the [`select`] function. + /// + /// # Example + /// + /// ``` + /// # use nix::sys::select::FdSet; + /// let mut set = FdSet::new(); + /// set.insert(4); + /// set.insert(9); + /// assert_eq!(set.highest(), Some(9)); + /// ``` + /// + /// [`select`]: fn.select.html + pub fn highest(&self) -> Option { + self.fds(None).next_back() + } + + /// Returns an iterator over the file descriptors in the set. + /// + /// For performance, it takes an optional higher bound: the iterator will + /// not return any elements of the set greater than the given file + /// descriptor. + /// + /// # Examples + /// + /// ``` + /// # use nix::sys::select::FdSet; + /// # use std::os::unix::io::RawFd; + /// let mut set = FdSet::new(); + /// set.insert(4); + /// set.insert(9); + /// let fds: Vec = set.fds(None).collect(); + /// assert_eq!(fds, vec![4, 9]); + /// ``` + #[inline] + pub fn fds(&self, highest: Option) -> Fds { + Fds { + set: self, + range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE), + } + } +} + +impl Default for FdSet { + fn default() -> Self { + Self::new() + } +} + +/// Iterator over `FdSet`. +#[derive(Debug)] +pub struct Fds<'a> { + set: &'a FdSet, + range: Range, +} + +impl<'a> Iterator for Fds<'a> { + type Item = RawFd; + + fn next(&mut self) -> Option { + for i in &mut self.range { + if self.set.contains(i as RawFd) { + return Some(i as RawFd); + } + } + None + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.range.size_hint(); + (0, upper) + } +} + +impl<'a> DoubleEndedIterator for Fds<'a> { + #[inline] + fn next_back(&mut self) -> Option { + while let Some(i) = self.range.next_back() { + if self.set.contains(i as RawFd) { + return Some(i as RawFd); + } + } + None + } +} + +impl<'a> FusedIterator for Fds<'a> {} + +/// Monitors file descriptors for readiness +/// +/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all +/// file descriptors that are ready for the given operation are set. +/// +/// When this function returns, `timeout` has an implementation-defined value. +/// +/// # Parameters +/// +/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this +/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1 +/// to the maximum of that. +/// * `readfds`: File descriptors to check for being ready to read. +/// * `writefds`: File descriptors to check for being ready to write. +/// * `errorfds`: File descriptors to check for pending error conditions. +/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block +/// indefinitely). +/// +/// # References +/// +/// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html) +/// +/// [`FdSet::highest`]: struct.FdSet.html#method.highest +pub fn select<'a, N, R, W, E, T>(nfds: N, + readfds: R, + writefds: W, + errorfds: E, + timeout: T) -> Result +where + N: Into>, + R: Into>, + W: Into>, + E: Into>, + T: Into>, +{ + let mut readfds = readfds.into(); + let mut writefds = writefds.into(); + let mut errorfds = errorfds.into(); + let timeout = timeout.into(); + + let nfds = nfds.into().unwrap_or_else(|| { + readfds.iter_mut() + .chain(writefds.iter_mut()) + .chain(errorfds.iter_mut()) + .map(|set| set.highest().unwrap_or(-1)) + .max() + .unwrap_or(-1) + 1 + }); + + let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut()); + let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut()); + let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut()); + let timeout = timeout.map(|tv| tv as *mut _ as *mut libc::timeval) + .unwrap_or(null_mut()); + + let res = unsafe { + libc::select(nfds, readfds, writefds, errorfds, timeout) + }; + + Errno::result(res) +} + +/// Monitors file descriptors for readiness with an altered signal mask. +/// +/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all +/// file descriptors that are ready for the given operation are set. +/// +/// When this function returns, the original signal mask is restored. +/// +/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value. +/// +/// # Parameters +/// +/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this +/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1 +/// to the maximum of that. +/// * `readfds`: File descriptors to check for read readiness +/// * `writefds`: File descriptors to check for write readiness +/// * `errorfds`: File descriptors to check for pending error conditions. +/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block +/// indefinitely). +/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn +/// ready (`None` to set no alternative signal mask). +/// +/// # References +/// +/// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html) +/// +/// [The new pselect() system call](https://lwn.net/Articles/176911/) +/// +/// [`FdSet::highest`]: struct.FdSet.html#method.highest +pub fn pselect<'a, N, R, W, E, T, S>(nfds: N, + readfds: R, + writefds: W, + errorfds: E, + timeout: T, + sigmask: S) -> Result +where + N: Into>, + R: Into>, + W: Into>, + E: Into>, + T: Into>, + S: Into>, +{ + let mut readfds = readfds.into(); + let mut writefds = writefds.into(); + let mut errorfds = errorfds.into(); + let sigmask = sigmask.into(); + let timeout = timeout.into(); + + let nfds = nfds.into().unwrap_or_else(|| { + readfds.iter_mut() + .chain(writefds.iter_mut()) + .chain(errorfds.iter_mut()) + .map(|set| set.highest().unwrap_or(-1)) + .max() + .unwrap_or(-1) + 1 + }); + + let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut()); + let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut()); + let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut()); + let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null()); + let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null()); + + let res = unsafe { + libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask) + }; + + Errno::result(res) +} + + +#[cfg(test)] +mod tests { + use super::*; + use std::os::unix::io::RawFd; + use crate::sys::time::{TimeVal, TimeValLike}; + use crate::unistd::{write, pipe}; + + #[test] + fn fdset_insert() { + let mut fd_set = FdSet::new(); + + for i in 0..FD_SETSIZE { + assert!(!fd_set.contains(i as RawFd)); + } + + fd_set.insert(7); + + assert!(fd_set.contains(7)); + } + + #[test] + fn fdset_remove() { + let mut fd_set = FdSet::new(); + + for i in 0..FD_SETSIZE { + assert!(!fd_set.contains(i as RawFd)); + } + + fd_set.insert(7); + fd_set.remove(7); + + for i in 0..FD_SETSIZE { + assert!(!fd_set.contains(i as RawFd)); + } + } + + #[test] + fn fdset_clear() { + let mut fd_set = FdSet::new(); + fd_set.insert(1); + fd_set.insert((FD_SETSIZE / 2) as RawFd); + fd_set.insert((FD_SETSIZE - 1) as RawFd); + + fd_set.clear(); + + for i in 0..FD_SETSIZE { + assert!(!fd_set.contains(i as RawFd)); + } + } + + #[test] + fn fdset_highest() { + let mut set = FdSet::new(); + assert_eq!(set.highest(), None); + set.insert(0); + assert_eq!(set.highest(), Some(0)); + set.insert(90); + assert_eq!(set.highest(), Some(90)); + set.remove(0); + assert_eq!(set.highest(), Some(90)); + set.remove(90); + assert_eq!(set.highest(), None); + + set.insert(4); + set.insert(5); + set.insert(7); + assert_eq!(set.highest(), Some(7)); + } + + #[test] + fn fdset_fds() { + let mut set = FdSet::new(); + assert_eq!(set.fds(None).collect::>(), vec![]); + set.insert(0); + assert_eq!(set.fds(None).collect::>(), vec![0]); + set.insert(90); + assert_eq!(set.fds(None).collect::>(), vec![0, 90]); + + // highest limit + assert_eq!(set.fds(Some(89)).collect::>(), vec![0]); + assert_eq!(set.fds(Some(90)).collect::>(), vec![0, 90]); + } + + #[test] + fn test_select() { + let (r1, w1) = pipe().unwrap(); + write(w1, b"hi!").unwrap(); + let (r2, _w2) = pipe().unwrap(); + + let mut fd_set = FdSet::new(); + fd_set.insert(r1); + fd_set.insert(r2); + + let mut timeout = TimeVal::seconds(10); + assert_eq!(1, select(None, + &mut fd_set, + None, + None, + &mut timeout).unwrap()); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); + } + + #[test] + fn test_select_nfds() { + let (r1, w1) = pipe().unwrap(); + write(w1, b"hi!").unwrap(); + let (r2, _w2) = pipe().unwrap(); + + let mut fd_set = FdSet::new(); + fd_set.insert(r1); + fd_set.insert(r2); + + let mut timeout = TimeVal::seconds(10); + assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1), + &mut fd_set, + None, + None, + &mut timeout).unwrap()); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); + } + + #[test] + fn test_select_nfds2() { + let (r1, w1) = pipe().unwrap(); + write(w1, b"hi!").unwrap(); + let (r2, _w2) = pipe().unwrap(); + + let mut fd_set = FdSet::new(); + fd_set.insert(r1); + fd_set.insert(r2); + + let mut timeout = TimeVal::seconds(10); + assert_eq!(1, select(::std::cmp::max(r1, r2) + 1, + &mut fd_set, + None, + None, + &mut timeout).unwrap()); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/sendfile.rs b/vendor/nix-v0.23.1-patched/src/sys/sendfile.rs new file mode 100644 index 000000000..7a210c6fc --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/sendfile.rs @@ -0,0 +1,231 @@ +//! Send data from a file to a socket, bypassing userland. + +use cfg_if::cfg_if; +use std::os::unix::io::RawFd; +use std::ptr; + +use libc::{self, off_t}; + +use crate::Result; +use crate::errno::Errno; + +/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`. +/// +/// Returns a `Result` with the number of bytes written. +/// +/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will +/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified +/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to +/// the byte after the last byte copied. +/// +/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket. +/// +/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) +#[cfg(any(target_os = "android", target_os = "linux"))] +pub fn sendfile( + out_fd: RawFd, + in_fd: RawFd, + offset: Option<&mut off_t>, + count: usize, +) -> Result { + let offset = offset + .map(|offset| offset as *mut _) + .unwrap_or(ptr::null_mut()); + let ret = unsafe { libc::sendfile(out_fd, in_fd, offset, count) }; + Errno::result(ret).map(|r| r as usize) +} + +/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`. +/// +/// Returns a `Result` with the number of bytes written. +/// +/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will +/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified +/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to +/// the byte after the last byte copied. +/// +/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket. +/// +/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) +#[cfg(target_os = "linux")] +pub fn sendfile64( + out_fd: RawFd, + in_fd: RawFd, + offset: Option<&mut libc::off64_t>, + count: usize, +) -> Result { + let offset = offset + .map(|offset| offset as *mut _) + .unwrap_or(ptr::null_mut()); + let ret = unsafe { libc::sendfile64(out_fd, in_fd, offset, count) }; + Errno::result(ret).map(|r| r as usize) +} + +cfg_if! { + if #[cfg(any(target_os = "freebsd", + target_os = "ios", + target_os = "macos"))] { + use crate::sys::uio::IoVec; + + #[derive(Clone, Debug, Eq, Hash, PartialEq)] + struct SendfileHeaderTrailer<'a>( + libc::sf_hdtr, + Option>>, + Option>>, + ); + + impl<'a> SendfileHeaderTrailer<'a> { + fn new( + headers: Option<&'a [&'a [u8]]>, + trailers: Option<&'a [&'a [u8]]> + ) -> SendfileHeaderTrailer<'a> { + let header_iovecs: Option>> = + headers.map(|s| s.iter().map(|b| IoVec::from_slice(b)).collect()); + let trailer_iovecs: Option>> = + trailers.map(|s| s.iter().map(|b| IoVec::from_slice(b)).collect()); + SendfileHeaderTrailer( + libc::sf_hdtr { + headers: { + header_iovecs + .as_ref() + .map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec + }, + hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32, + trailers: { + trailer_iovecs + .as_ref() + .map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec + }, + trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32 + }, + header_iovecs, + trailer_iovecs, + ) + } + } + } +} + +cfg_if! { + if #[cfg(target_os = "freebsd")] { + use libc::c_int; + + libc_bitflags!{ + /// Configuration options for [`sendfile`.](fn.sendfile.html) + pub struct SfFlags: c_int { + /// Causes `sendfile` to return EBUSY instead of blocking when attempting to read a + /// busy page. + SF_NODISKIO; + /// Causes `sendfile` to sleep until the network stack releases its reference to the + /// VM pages read. When `sendfile` returns, the data is not guaranteed to have been + /// sent, but it is safe to modify the file. + SF_SYNC; + /// Causes `sendfile` to cache exactly the number of pages specified in the + /// `readahead` parameter, disabling caching heuristics. + SF_USER_READAHEAD; + /// Causes `sendfile` not to cache the data read. + SF_NOCACHE; + } + } + + /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`. + /// + /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if + /// an error occurs. + /// + /// `in_fd` must describe a regular file or shared memory object. `out_sock` must describe a + /// stream socket. + /// + /// If `offset` falls past the end of the file, the function returns success and zero bytes + /// written. + /// + /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of + /// file (EOF). + /// + /// `headers` and `trailers` specify optional slices of byte slices to be sent before and + /// after the data read from `in_fd`, respectively. The length of headers and trailers sent + /// is included in the returned count of bytes written. The values of `offset` and `count` + /// do not apply to headers or trailers. + /// + /// `readahead` specifies the minimum number of pages to cache in memory ahead of the page + /// currently being sent. + /// + /// For more information, see + /// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2) + #[allow(clippy::too_many_arguments)] + pub fn sendfile( + in_fd: RawFd, + out_sock: RawFd, + offset: off_t, + count: Option, + headers: Option<&[&[u8]]>, + trailers: Option<&[&[u8]]>, + flags: SfFlags, + readahead: u16 + ) -> (Result<()>, off_t) { + // Readahead goes in upper 16 bits + // Flags goes in lower 16 bits + // see `man 2 sendfile` + let ra32 = u32::from(readahead); + let flags: u32 = (ra32 << 16) | (flags.bits() as u32); + let mut bytes_sent: off_t = 0; + let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); + let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr); + let return_code = unsafe { + libc::sendfile(in_fd, + out_sock, + offset, + count.unwrap_or(0), + hdtr_ptr as *mut libc::sf_hdtr, + &mut bytes_sent as *mut off_t, + flags as c_int) + }; + (Errno::result(return_code).and(Ok(())), bytes_sent) + } + } else if #[cfg(any(target_os = "ios", target_os = "macos"))] { + /// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to + /// `out_sock`. + /// + /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if + /// an error occurs. + /// + /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket. + /// + /// If `offset` falls past the end of the file, the function returns success and zero bytes + /// written. + /// + /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of + /// file (EOF). + /// + /// `hdtr` specifies an optional list of headers and trailers to be sent before and after + /// the data read from `in_fd`, respectively. The length of headers and trailers sent is + /// included in the returned count of bytes written. If any headers are specified and + /// `count` is non-zero, the length of the headers will be counted in the limit of total + /// bytes sent. Trailers do not count toward the limit of bytes sent and will always be sent + /// regardless. The value of `offset` does not affect headers or trailers. + /// + /// For more information, see + /// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html) + pub fn sendfile( + in_fd: RawFd, + out_sock: RawFd, + offset: off_t, + count: Option, + headers: Option<&[&[u8]]>, + trailers: Option<&[&[u8]]> + ) -> (Result<()>, off_t) { + let mut len = count.unwrap_or(0); + let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); + let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr); + let return_code = unsafe { + libc::sendfile(in_fd, + out_sock, + offset, + &mut len as *mut off_t, + hdtr_ptr as *mut libc::sf_hdtr, + 0) + }; + (Errno::result(return_code).and(Ok(())), len) + } + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/signal.rs b/vendor/nix-v0.23.1-patched/src/sys/signal.rs new file mode 100644 index 000000000..e8c79d336 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/signal.rs @@ -0,0 +1,1234 @@ +// Portions of this file are Copyright 2014 The Rust Project Developers. +// See https://www.rust-lang.org/policies/licenses. + +//! Operating system signals. + +use crate::{Error, Result}; +use crate::errno::Errno; +use crate::unistd::Pid; +use std::mem; +use std::fmt; +use std::str::FromStr; +#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] +use std::os::unix::io::RawFd; +use std::ptr; + +#[cfg(not(any(target_os = "openbsd", target_os = "redox")))] +pub use self::sigevent::*; + +libc_enum!{ + /// Types of operating system signals + // Currently there is only one definition of c_int in libc, as well as only one + // type for signal constants. + // We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately + // this is not (yet) possible. + #[repr(i32)] + #[non_exhaustive] + pub enum Signal { + /// Hangup + SIGHUP, + /// Interrupt + SIGINT, + /// Quit + SIGQUIT, + /// Illegal instruction (not reset when caught) + SIGILL, + /// Trace trap (not reset when caught) + SIGTRAP, + /// Abort + SIGABRT, + /// Bus error + SIGBUS, + /// Floating point exception + SIGFPE, + /// Kill (cannot be caught or ignored) + SIGKILL, + /// User defined signal 1 + SIGUSR1, + /// Segmentation violation + SIGSEGV, + /// User defined signal 2 + SIGUSR2, + /// Write on a pipe with no one to read it + SIGPIPE, + /// Alarm clock + SIGALRM, + /// Software termination signal from kill + SIGTERM, + /// Stack fault (obsolete) + #[cfg(all(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"), + not(any(target_arch = "mips", target_arch = "mips64", + target_arch = "sparc64"))))] + SIGSTKFLT, + /// To parent on child stop or exit + SIGCHLD, + /// Continue a stopped process + SIGCONT, + /// Sendable stop signal not from tty + SIGSTOP, + /// Stop signal from tty + SIGTSTP, + /// To readers pgrp upon background tty read + SIGTTIN, + /// Like TTIN if (tp->t_local<OSTOP) + SIGTTOU, + /// Urgent condition on IO channel + SIGURG, + /// Exceeded CPU time limit + SIGXCPU, + /// Exceeded file size limit + SIGXFSZ, + /// Virtual time alarm + SIGVTALRM, + /// Profiling time alarm + SIGPROF, + /// Window size changes + SIGWINCH, + /// Input/output possible signal + SIGIO, + #[cfg(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"))] + /// Power failure imminent. + SIGPWR, + /// Bad system call + SIGSYS, + #[cfg(not(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] + /// Emulator trap + SIGEMT, + #[cfg(not(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] + /// Information request + SIGINFO, + } + impl TryFrom +} + +impl FromStr for Signal { + type Err = Error; + fn from_str(s: &str) -> Result { + Ok(match s { + "SIGHUP" => Signal::SIGHUP, + "SIGINT" => Signal::SIGINT, + "SIGQUIT" => Signal::SIGQUIT, + "SIGILL" => Signal::SIGILL, + "SIGTRAP" => Signal::SIGTRAP, + "SIGABRT" => Signal::SIGABRT, + "SIGBUS" => Signal::SIGBUS, + "SIGFPE" => Signal::SIGFPE, + "SIGKILL" => Signal::SIGKILL, + "SIGUSR1" => Signal::SIGUSR1, + "SIGSEGV" => Signal::SIGSEGV, + "SIGUSR2" => Signal::SIGUSR2, + "SIGPIPE" => Signal::SIGPIPE, + "SIGALRM" => Signal::SIGALRM, + "SIGTERM" => Signal::SIGTERM, + #[cfg(all(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"), + not(any(target_arch = "mips", target_arch = "mips64", + target_arch = "sparc64"))))] + "SIGSTKFLT" => Signal::SIGSTKFLT, + "SIGCHLD" => Signal::SIGCHLD, + "SIGCONT" => Signal::SIGCONT, + "SIGSTOP" => Signal::SIGSTOP, + "SIGTSTP" => Signal::SIGTSTP, + "SIGTTIN" => Signal::SIGTTIN, + "SIGTTOU" => Signal::SIGTTOU, + "SIGURG" => Signal::SIGURG, + "SIGXCPU" => Signal::SIGXCPU, + "SIGXFSZ" => Signal::SIGXFSZ, + "SIGVTALRM" => Signal::SIGVTALRM, + "SIGPROF" => Signal::SIGPROF, + "SIGWINCH" => Signal::SIGWINCH, + "SIGIO" => Signal::SIGIO, + #[cfg(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"))] + "SIGPWR" => Signal::SIGPWR, + "SIGSYS" => Signal::SIGSYS, + #[cfg(not(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] + "SIGEMT" => Signal::SIGEMT, + #[cfg(not(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] + "SIGINFO" => Signal::SIGINFO, + _ => return Err(Errno::EINVAL), + }) + } +} + +impl Signal { + /// Returns name of signal. + /// + /// This function is equivalent to `>::as_ref()`, + /// with difference that returned string is `'static` + /// and not bound to `self`'s lifetime. + pub const fn as_str(self) -> &'static str { + match self { + Signal::SIGHUP => "SIGHUP", + Signal::SIGINT => "SIGINT", + Signal::SIGQUIT => "SIGQUIT", + Signal::SIGILL => "SIGILL", + Signal::SIGTRAP => "SIGTRAP", + Signal::SIGABRT => "SIGABRT", + Signal::SIGBUS => "SIGBUS", + Signal::SIGFPE => "SIGFPE", + Signal::SIGKILL => "SIGKILL", + Signal::SIGUSR1 => "SIGUSR1", + Signal::SIGSEGV => "SIGSEGV", + Signal::SIGUSR2 => "SIGUSR2", + Signal::SIGPIPE => "SIGPIPE", + Signal::SIGALRM => "SIGALRM", + Signal::SIGTERM => "SIGTERM", + #[cfg(all(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"), + not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] + Signal::SIGSTKFLT => "SIGSTKFLT", + Signal::SIGCHLD => "SIGCHLD", + Signal::SIGCONT => "SIGCONT", + Signal::SIGSTOP => "SIGSTOP", + Signal::SIGTSTP => "SIGTSTP", + Signal::SIGTTIN => "SIGTTIN", + Signal::SIGTTOU => "SIGTTOU", + Signal::SIGURG => "SIGURG", + Signal::SIGXCPU => "SIGXCPU", + Signal::SIGXFSZ => "SIGXFSZ", + Signal::SIGVTALRM => "SIGVTALRM", + Signal::SIGPROF => "SIGPROF", + Signal::SIGWINCH => "SIGWINCH", + Signal::SIGIO => "SIGIO", + #[cfg(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"))] + Signal::SIGPWR => "SIGPWR", + Signal::SIGSYS => "SIGSYS", + #[cfg(not(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] + Signal::SIGEMT => "SIGEMT", + #[cfg(not(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux", + target_os = "redox")))] + Signal::SIGINFO => "SIGINFO", + } + } +} + +impl AsRef for Signal { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl fmt::Display for Signal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.as_ref()) + } +} + +pub use self::Signal::*; + +#[cfg(target_os = "redox")] +const SIGNALS: [Signal; 29] = [ + SIGHUP, + SIGINT, + SIGQUIT, + SIGILL, + SIGTRAP, + SIGABRT, + SIGBUS, + SIGFPE, + SIGKILL, + SIGUSR1, + SIGSEGV, + SIGUSR2, + SIGPIPE, + SIGALRM, + SIGTERM, + SIGCHLD, + SIGCONT, + SIGSTOP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGURG, + SIGXCPU, + SIGXFSZ, + SIGVTALRM, + SIGPROF, + SIGWINCH, + SIGIO, + SIGSYS]; +#[cfg(all(any(target_os = "linux", target_os = "android", + target_os = "emscripten", target_os = "fuchsia"), + not(any(target_arch = "mips", target_arch = "mips64", + target_arch = "sparc64"))))] +const SIGNALS: [Signal; 31] = [ + SIGHUP, + SIGINT, + SIGQUIT, + SIGILL, + SIGTRAP, + SIGABRT, + SIGBUS, + SIGFPE, + SIGKILL, + SIGUSR1, + SIGSEGV, + SIGUSR2, + SIGPIPE, + SIGALRM, + SIGTERM, + SIGSTKFLT, + SIGCHLD, + SIGCONT, + SIGSTOP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGURG, + SIGXCPU, + SIGXFSZ, + SIGVTALRM, + SIGPROF, + SIGWINCH, + SIGIO, + SIGPWR, + SIGSYS]; +#[cfg(all(any(target_os = "linux", target_os = "android", + target_os = "emscripten", target_os = "fuchsia"), + any(target_arch = "mips", target_arch = "mips64", + target_arch = "sparc64")))] +const SIGNALS: [Signal; 30] = [ + SIGHUP, + SIGINT, + SIGQUIT, + SIGILL, + SIGTRAP, + SIGABRT, + SIGBUS, + SIGFPE, + SIGKILL, + SIGUSR1, + SIGSEGV, + SIGUSR2, + SIGPIPE, + SIGALRM, + SIGTERM, + SIGCHLD, + SIGCONT, + SIGSTOP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGURG, + SIGXCPU, + SIGXFSZ, + SIGVTALRM, + SIGPROF, + SIGWINCH, + SIGIO, + SIGPWR, + SIGSYS]; +#[cfg(not(any(target_os = "linux", target_os = "android", + target_os = "fuchsia", target_os = "emscripten", + target_os = "redox")))] +const SIGNALS: [Signal; 31] = [ + SIGHUP, + SIGINT, + SIGQUIT, + SIGILL, + SIGTRAP, + SIGABRT, + SIGBUS, + SIGFPE, + SIGKILL, + SIGUSR1, + SIGSEGV, + SIGUSR2, + SIGPIPE, + SIGALRM, + SIGTERM, + SIGCHLD, + SIGCONT, + SIGSTOP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGURG, + SIGXCPU, + SIGXFSZ, + SIGVTALRM, + SIGPROF, + SIGWINCH, + SIGIO, + SIGSYS, + SIGEMT, + SIGINFO]; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +/// Iterate through all signals defined by this operating system +pub struct SignalIterator { + next: usize, +} + +impl Iterator for SignalIterator { + type Item = Signal; + + fn next(&mut self) -> Option { + if self.next < SIGNALS.len() { + let next_signal = SIGNALS[self.next]; + self.next += 1; + Some(next_signal) + } else { + None + } + } +} + +impl Signal { + /// Iterate through all signals defined by this OS + pub const fn iterator() -> SignalIterator { + SignalIterator{next: 0} + } +} + +/// Alias for [`SIGABRT`] +pub const SIGIOT : Signal = SIGABRT; +/// Alias for [`SIGIO`] +pub const SIGPOLL : Signal = SIGIO; +/// Alias for [`SIGSYS`] +pub const SIGUNUSED : Signal = SIGSYS; + +#[cfg(not(target_os = "redox"))] +type SaFlags_t = libc::c_int; +#[cfg(target_os = "redox")] +type SaFlags_t = libc::c_ulong; + +libc_bitflags!{ + /// Controls the behavior of a [`SigAction`] + pub struct SaFlags: SaFlags_t { + /// When catching a [`Signal::SIGCHLD`] signal, the signal will be + /// generated only when a child process exits, not when a child process + /// stops. + SA_NOCLDSTOP; + /// When catching a [`Signal::SIGCHLD`] signal, the system will not + /// create zombie processes when children of the calling process exit. + SA_NOCLDWAIT; + /// Further occurrences of the delivered signal are not masked during + /// the execution of the handler. + SA_NODEFER; + /// The system will deliver the signal to the process on a signal stack, + /// specified by each thread with sigaltstack(2). + SA_ONSTACK; + /// The handler is reset back to the default at the moment the signal is + /// delivered. + SA_RESETHAND; + /// Requests that certain system calls restart if interrupted by this + /// signal. See the man page for complete details. + SA_RESTART; + /// This flag is controlled internally by Nix. + SA_SIGINFO; + } +} + +libc_enum! { + /// Specifies how certain functions should manipulate a signal mask + #[repr(i32)] + #[non_exhaustive] + pub enum SigmaskHow { + /// The new mask is the union of the current mask and the specified set. + SIG_BLOCK, + /// The new mask is the intersection of the current mask and the + /// complement of the specified set. + SIG_UNBLOCK, + /// The current mask is replaced by the specified set. + SIG_SETMASK, + } +} + +/// Specifies a set of [`Signal`]s that may be blocked, waited for, etc. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct SigSet { + sigset: libc::sigset_t +} + + +impl SigSet { + /// Initialize to include all signals. + pub fn all() -> SigSet { + let mut sigset = mem::MaybeUninit::uninit(); + let _ = unsafe { libc::sigfillset(sigset.as_mut_ptr()) }; + + unsafe{ SigSet { sigset: sigset.assume_init() } } + } + + /// Initialize to include nothing. + pub fn empty() -> SigSet { + let mut sigset = mem::MaybeUninit::uninit(); + let _ = unsafe { libc::sigemptyset(sigset.as_mut_ptr()) }; + + unsafe{ SigSet { sigset: sigset.assume_init() } } + } + + /// Add the specified signal to the set. + pub fn add(&mut self, signal: Signal) { + unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) }; + } + + /// Remove all signals from this set. + pub fn clear(&mut self) { + unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) }; + } + + /// Remove the specified signal from this set. + pub fn remove(&mut self, signal: Signal) { + unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) }; + } + + /// Return whether this set includes the specified signal. + pub fn contains(&self, signal: Signal) -> bool { + let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signal as libc::c_int) }; + + match res { + 1 => true, + 0 => false, + _ => unreachable!("unexpected value from sigismember"), + } + } + + /// Merge all of `other`'s signals into this set. + // TODO: use libc::sigorset on supported operating systems. + pub fn extend(&mut self, other: &SigSet) { + for signal in Signal::iterator() { + if other.contains(signal) { + self.add(signal); + } + } + } + + /// Gets the currently blocked (masked) set of signals for the calling thread. + pub fn thread_get_mask() -> Result { + let mut oldmask = mem::MaybeUninit::uninit(); + do_pthread_sigmask(SigmaskHow::SIG_SETMASK, None, Some(oldmask.as_mut_ptr()))?; + Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}}) + } + + /// Sets the set of signals as the signal mask for the calling thread. + pub fn thread_set_mask(&self) -> Result<()> { + pthread_sigmask(SigmaskHow::SIG_SETMASK, Some(self), None) + } + + /// Adds the set of signals to the signal mask for the calling thread. + pub fn thread_block(&self) -> Result<()> { + pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(self), None) + } + + /// Removes the set of signals from the signal mask for the calling thread. + pub fn thread_unblock(&self) -> Result<()> { + pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(self), None) + } + + /// Sets the set of signals as the signal mask, and returns the old mask. + pub fn thread_swap_mask(&self, how: SigmaskHow) -> Result { + let mut oldmask = mem::MaybeUninit::uninit(); + do_pthread_sigmask(how, Some(self), Some(oldmask.as_mut_ptr()))?; + Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}}) + } + + /// Suspends execution of the calling thread until one of the signals in the + /// signal mask becomes pending, and returns the accepted signal. + #[cfg(not(target_os = "redox"))] // RedoxFS does not yet support sigwait + pub fn wait(&self) -> Result { + use std::convert::TryFrom; + + let mut signum = mem::MaybeUninit::uninit(); + let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, signum.as_mut_ptr()) }; + + Errno::result(res).map(|_| unsafe { + Signal::try_from(signum.assume_init()).unwrap() + }) + } +} + +impl AsRef for SigSet { + fn as_ref(&self) -> &libc::sigset_t { + &self.sigset + } +} + +/// A signal handler. +#[allow(unknown_lints)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SigHandler { + /// Default signal handling. + SigDfl, + /// Request that the signal be ignored. + SigIgn, + /// Use the given signal-catching function, which takes in the signal. + Handler(extern fn(libc::c_int)), + /// Use the given signal-catching function, which takes in the signal, information about how + /// the signal was generated, and a pointer to the threads `ucontext_t`. + #[cfg(not(target_os = "redox"))] + SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)) +} + +/// Action to take on receipt of a signal. Corresponds to `sigaction`. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct SigAction { + sigaction: libc::sigaction +} + +impl SigAction { + /// Creates a new action. + /// + /// The `SA_SIGINFO` bit in the `flags` argument is ignored (it will be set only if `handler` + /// is the `SigAction` variant). `mask` specifies other signals to block during execution of + /// the signal-catching function. + pub fn new(handler: SigHandler, flags: SaFlags, mask: SigSet) -> SigAction { + #[cfg(target_os = "redox")] + unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) { + (*p).sa_handler = match handler { + SigHandler::SigDfl => libc::SIG_DFL, + SigHandler::SigIgn => libc::SIG_IGN, + SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize, + }; + } + + #[cfg(not(target_os = "redox"))] + unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) { + (*p).sa_sigaction = match handler { + SigHandler::SigDfl => libc::SIG_DFL, + SigHandler::SigIgn => libc::SIG_IGN, + SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize, + SigHandler::SigAction(f) => f as *const extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize, + }; + } + + let mut s = mem::MaybeUninit::::uninit(); + unsafe { + let p = s.as_mut_ptr(); + install_sig(p, handler); + (*p).sa_flags = match handler { + #[cfg(not(target_os = "redox"))] + SigHandler::SigAction(_) => (flags | SaFlags::SA_SIGINFO).bits(), + _ => (flags - SaFlags::SA_SIGINFO).bits(), + }; + (*p).sa_mask = mask.sigset; + + SigAction { sigaction: s.assume_init() } + } + } + + /// Returns the flags set on the action. + pub fn flags(&self) -> SaFlags { + SaFlags::from_bits_truncate(self.sigaction.sa_flags) + } + + /// Returns the set of signals that are blocked during execution of the action's + /// signal-catching function. + pub fn mask(&self) -> SigSet { + SigSet { sigset: self.sigaction.sa_mask } + } + + /// Returns the action's handler. + #[cfg(not(target_os = "redox"))] + pub fn handler(&self) -> SigHandler { + match self.sigaction.sa_sigaction { + libc::SIG_DFL => SigHandler::SigDfl, + libc::SIG_IGN => SigHandler::SigIgn, + p if self.flags().contains(SaFlags::SA_SIGINFO) => + SigHandler::SigAction( + // Safe for one of two reasons: + // * The SigHandler was created by SigHandler::new, in which + // case the pointer is correct, or + // * The SigHandler was created by signal or sigaction, which + // are unsafe functions, so the caller should've somehow + // ensured that it is correctly initialized. + unsafe{ + *(&p as *const usize + as *const extern fn(_, _, _)) + } + as extern fn(_, _, _)), + p => SigHandler::Handler( + // Safe for one of two reasons: + // * The SigHandler was created by SigHandler::new, in which + // case the pointer is correct, or + // * The SigHandler was created by signal or sigaction, which + // are unsafe functions, so the caller should've somehow + // ensured that it is correctly initialized. + unsafe{ + *(&p as *const usize + as *const extern fn(libc::c_int)) + } + as extern fn(libc::c_int)), + } + } + + /// Returns the action's handler. + #[cfg(target_os = "redox")] + pub fn handler(&self) -> SigHandler { + match self.sigaction.sa_handler { + libc::SIG_DFL => SigHandler::SigDfl, + libc::SIG_IGN => SigHandler::SigIgn, + p => SigHandler::Handler( + // Safe for one of two reasons: + // * The SigHandler was created by SigHandler::new, in which + // case the pointer is correct, or + // * The SigHandler was created by signal or sigaction, which + // are unsafe functions, so the caller should've somehow + // ensured that it is correctly initialized. + unsafe{ + *(&p as *const usize + as *const extern fn(libc::c_int)) + } + as extern fn(libc::c_int)), + } + } +} + +/// Changes the action taken by a process on receipt of a specific signal. +/// +/// `signal` can be any signal except `SIGKILL` or `SIGSTOP`. On success, it returns the previous +/// action for the given signal. If `sigaction` fails, no new signal handler is installed. +/// +/// # Safety +/// +/// * Signal handlers may be called at any point during execution, which limits +/// what is safe to do in the body of the signal-catching function. Be certain +/// to only make syscalls that are explicitly marked safe for signal handlers +/// and only share global data using atomics. +/// +/// * There is also no guarantee that the old signal handler was installed +/// correctly. If it was installed by this crate, it will be. But if it was +/// installed by, for example, C code, then there is no guarantee its function +/// pointer is valid. In that case, this function effectively dereferences a +/// raw pointer of unknown provenance. +pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result { + let mut oldact = mem::MaybeUninit::::uninit(); + + let res = libc::sigaction(signal as libc::c_int, + &sigaction.sigaction as *const libc::sigaction, + oldact.as_mut_ptr()); + + Errno::result(res).map(|_| SigAction { sigaction: oldact.assume_init() }) +} + +/// Signal management (see [signal(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html)) +/// +/// Installs `handler` for the given `signal`, returning the previous signal +/// handler. `signal` should only be used following another call to `signal` or +/// if the current handler is the default. The return value of `signal` is +/// undefined after setting the handler with [`sigaction`][SigActionFn]. +/// +/// # Safety +/// +/// If the pointer to the previous signal handler is invalid, undefined +/// behavior could be invoked when casting it back to a [`SigAction`][SigActionStruct]. +/// +/// # Examples +/// +/// Ignore `SIGINT`: +/// +/// ```no_run +/// # use nix::sys::signal::{self, Signal, SigHandler}; +/// unsafe { signal::signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap(); +/// ``` +/// +/// Use a signal handler to set a flag variable: +/// +/// ```no_run +/// # #[macro_use] extern crate lazy_static; +/// # use std::convert::TryFrom; +/// # use std::sync::atomic::{AtomicBool, Ordering}; +/// # use nix::sys::signal::{self, Signal, SigHandler}; +/// lazy_static! { +/// static ref SIGNALED: AtomicBool = AtomicBool::new(false); +/// } +/// +/// extern fn handle_sigint(signal: libc::c_int) { +/// let signal = Signal::try_from(signal).unwrap(); +/// SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed); +/// } +/// +/// fn main() { +/// let handler = SigHandler::Handler(handle_sigint); +/// unsafe { signal::signal(Signal::SIGINT, handler) }.unwrap(); +/// } +/// ``` +/// +/// # Errors +/// +/// Returns [`Error(Errno::EOPNOTSUPP)`] if `handler` is +/// [`SigAction`][SigActionStruct]. Use [`sigaction`][SigActionFn] instead. +/// +/// `signal` also returns any error from `libc::signal`, such as when an attempt +/// is made to catch a signal that cannot be caught or to ignore a signal that +/// cannot be ignored. +/// +/// [`Error::UnsupportedOperation`]: ../../enum.Error.html#variant.UnsupportedOperation +/// [SigActionStruct]: struct.SigAction.html +/// [sigactionFn]: fn.sigaction.html +pub unsafe fn signal(signal: Signal, handler: SigHandler) -> Result { + let signal = signal as libc::c_int; + let res = match handler { + SigHandler::SigDfl => libc::signal(signal, libc::SIG_DFL), + SigHandler::SigIgn => libc::signal(signal, libc::SIG_IGN), + SigHandler::Handler(handler) => libc::signal(signal, handler as libc::sighandler_t), + #[cfg(not(target_os = "redox"))] + SigHandler::SigAction(_) => return Err(Errno::ENOTSUP), + }; + Errno::result(res).map(|oldhandler| { + match oldhandler { + libc::SIG_DFL => SigHandler::SigDfl, + libc::SIG_IGN => SigHandler::SigIgn, + p => SigHandler::Handler( + *(&p as *const usize + as *const extern fn(libc::c_int)) + as extern fn(libc::c_int)), + } + }) +} + +fn do_pthread_sigmask(how: SigmaskHow, + set: Option<&SigSet>, + oldset: Option<*mut libc::sigset_t>) -> Result<()> { + if set.is_none() && oldset.is_none() { + return Ok(()) + } + + let res = unsafe { + // if set or oldset is None, pass in null pointers instead + libc::pthread_sigmask(how as libc::c_int, + set.map_or_else(ptr::null::, + |s| &s.sigset as *const libc::sigset_t), + oldset.unwrap_or(ptr::null_mut()) + ) + }; + + Errno::result(res).map(drop) +} + +/// Manages the signal mask (set of blocked signals) for the calling thread. +/// +/// If the `set` parameter is `Some(..)`, then the signal mask will be updated with the signal set. +/// The `how` flag decides the type of update. If `set` is `None`, `how` will be ignored, +/// and no modification will take place. +/// +/// If the 'oldset' parameter is `Some(..)` then the current signal mask will be written into it. +/// +/// If both `set` and `oldset` is `Some(..)`, the current signal mask will be written into oldset, +/// and then it will be updated with `set`. +/// +/// If both `set` and `oldset` is None, this function is a no-op. +/// +/// For more information, visit the [`pthread_sigmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html), +/// or [`sigprocmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html) man pages. +pub fn pthread_sigmask(how: SigmaskHow, + set: Option<&SigSet>, + oldset: Option<&mut SigSet>) -> Result<()> +{ + do_pthread_sigmask(how, set, oldset.map(|os| &mut os.sigset as *mut _ )) +} + +/// Examine and change blocked signals. +/// +/// For more informations see the [`sigprocmask` man +/// pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html). +pub fn sigprocmask(how: SigmaskHow, set: Option<&SigSet>, oldset: Option<&mut SigSet>) -> Result<()> { + if set.is_none() && oldset.is_none() { + return Ok(()) + } + + let res = unsafe { + // if set or oldset is None, pass in null pointers instead + libc::sigprocmask(how as libc::c_int, + set.map_or_else(ptr::null::, + |s| &s.sigset as *const libc::sigset_t), + oldset.map_or_else(ptr::null_mut::, + |os| &mut os.sigset as *mut libc::sigset_t)) + }; + + Errno::result(res).map(drop) +} + +/// Send a signal to a process +/// +/// # Arguments +/// +/// * `pid` - Specifies which processes should receive the signal. +/// - If positive, specifies an individual process +/// - If zero, the signal will be sent to all processes whose group +/// ID is equal to the process group ID of the sender. This is a +/// variant of [`killpg`]. +/// - If `-1` and the process has super-user privileges, the signal +/// is sent to all processes exclusing system processes. +/// - If less than `-1`, the signal is sent to all processes whose +/// process group ID is equal to the absolute value of `pid`. +/// * `signal` - Signal to send. If 0, error checking if performed but no +/// signal is actually sent. +/// +/// See Also +/// [`kill(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html) +pub fn kill>>(pid: Pid, signal: T) -> Result<()> { + let res = unsafe { libc::kill(pid.into(), + match signal.into() { + Some(s) => s as libc::c_int, + None => 0, + }) }; + + Errno::result(res).map(drop) +} + +/// Send a signal to a process group +/// +/// # Arguments +/// +/// * `pgrp` - Process group to signal. If less then or equal 1, the behavior +/// is platform-specific. +/// * `signal` - Signal to send. If `None`, `killpg` will only preform error +/// checking and won't send any signal. +/// +/// See Also [killpg(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html). +#[cfg(not(target_os = "fuchsia"))] +pub fn killpg>>(pgrp: Pid, signal: T) -> Result<()> { + let res = unsafe { libc::killpg(pgrp.into(), + match signal.into() { + Some(s) => s as libc::c_int, + None => 0, + }) }; + + Errno::result(res).map(drop) +} + +/// Send a signal to the current thread +/// +/// See Also [raise(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html) +pub fn raise(signal: Signal) -> Result<()> { + let res = unsafe { libc::raise(signal as libc::c_int) }; + + Errno::result(res).map(drop) +} + + +/// Identifies a thread for [`SigevNotify::SigevThreadId`] +#[cfg(target_os = "freebsd")] +pub type type_of_thread_id = libc::lwpid_t; +/// Identifies a thread for [`SigevNotify::SigevThreadId`] +#[cfg(target_os = "linux")] +pub type type_of_thread_id = libc::pid_t; + +/// Specifies the notification method used by a [`SigEvent`] +// sigval is actually a union of a int and a void*. But it's never really used +// as a pointer, because neither libc nor the kernel ever dereference it. nix +// therefore presents it as an intptr_t, which is how kevent uses it. +#[cfg(not(any(target_os = "openbsd", target_os = "redox")))] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SigevNotify { + /// No notification will be delivered + SigevNone, + /// Notify by delivering a signal to the process. + SigevSignal { + /// Signal to deliver + signal: Signal, + /// Will be present in the `si_value` field of the [`libc::siginfo_t`] + /// structure of the queued signal. + si_value: libc::intptr_t + }, + // Note: SIGEV_THREAD is not implemented because libc::sigevent does not + // expose a way to set the union members needed by SIGEV_THREAD. + /// Notify by delivering an event to a kqueue. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevKevent { + /// File descriptor of the kqueue to notify. + kq: RawFd, + /// Will be contained in the kevent's `udata` field. + udata: libc::intptr_t + }, + /// Notify by delivering a signal to a thread. + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + SigevThreadId { + /// Signal to send + signal: Signal, + /// LWP ID of the thread to notify + thread_id: type_of_thread_id, + /// Will be present in the `si_value` field of the [`libc::siginfo_t`] + /// structure of the queued signal. + si_value: libc::intptr_t + }, +} + +#[cfg(not(any(target_os = "openbsd", target_os = "redox")))] +mod sigevent { + use std::mem; + use std::ptr; + use super::SigevNotify; + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + use super::type_of_thread_id; + + /// Used to request asynchronous notification of the completion of certain + /// events, such as POSIX AIO and timers. + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct SigEvent { + sigevent: libc::sigevent + } + + impl SigEvent { + /// **Note:** this constructor does not allow the user to set the + /// `sigev_notify_kevent_flags` field. That's considered ok because on FreeBSD + /// at least those flags don't do anything useful. That field is part of a + /// union that shares space with the more genuinely useful fields. + /// + /// **Note:** This constructor also doesn't allow the caller to set the + /// `sigev_notify_function` or `sigev_notify_attributes` fields, which are + /// required for `SIGEV_THREAD`. That's considered ok because on no operating + /// system is `SIGEV_THREAD` the most efficient way to deliver AIO + /// notification. FreeBSD and DragonFly BSD programs should prefer `SIGEV_KEVENT`. + /// Linux, Solaris, and portable programs should prefer `SIGEV_THREAD_ID` or + /// `SIGEV_SIGNAL`. That field is part of a union that shares space with the + /// more genuinely useful `sigev_notify_thread_id` + // Allow invalid_value warning on Fuchsia only. + // See https://github.com/nix-rust/nix/issues/1441 + #[cfg_attr(target_os = "fuchsia", allow(invalid_value))] + pub fn new(sigev_notify: SigevNotify) -> SigEvent { + let mut sev = unsafe { mem::MaybeUninit::::zeroed().assume_init() }; + sev.sigev_notify = match sigev_notify { + SigevNotify::SigevNone => libc::SIGEV_NONE, + SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevNotify::SigevKevent{..} => libc::SIGEV_KEVENT, + #[cfg(target_os = "freebsd")] + SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, + #[cfg(all(target_os = "linux", target_env = "gnu", not(target_arch = "mips")))] + SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, + #[cfg(any(all(target_os = "linux", target_env = "musl"), target_arch = "mips"))] + SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined + }; + sev.sigev_signo = match sigev_notify { + SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevNotify::SigevKevent{ kq, ..} => kq, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int, + _ => 0 + }; + sev.sigev_value.sival_ptr = match sigev_notify { + SigevNotify::SigevNone => ptr::null_mut::(), + SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void, + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void, + }; + SigEvent::set_tid(&mut sev, &sigev_notify); + SigEvent{sigevent: sev} + } + + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + fn set_tid(sev: &mut libc::sigevent, sigev_notify: &SigevNotify) { + sev.sigev_notify_thread_id = match *sigev_notify { + SigevNotify::SigevThreadId { thread_id, .. } => thread_id, + _ => 0 as type_of_thread_id + }; + } + + #[cfg(not(any(target_os = "freebsd", target_os = "linux")))] + fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) { + } + + /// Return a copy of the inner structure + pub fn sigevent(&self) -> libc::sigevent { + self.sigevent + } + } + + impl<'a> From<&'a libc::sigevent> for SigEvent { + fn from(sigevent: &libc::sigevent) -> Self { + SigEvent{ sigevent: *sigevent } + } + } +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "redox"))] + use std::thread; + use super::*; + + #[test] + fn test_contains() { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + assert!(mask.contains(SIGUSR1)); + assert!(!mask.contains(SIGUSR2)); + + let all = SigSet::all(); + assert!(all.contains(SIGUSR1)); + assert!(all.contains(SIGUSR2)); + } + + #[test] + fn test_clear() { + let mut set = SigSet::all(); + set.clear(); + for signal in Signal::iterator() { + assert!(!set.contains(signal)); + } + } + + #[test] + fn test_from_str_round_trips() { + for signal in Signal::iterator() { + assert_eq!(signal.as_ref().parse::().unwrap(), signal); + assert_eq!(signal.to_string().parse::().unwrap(), signal); + } + } + + #[test] + fn test_from_str_invalid_value() { + let errval = Err(Errno::EINVAL); + assert_eq!("NOSIGNAL".parse::(), errval); + assert_eq!("kill".parse::(), errval); + assert_eq!("9".parse::(), errval); + } + + #[test] + fn test_extend() { + let mut one_signal = SigSet::empty(); + one_signal.add(SIGUSR1); + + let mut two_signals = SigSet::empty(); + two_signals.add(SIGUSR2); + two_signals.extend(&one_signal); + + assert!(two_signals.contains(SIGUSR1)); + assert!(two_signals.contains(SIGUSR2)); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_thread_signal_set_mask() { + thread::spawn(|| { + let prev_mask = SigSet::thread_get_mask() + .expect("Failed to get existing signal mask!"); + + let mut test_mask = prev_mask; + test_mask.add(SIGUSR1); + + assert!(test_mask.thread_set_mask().is_ok()); + let new_mask = SigSet::thread_get_mask() + .expect("Failed to get new mask!"); + + assert!(new_mask.contains(SIGUSR1)); + assert!(!new_mask.contains(SIGUSR2)); + + prev_mask.thread_set_mask().expect("Failed to revert signal mask!"); + }).join().unwrap(); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_thread_signal_block() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + assert!(mask.thread_block().is_ok()); + + assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); + }).join().unwrap(); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_thread_signal_unblock() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + assert!(mask.thread_unblock().is_ok()); + + assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); + }).join().unwrap(); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_thread_signal_swap() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + mask.thread_block().unwrap(); + + assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); + + let mut mask2 = SigSet::empty(); + mask2.add(SIGUSR2); + + let oldmask = mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK) + .unwrap(); + + assert!(oldmask.contains(SIGUSR1)); + assert!(!oldmask.contains(SIGUSR2)); + + assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2)); + }).join().unwrap(); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_sigaction() { + thread::spawn(|| { + extern fn test_sigaction_handler(_: libc::c_int) {} + extern fn test_sigaction_action(_: libc::c_int, + _: *mut libc::siginfo_t, _: *mut libc::c_void) {} + + let handler_sig = SigHandler::Handler(test_sigaction_handler); + + let flags = SaFlags::SA_ONSTACK | SaFlags::SA_RESTART | + SaFlags::SA_SIGINFO; + + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + let action_sig = SigAction::new(handler_sig, flags, mask); + + assert_eq!(action_sig.flags(), + SaFlags::SA_ONSTACK | SaFlags::SA_RESTART); + assert_eq!(action_sig.handler(), handler_sig); + + mask = action_sig.mask(); + assert!(mask.contains(SIGUSR1)); + assert!(!mask.contains(SIGUSR2)); + + let handler_act = SigHandler::SigAction(test_sigaction_action); + let action_act = SigAction::new(handler_act, flags, mask); + assert_eq!(action_act.handler(), handler_act); + + let action_dfl = SigAction::new(SigHandler::SigDfl, flags, mask); + assert_eq!(action_dfl.handler(), SigHandler::SigDfl); + + let action_ign = SigAction::new(SigHandler::SigIgn, flags, mask); + assert_eq!(action_ign.handler(), SigHandler::SigIgn); + }).join().unwrap(); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_sigwait() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + mask.add(SIGUSR2); + mask.thread_block().unwrap(); + + raise(SIGUSR1).unwrap(); + assert_eq!(mask.wait().unwrap(), SIGUSR1); + }).join().unwrap(); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/signalfd.rs b/vendor/nix-v0.23.1-patched/src/sys/signalfd.rs new file mode 100644 index 000000000..bc4a45224 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/signalfd.rs @@ -0,0 +1,169 @@ +//! Interface for the `signalfd` syscall. +//! +//! # Signal discarding +//! When a signal can't be delivered to a process (or thread), it will become a pending signal. +//! Failure to deliver could happen if the signal is blocked by every thread in the process or if +//! the signal handler is still handling a previous signal. +//! +//! If a signal is sent to a process (or thread) that already has a pending signal of the same +//! type, it will be discarded. This means that if signals of the same type are received faster than +//! they are processed, some of those signals will be dropped. Because of this limitation, +//! `signalfd` in itself cannot be used for reliable communication between processes or threads. +//! +//! Once the signal is unblocked, or the signal handler is finished, and a signal is still pending +//! (ie. not consumed from a signalfd) it will be delivered to the signal handler. +//! +//! Please note that signal discarding is not specific to `signalfd`, but also happens with regular +//! signal handlers. +use crate::unistd; +use crate::Result; +use crate::errno::Errno; +pub use crate::sys::signal::{self, SigSet}; +pub use libc::signalfd_siginfo as siginfo; + +use std::os::unix::io::{RawFd, AsRawFd}; +use std::mem; + + +libc_bitflags!{ + pub struct SfdFlags: libc::c_int { + SFD_NONBLOCK; + SFD_CLOEXEC; + } +} + +pub const SIGNALFD_NEW: RawFd = -1; +#[deprecated(since = "0.23.0", note = "use mem::size_of::() instead")] +pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::(); + +/// Creates a new file descriptor for reading signals. +/// +/// **Important:** please read the module level documentation about signal discarding before using +/// this function! +/// +/// The `mask` parameter specifies the set of signals that can be accepted via this file descriptor. +/// +/// A signal must be blocked on every thread in a process, otherwise it won't be visible from +/// signalfd (the default handler will be invoked instead). +/// +/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html) +pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result { + unsafe { + Errno::result(libc::signalfd(fd as libc::c_int, mask.as_ref(), flags.bits())) + } +} + +/// A helper struct for creating, reading and closing a `signalfd` instance. +/// +/// **Important:** please read the module level documentation about signal discarding before using +/// this struct! +/// +/// # Examples +/// +/// ``` +/// # use nix::sys::signalfd::*; +/// // Set the thread to block the SIGUSR1 signal, otherwise the default handler will be used +/// let mut mask = SigSet::empty(); +/// mask.add(signal::SIGUSR1); +/// mask.thread_block().unwrap(); +/// +/// // Signals are queued up on the file descriptor +/// let mut sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap(); +/// +/// match sfd.read_signal() { +/// // we caught a signal +/// Ok(Some(sig)) => (), +/// // there were no signals waiting (only happens when the SFD_NONBLOCK flag is set, +/// // otherwise the read_signal call blocks) +/// Ok(None) => (), +/// Err(err) => (), // some error happend +/// } +/// ``` +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct SignalFd(RawFd); + +impl SignalFd { + pub fn new(mask: &SigSet) -> Result { + Self::with_flags(mask, SfdFlags::empty()) + } + + pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result { + let fd = signalfd(SIGNALFD_NEW, mask, flags)?; + + Ok(SignalFd(fd)) + } + + pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> { + signalfd(self.0, mask, SfdFlags::empty()).map(drop) + } + + pub fn read_signal(&mut self) -> Result> { + let mut buffer = mem::MaybeUninit::::uninit(); + + let size = mem::size_of_val(&buffer); + let res = Errno::result(unsafe { + libc::read(self.0, buffer.as_mut_ptr() as *mut libc::c_void, size) + }).map(|r| r as usize); + match res { + Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })), + Ok(_) => unreachable!("partial read on signalfd"), + Err(Errno::EAGAIN) => Ok(None), + Err(error) => Err(error) + } + } +} + +impl Drop for SignalFd { + fn drop(&mut self) { + let e = unistd::close(self.0); + if !std::thread::panicking() && e == Err(Errno::EBADF) { + panic!("Closing an invalid file descriptor!"); + }; + } +} + +impl AsRawFd for SignalFd { + fn as_raw_fd(&self) -> RawFd { + self.0 + } +} + +impl Iterator for SignalFd { + type Item = siginfo; + + fn next(&mut self) -> Option { + match self.read_signal() { + Ok(Some(sig)) => Some(sig), + Ok(None) | Err(_) => None, + } + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn create_signalfd() { + let mask = SigSet::empty(); + let fd = SignalFd::new(&mask); + assert!(fd.is_ok()); + } + + #[test] + fn create_signalfd_with_opts() { + let mask = SigSet::empty(); + let fd = SignalFd::with_flags(&mask, SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK); + assert!(fd.is_ok()); + } + + #[test] + fn read_empty_signalfd() { + let mask = SigSet::empty(); + let mut fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap(); + + let res = fd.read_signal(); + assert!(res.unwrap().is_none()); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/socket/addr.rs b/vendor/nix-v0.23.1-patched/src/sys/socket/addr.rs new file mode 100644 index 000000000..b119642b3 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/socket/addr.rs @@ -0,0 +1,1447 @@ +use super::sa_family_t; +use crate::{Result, NixPath}; +use crate::errno::Errno; +use memoffset::offset_of; +use std::{fmt, mem, net, ptr, slice}; +use std::ffi::OsStr; +use std::hash::{Hash, Hasher}; +use std::path::Path; +use std::os::unix::ffi::OsStrExt; +#[cfg(any(target_os = "android", target_os = "linux"))] +use crate::sys::socket::addr::netlink::NetlinkAddr; +#[cfg(any(target_os = "android", target_os = "linux"))] +use crate::sys::socket::addr::alg::AlgAddr; +#[cfg(any(target_os = "ios", target_os = "macos"))] +use std::os::unix::io::RawFd; +#[cfg(any(target_os = "ios", target_os = "macos"))] +use crate::sys::socket::addr::sys_control::SysControlAddr; +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "fuchsia"))] +pub use self::datalink::LinkAddr; +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::vsock::VsockAddr; + +/// These constants specify the protocol family to be used +/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html) +#[repr(i32)] +#[non_exhaustive] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub enum AddressFamily { + /// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html)) + Unix = libc::AF_UNIX, + /// IPv4 Internet protocols (see [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html)) + Inet = libc::AF_INET, + /// IPv6 Internet protocols (see [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html)) + Inet6 = libc::AF_INET6, + /// Kernel user interface device (see [`netlink(7)`](https://man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + Netlink = libc::AF_NETLINK, + /// Low level packet interface (see [`packet(7)`](https://man7.org/linux/man-pages/man7/packet.7.html)) + #[cfg(any(target_os = "android", + target_os = "linux", + target_os = "illumos", + target_os = "fuchsia", + target_os = "solaris"))] + Packet = libc::AF_PACKET, + /// KEXT Controls and Notifications + #[cfg(any(target_os = "ios", target_os = "macos"))] + System = libc::AF_SYSTEM, + /// Amateur radio AX.25 protocol + #[cfg(any(target_os = "android", target_os = "linux"))] + Ax25 = libc::AF_AX25, + /// IPX - Novell protocols + Ipx = libc::AF_IPX, + /// AppleTalk + AppleTalk = libc::AF_APPLETALK, + #[cfg(any(target_os = "android", target_os = "linux"))] + NetRom = libc::AF_NETROM, + #[cfg(any(target_os = "android", target_os = "linux"))] + Bridge = libc::AF_BRIDGE, + /// Access to raw ATM PVCs + #[cfg(any(target_os = "android", target_os = "linux"))] + AtmPvc = libc::AF_ATMPVC, + /// ITU-T X.25 / ISO-8208 protocol (see [`x25(7)`](https://man7.org/linux/man-pages/man7/x25.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + X25 = libc::AF_X25, + #[cfg(any(target_os = "android", target_os = "linux"))] + Rose = libc::AF_ROSE, + Decnet = libc::AF_DECnet, + #[cfg(any(target_os = "android", target_os = "linux"))] + NetBeui = libc::AF_NETBEUI, + #[cfg(any(target_os = "android", target_os = "linux"))] + Security = libc::AF_SECURITY, + #[cfg(any(target_os = "android", target_os = "linux"))] + Key = libc::AF_KEY, + #[cfg(any(target_os = "android", target_os = "linux"))] + Ash = libc::AF_ASH, + #[cfg(any(target_os = "android", target_os = "linux"))] + Econet = libc::AF_ECONET, + #[cfg(any(target_os = "android", target_os = "linux"))] + AtmSvc = libc::AF_ATMSVC, + #[cfg(any(target_os = "android", target_os = "linux"))] + Rds = libc::AF_RDS, + Sna = libc::AF_SNA, + #[cfg(any(target_os = "android", target_os = "linux"))] + Irda = libc::AF_IRDA, + #[cfg(any(target_os = "android", target_os = "linux"))] + Pppox = libc::AF_PPPOX, + #[cfg(any(target_os = "android", target_os = "linux"))] + Wanpipe = libc::AF_WANPIPE, + #[cfg(any(target_os = "android", target_os = "linux"))] + Llc = libc::AF_LLC, + #[cfg(target_os = "linux")] + Ib = libc::AF_IB, + #[cfg(target_os = "linux")] + Mpls = libc::AF_MPLS, + #[cfg(any(target_os = "android", target_os = "linux"))] + Can = libc::AF_CAN, + #[cfg(any(target_os = "android", target_os = "linux"))] + Tipc = libc::AF_TIPC, + #[cfg(not(any(target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "solaris")))] + Bluetooth = libc::AF_BLUETOOTH, + #[cfg(any(target_os = "android", target_os = "linux"))] + Iucv = libc::AF_IUCV, + #[cfg(any(target_os = "android", target_os = "linux"))] + RxRpc = libc::AF_RXRPC, + #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] + Isdn = libc::AF_ISDN, + #[cfg(any(target_os = "android", target_os = "linux"))] + Phonet = libc::AF_PHONET, + #[cfg(any(target_os = "android", target_os = "linux"))] + Ieee802154 = libc::AF_IEEE802154, + #[cfg(any(target_os = "android", target_os = "linux"))] + Caif = libc::AF_CAIF, + /// Interface to kernel crypto API + #[cfg(any(target_os = "android", target_os = "linux"))] + Alg = libc::AF_ALG, + #[cfg(target_os = "linux")] + Nfc = libc::AF_NFC, + #[cfg(any(target_os = "android", target_os = "linux"))] + Vsock = libc::AF_VSOCK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + ImpLink = libc::AF_IMPLINK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Pup = libc::AF_PUP, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Chaos = libc::AF_CHAOS, + #[cfg(any(target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Ns = libc::AF_NS, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Iso = libc::AF_ISO, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Datakit = libc::AF_DATAKIT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Ccitt = libc::AF_CCITT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Dli = libc::AF_DLI, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Lat = libc::AF_LAT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Hylink = libc::AF_HYLINK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd"))] + Link = libc::AF_LINK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Coip = libc::AF_COIP, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Cnt = libc::AF_CNT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Natm = libc::AF_NATM, + /// Unspecified address family, (see [`getaddrinfo(3)`](https://man7.org/linux/man-pages/man3/getaddrinfo.3.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + Unspec = libc::AF_UNSPEC, +} + +impl AddressFamily { + /// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from + /// the `sa_family` field of a `sockaddr`. + /// + /// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet + /// and System. Returns None for unsupported or unknown address families. + pub const fn from_i32(family: i32) -> Option { + match family { + libc::AF_UNIX => Some(AddressFamily::Unix), + libc::AF_INET => Some(AddressFamily::Inet), + libc::AF_INET6 => Some(AddressFamily::Inet6), + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_NETLINK => Some(AddressFamily::Netlink), + #[cfg(any(target_os = "macos", target_os = "macos"))] + libc::AF_SYSTEM => Some(AddressFamily::System), + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_PACKET => Some(AddressFamily::Packet), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "illumos", + target_os = "openbsd"))] + libc::AF_LINK => Some(AddressFamily::Link), + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_VSOCK => Some(AddressFamily::Vsock), + _ => None + } + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum InetAddr { + V4(libc::sockaddr_in), + V6(libc::sockaddr_in6), +} + +impl InetAddr { + #[allow(clippy::needless_update)] // It isn't needless on all OSes + pub fn from_std(std: &net::SocketAddr) -> InetAddr { + match *std { + net::SocketAddr::V4(ref addr) => { + InetAddr::V4(libc::sockaddr_in { + sin_family: AddressFamily::Inet as sa_family_t, + sin_port: addr.port().to_be(), // network byte order + sin_addr: Ipv4Addr::from_std(addr.ip()).0, + .. unsafe { mem::zeroed() } + }) + } + net::SocketAddr::V6(ref addr) => { + InetAddr::V6(libc::sockaddr_in6 { + sin6_family: AddressFamily::Inet6 as sa_family_t, + sin6_port: addr.port().to_be(), // network byte order + sin6_addr: Ipv6Addr::from_std(addr.ip()).0, + sin6_flowinfo: addr.flowinfo(), // host byte order + sin6_scope_id: addr.scope_id(), // host byte order + .. unsafe { mem::zeroed() } + }) + } + } + } + + #[allow(clippy::needless_update)] // It isn't needless on all OSes + pub fn new(ip: IpAddr, port: u16) -> InetAddr { + match ip { + IpAddr::V4(ref ip) => { + InetAddr::V4(libc::sockaddr_in { + sin_family: AddressFamily::Inet as sa_family_t, + sin_port: port.to_be(), + sin_addr: ip.0, + .. unsafe { mem::zeroed() } + }) + } + IpAddr::V6(ref ip) => { + InetAddr::V6(libc::sockaddr_in6 { + sin6_family: AddressFamily::Inet6 as sa_family_t, + sin6_port: port.to_be(), + sin6_addr: ip.0, + .. unsafe { mem::zeroed() } + }) + } + } + } + /// Gets the IP address associated with this socket address. + pub const fn ip(&self) -> IpAddr { + match *self { + InetAddr::V4(ref sa) => IpAddr::V4(Ipv4Addr(sa.sin_addr)), + InetAddr::V6(ref sa) => IpAddr::V6(Ipv6Addr(sa.sin6_addr)), + } + } + + /// Gets the port number associated with this socket address + pub const fn port(&self) -> u16 { + match *self { + InetAddr::V6(ref sa) => u16::from_be(sa.sin6_port), + InetAddr::V4(ref sa) => u16::from_be(sa.sin_port), + } + } + + pub fn to_std(&self) -> net::SocketAddr { + match *self { + InetAddr::V4(ref sa) => net::SocketAddr::V4( + net::SocketAddrV4::new( + Ipv4Addr(sa.sin_addr).to_std(), + self.port())), + InetAddr::V6(ref sa) => net::SocketAddr::V6( + net::SocketAddrV6::new( + Ipv6Addr(sa.sin6_addr).to_std(), + self.port(), + sa.sin6_flowinfo, + sa.sin6_scope_id)), + } + } + + #[deprecated(since = "0.23.0", note = "use .to_string() instead")] + pub fn to_str(&self) -> String { + format!("{}", self) + } +} + +impl fmt::Display for InetAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + InetAddr::V4(_) => write!(f, "{}:{}", self.ip(), self.port()), + InetAddr::V6(_) => write!(f, "[{}]:{}", self.ip(), self.port()), + } + } +} + +/* + * + * ===== IpAddr ===== + * + */ +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum IpAddr { + V4(Ipv4Addr), + V6(Ipv6Addr), +} + +impl IpAddr { + /// Create a new IpAddr that contains an IPv4 address. + /// + /// The result will represent the IP address a.b.c.d + pub const fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr { + IpAddr::V4(Ipv4Addr::new(a, b, c, d)) + } + + /// Create a new IpAddr that contains an IPv6 address. + /// + /// The result will represent the IP address a:b:c:d:e:f + #[allow(clippy::many_single_char_names)] + #[allow(clippy::too_many_arguments)] + pub const fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr { + IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)) + } + + pub fn from_std(std: &net::IpAddr) -> IpAddr { + match *std { + net::IpAddr::V4(ref std) => IpAddr::V4(Ipv4Addr::from_std(std)), + net::IpAddr::V6(ref std) => IpAddr::V6(Ipv6Addr::from_std(std)), + } + } + + pub const fn to_std(&self) -> net::IpAddr { + match *self { + IpAddr::V4(ref ip) => net::IpAddr::V4(ip.to_std()), + IpAddr::V6(ref ip) => net::IpAddr::V6(ip.to_std()), + } + } +} + +impl fmt::Display for IpAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + IpAddr::V4(ref v4) => v4.fmt(f), + IpAddr::V6(ref v6) => v6.fmt(f) + } + } +} + +/* + * + * ===== Ipv4Addr ===== + * + */ + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Ipv4Addr(pub libc::in_addr); + +impl Ipv4Addr { + #[allow(clippy::identity_op)] // More readable this way + pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { + let ip = (((a as u32) << 24) | + ((b as u32) << 16) | + ((c as u32) << 8) | + ((d as u32) << 0)).to_be(); + + Ipv4Addr(libc::in_addr { s_addr: ip }) + } + + // Use pass by reference for symmetry with Ipv6Addr::from_std + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn from_std(std: &net::Ipv4Addr) -> Ipv4Addr { + let bits = std.octets(); + Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3]) + } + + pub const fn any() -> Ipv4Addr { + Ipv4Addr(libc::in_addr { s_addr: libc::INADDR_ANY }) + } + + pub const fn octets(self) -> [u8; 4] { + let bits = u32::from_be(self.0.s_addr); + [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8] + } + + pub const fn to_std(self) -> net::Ipv4Addr { + let bits = self.octets(); + net::Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3]) + } +} + +impl fmt::Display for Ipv4Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let octets = self.octets(); + write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) + } +} + +/* + * + * ===== Ipv6Addr ===== + * + */ + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Ipv6Addr(pub libc::in6_addr); + +// Note that IPv6 addresses are stored in big endian order on all architectures. +// See https://tools.ietf.org/html/rfc1700 or consult your favorite search +// engine. + +macro_rules! to_u8_array { + ($($num:ident),*) => { + [ $(($num>>8) as u8, ($num&0xff) as u8,)* ] + } +} + +macro_rules! to_u16_array { + ($slf:ident, $($first:expr, $second:expr),*) => { + [$( (($slf.0.s6_addr[$first] as u16) << 8) + $slf.0.s6_addr[$second] as u16,)*] + } +} + +impl Ipv6Addr { + #[allow(clippy::many_single_char_names)] + #[allow(clippy::too_many_arguments)] + pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { + Ipv6Addr(libc::in6_addr{s6_addr: to_u8_array!(a,b,c,d,e,f,g,h)}) + } + + pub fn from_std(std: &net::Ipv6Addr) -> Ipv6Addr { + let s = std.segments(); + Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]) + } + + /// Return the eight 16-bit segments that make up this address + pub const fn segments(&self) -> [u16; 8] { + to_u16_array!(self, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) + } + + pub const fn to_std(&self) -> net::Ipv6Addr { + let s = self.segments(); + net::Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]) + } +} + +impl fmt::Display for Ipv6Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.to_std().fmt(fmt) + } +} + +/// A wrapper around `sockaddr_un`. +#[derive(Clone, Copy, Debug)] +pub struct UnixAddr { + // INVARIANT: sun & path_len are valid as defined by docs for from_raw_parts + sun: libc::sockaddr_un, + path_len: usize, +} + +// linux man page unix(7) says there are 3 kinds of unix socket: +// pathname: addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1 +// unnamed: addrlen = sizeof(sa_family_t) +// abstract: addren > sizeof(sa_family_t), name = sun_path[..(addrlen - sizeof(sa_family_t))] +// +// what we call path_len = addrlen - offsetof(struct sockaddr_un, sun_path) +#[derive(PartialEq, Eq, Hash)] +enum UnixAddrKind<'a> { + Pathname(&'a Path), + Unnamed, + #[cfg(any(target_os = "android", target_os = "linux"))] + Abstract(&'a [u8]), +} +impl<'a> UnixAddrKind<'a> { + /// Safety: sun & path_len must be valid + unsafe fn get(sun: &'a libc::sockaddr_un, path_len: usize) -> Self { + if path_len == 0 { + return Self::Unnamed; + } + #[cfg(any(target_os = "android", target_os = "linux"))] + if sun.sun_path[0] == 0 { + let name = + slice::from_raw_parts(sun.sun_path.as_ptr().add(1) as *const u8, path_len - 1); + return Self::Abstract(name); + } + let pathname = slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len - 1); + Self::Pathname(Path::new(OsStr::from_bytes(pathname))) + } +} + +impl UnixAddr { + /// Create a new sockaddr_un representing a filesystem path. + pub fn new(path: &P) -> Result { + path.with_nix_path(|cstr| { + unsafe { + let mut ret = libc::sockaddr_un { + sun_family: AddressFamily::Unix as sa_family_t, + .. mem::zeroed() + }; + + let bytes = cstr.to_bytes(); + + if bytes.len() >= ret.sun_path.len() { + return Err(Errno::ENAMETOOLONG); + } + + ptr::copy_nonoverlapping(bytes.as_ptr(), + ret.sun_path.as_mut_ptr() as *mut u8, + bytes.len()); + + Ok(UnixAddr::from_raw_parts(ret, bytes.len() + 1)) + } + })? + } + + /// Create a new `sockaddr_un` representing an address in the "abstract namespace". + /// + /// The leading null byte for the abstract namespace is automatically added; + /// thus the input `path` is expected to be the bare name, not null-prefixed. + /// This is a Linux-specific extension, primarily used to allow chrooted + /// processes to communicate with processes having a different filesystem view. + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn new_abstract(path: &[u8]) -> Result { + unsafe { + let mut ret = libc::sockaddr_un { + sun_family: AddressFamily::Unix as sa_family_t, + .. mem::zeroed() + }; + + if path.len() >= ret.sun_path.len() { + return Err(Errno::ENAMETOOLONG); + } + + // Abstract addresses are represented by sun_path[0] == + // b'\0', so copy starting one byte in. + ptr::copy_nonoverlapping(path.as_ptr(), + ret.sun_path.as_mut_ptr().offset(1) as *mut u8, + path.len()); + + Ok(UnixAddr::from_raw_parts(ret, path.len() + 1)) + } + } + + /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `path_len` is the "addrlen" + /// of this address, but minus `offsetof(struct sockaddr_un, sun_path)`. Basically the length + /// of the data in `sun_path`. + /// + /// # Safety + /// This pair of sockaddr_un & path_len must be a valid unix addr, which means: + /// - path_len <= sockaddr_un.sun_path.len() + /// - if this is a unix addr with a pathname, sun.sun_path is a nul-terminated fs path and + /// sun.sun_path[path_len - 1] == 0 || sun.sun_path[path_len] == 0 + pub(crate) unsafe fn from_raw_parts(sun: libc::sockaddr_un, mut path_len: usize) -> UnixAddr { + if let UnixAddrKind::Pathname(_) = UnixAddrKind::get(&sun, path_len) { + if sun.sun_path[path_len - 1] != 0 { + assert_eq!(sun.sun_path[path_len], 0); + path_len += 1 + } + } + UnixAddr { sun, path_len } + } + + fn kind(&self) -> UnixAddrKind<'_> { + // SAFETY: our sockaddr is always valid because of the invariant on the struct + unsafe { UnixAddrKind::get(&self.sun, self.path_len) } + } + + /// If this address represents a filesystem path, return that path. + pub fn path(&self) -> Option<&Path> { + match self.kind() { + UnixAddrKind::Pathname(path) => Some(path), + _ => None, + } + } + + /// If this address represents an abstract socket, return its name. + /// + /// For abstract sockets only the bare name is returned, without the + /// leading null byte. `None` is returned for unnamed or path-backed sockets. + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn as_abstract(&self) -> Option<&[u8]> { + match self.kind() { + UnixAddrKind::Abstract(name) => Some(name), + _ => None, + } + } + + /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)` + #[inline] + pub fn path_len(&self) -> usize { + self.path_len + } + /// Returns a pointer to the raw `sockaddr_un` struct + #[inline] + pub fn as_ptr(&self) -> *const libc::sockaddr_un { + &self.sun + } + /// Returns a mutable pointer to the raw `sockaddr_un` struct + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un { + &mut self.sun + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result { + use fmt::Write; + f.write_str("@\"")?; + for &b in abs { + use fmt::Display; + char::from(b).escape_default().fmt(f)?; + } + f.write_char('"')?; + Ok(()) +} + +impl fmt::Display for UnixAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.kind() { + UnixAddrKind::Pathname(path) => path.display().fmt(f), + UnixAddrKind::Unnamed => f.pad(""), + #[cfg(any(target_os = "android", target_os = "linux"))] + UnixAddrKind::Abstract(name) => fmt_abstract(name, f), + } + } +} + +impl PartialEq for UnixAddr { + fn eq(&self, other: &UnixAddr) -> bool { + self.kind() == other.kind() + } +} + +impl Eq for UnixAddr {} + +impl Hash for UnixAddr { + fn hash(&self, s: &mut H) { + self.kind().hash(s) + } +} + +/// Represents a socket address +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[non_exhaustive] +pub enum SockAddr { + Inet(InetAddr), + Unix(UnixAddr), + #[cfg(any(target_os = "android", target_os = "linux"))] + Netlink(NetlinkAddr), + #[cfg(any(target_os = "android", target_os = "linux"))] + Alg(AlgAddr), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SysControl(SysControlAddr), + /// Datalink address (MAC) + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd"))] + Link(LinkAddr), + #[cfg(any(target_os = "android", target_os = "linux"))] + Vsock(VsockAddr), +} + +impl SockAddr { + pub fn new_inet(addr: InetAddr) -> SockAddr { + SockAddr::Inet(addr) + } + + pub fn new_unix(path: &P) -> Result { + Ok(SockAddr::Unix(UnixAddr::new(path)?)) + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn new_netlink(pid: u32, groups: u32) -> SockAddr { + SockAddr::Netlink(NetlinkAddr::new(pid, groups)) + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn new_alg(alg_type: &str, alg_name: &str) -> SockAddr { + SockAddr::Alg(AlgAddr::new(alg_type, alg_name)) + } + + #[cfg(any(target_os = "ios", target_os = "macos"))] + pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result { + SysControlAddr::from_name(sockfd, name, unit).map(SockAddr::SysControl) + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn new_vsock(cid: u32, port: u32) -> SockAddr { + SockAddr::Vsock(VsockAddr::new(cid, port)) + } + + pub fn family(&self) -> AddressFamily { + match *self { + SockAddr::Inet(InetAddr::V4(..)) => AddressFamily::Inet, + SockAddr::Inet(InetAddr::V6(..)) => AddressFamily::Inet6, + SockAddr::Unix(..) => AddressFamily::Unix, + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(..) => AddressFamily::Netlink, + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Alg(..) => AddressFamily::Alg, + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(..) => AddressFamily::System, + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Link(..) => AddressFamily::Packet, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "illumos", + target_os = "openbsd"))] + SockAddr::Link(..) => AddressFamily::Link, + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Vsock(..) => AddressFamily::Vsock, + } + } + + #[deprecated(since = "0.23.0", note = "use .to_string() instead")] + pub fn to_str(&self) -> String { + format!("{}", self) + } + + /// Creates a `SockAddr` struct from libc's sockaddr. + /// + /// Supports only the following address families: Unix, Inet (v4 & v6), Netlink and System. + /// Returns None for unsupported families. + /// + /// # Safety + /// + /// unsafe because it takes a raw pointer as argument. The caller must + /// ensure that the pointer is valid. + #[cfg(not(target_os = "fuchsia"))] + pub(crate) unsafe fn from_libc_sockaddr(addr: *const libc::sockaddr) -> Option { + if addr.is_null() { + None + } else { + match AddressFamily::from_i32(i32::from((*addr).sa_family)) { + Some(AddressFamily::Unix) => None, + Some(AddressFamily::Inet) => Some(SockAddr::Inet( + InetAddr::V4(*(addr as *const libc::sockaddr_in)))), + Some(AddressFamily::Inet6) => Some(SockAddr::Inet( + InetAddr::V6(*(addr as *const libc::sockaddr_in6)))), + #[cfg(any(target_os = "android", target_os = "linux"))] + Some(AddressFamily::Netlink) => Some(SockAddr::Netlink( + NetlinkAddr(*(addr as *const libc::sockaddr_nl)))), + #[cfg(any(target_os = "ios", target_os = "macos"))] + Some(AddressFamily::System) => Some(SockAddr::SysControl( + SysControlAddr(*(addr as *const libc::sockaddr_ctl)))), + #[cfg(any(target_os = "android", target_os = "linux"))] + Some(AddressFamily::Packet) => Some(SockAddr::Link( + LinkAddr(*(addr as *const libc::sockaddr_ll)))), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "illumos", + target_os = "openbsd"))] + Some(AddressFamily::Link) => { + let ether_addr = LinkAddr(*(addr as *const libc::sockaddr_dl)); + if ether_addr.is_empty() { + None + } else { + Some(SockAddr::Link(ether_addr)) + } + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + Some(AddressFamily::Vsock) => Some(SockAddr::Vsock( + VsockAddr(*(addr as *const libc::sockaddr_vm)))), + // Other address families are currently not supported and simply yield a None + // entry instead of a proper conversion to a `SockAddr`. + Some(_) | None => None, + } + } + } + + /// Conversion from nix's SockAddr type to the underlying libc sockaddr type. + /// + /// This is useful for interfacing with other libc functions that don't yet have nix wrappers. + /// Returns a reference to the underlying data type (as a sockaddr reference) along + /// with the size of the actual data type. sockaddr is commonly used as a proxy for + /// a superclass as C doesn't support inheritance, so many functions that take + /// a sockaddr * need to take the size of the underlying type as well and then internally cast it back. + pub fn as_ffi_pair(&self) -> (&libc::sockaddr, libc::socklen_t) { + match *self { + SockAddr::Inet(InetAddr::V4(ref addr)) => ( + // This cast is always allowed in C + unsafe { + &*(addr as *const libc::sockaddr_in as *const libc::sockaddr) + }, + mem::size_of_val(addr) as libc::socklen_t + ), + SockAddr::Inet(InetAddr::V6(ref addr)) => ( + // This cast is always allowed in C + unsafe { + &*(addr as *const libc::sockaddr_in6 as *const libc::sockaddr) + }, + mem::size_of_val(addr) as libc::socklen_t + ), + SockAddr::Unix(UnixAddr { ref sun, path_len }) => ( + // This cast is always allowed in C + unsafe { + &*(sun as *const libc::sockaddr_un as *const libc::sockaddr) + }, + (path_len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t + ), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(NetlinkAddr(ref sa)) => ( + // This cast is always allowed in C + unsafe { + &*(sa as *const libc::sockaddr_nl as *const libc::sockaddr) + }, + mem::size_of_val(sa) as libc::socklen_t + ), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Alg(AlgAddr(ref sa)) => ( + // This cast is always allowed in C + unsafe { + &*(sa as *const libc::sockaddr_alg as *const libc::sockaddr) + }, + mem::size_of_val(sa) as libc::socklen_t + ), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(SysControlAddr(ref sa)) => ( + // This cast is always allowed in C + unsafe { + &*(sa as *const libc::sockaddr_ctl as *const libc::sockaddr) + }, + mem::size_of_val(sa) as libc::socklen_t + + ), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Link(LinkAddr(ref addr)) => ( + // This cast is always allowed in C + unsafe { + &*(addr as *const libc::sockaddr_ll as *const libc::sockaddr) + }, + mem::size_of_val(addr) as libc::socklen_t + ), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd"))] + SockAddr::Link(LinkAddr(ref addr)) => ( + // This cast is always allowed in C + unsafe { + &*(addr as *const libc::sockaddr_dl as *const libc::sockaddr) + }, + mem::size_of_val(addr) as libc::socklen_t + ), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Vsock(VsockAddr(ref sa)) => ( + // This cast is always allowed in C + unsafe { + &*(sa as *const libc::sockaddr_vm as *const libc::sockaddr) + }, + mem::size_of_val(sa) as libc::socklen_t + ), + } + } +} + +impl fmt::Display for SockAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SockAddr::Inet(ref inet) => inet.fmt(f), + SockAddr::Unix(ref unix) => unix.fmt(f), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(ref nl) => nl.fmt(f), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Alg(ref nl) => nl.fmt(f), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(ref sc) => sc.fmt(f), + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "illumos", + target_os = "openbsd"))] + SockAddr::Link(ref ether_addr) => ether_addr.fmt(f), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Vsock(ref svm) => svm.fmt(f), + } + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod netlink { + use crate::sys::socket::addr::AddressFamily; + use libc::{sa_family_t, sockaddr_nl}; + use std::{fmt, mem}; + + #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] + pub struct NetlinkAddr(pub sockaddr_nl); + + impl NetlinkAddr { + pub fn new(pid: u32, groups: u32) -> NetlinkAddr { + let mut addr: sockaddr_nl = unsafe { mem::zeroed() }; + addr.nl_family = AddressFamily::Netlink as sa_family_t; + addr.nl_pid = pid; + addr.nl_groups = groups; + + NetlinkAddr(addr) + } + + pub const fn pid(&self) -> u32 { + self.0.nl_pid + } + + pub const fn groups(&self) -> u32 { + self.0.nl_groups + } + } + + impl fmt::Display for NetlinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "pid: {} groups: {}", self.pid(), self.groups()) + } + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod alg { + use libc::{AF_ALG, sockaddr_alg, c_char}; + use std::{fmt, mem, str}; + use std::hash::{Hash, Hasher}; + use std::ffi::CStr; + + #[derive(Copy, Clone)] + pub struct AlgAddr(pub sockaddr_alg); + + // , PartialEq, Eq, Debug, Hash + impl PartialEq for AlgAddr { + fn eq(&self, other: &Self) -> bool { + let (inner, other) = (self.0, other.0); + (inner.salg_family, &inner.salg_type[..], inner.salg_feat, inner.salg_mask, &inner.salg_name[..]) == + (other.salg_family, &other.salg_type[..], other.salg_feat, other.salg_mask, &other.salg_name[..]) + } + } + + impl Eq for AlgAddr {} + + impl Hash for AlgAddr { + fn hash(&self, s: &mut H) { + let inner = self.0; + (inner.salg_family, &inner.salg_type[..], inner.salg_feat, inner.salg_mask, &inner.salg_name[..]).hash(s); + } + } + + impl AlgAddr { + pub fn new(alg_type: &str, alg_name: &str) -> AlgAddr { + let mut addr: sockaddr_alg = unsafe { mem::zeroed() }; + addr.salg_family = AF_ALG as u16; + addr.salg_type[..alg_type.len()].copy_from_slice(alg_type.to_string().as_bytes()); + addr.salg_name[..alg_name.len()].copy_from_slice(alg_name.to_string().as_bytes()); + + AlgAddr(addr) + } + + + pub fn alg_type(&self) -> &CStr { + unsafe { CStr::from_ptr(self.0.salg_type.as_ptr() as *const c_char) } + } + + pub fn alg_name(&self) -> &CStr { + unsafe { CStr::from_ptr(self.0.salg_name.as_ptr() as *const c_char) } + } + } + + impl fmt::Display for AlgAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "type: {} alg: {}", + self.alg_name().to_string_lossy(), + self.alg_type().to_string_lossy()) + } + } + + impl fmt::Debug for AlgAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } +} + +#[cfg(any(target_os = "ios", target_os = "macos"))] +pub mod sys_control { + use crate::sys::socket::addr::AddressFamily; + use libc::{self, c_uchar}; + use std::{fmt, mem}; + use std::os::unix::io::RawFd; + use crate::{Errno, Result}; + + // FIXME: Move type into `libc` + #[repr(C)] + #[derive(Clone, Copy)] + #[allow(missing_debug_implementations)] + pub struct ctl_ioc_info { + pub ctl_id: u32, + pub ctl_name: [c_uchar; MAX_KCTL_NAME], + } + + const CTL_IOC_MAGIC: u8 = b'N'; + const CTL_IOC_INFO: u8 = 3; + const MAX_KCTL_NAME: usize = 96; + + ioctl_readwrite!(ctl_info, CTL_IOC_MAGIC, CTL_IOC_INFO, ctl_ioc_info); + + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct SysControlAddr(pub libc::sockaddr_ctl); + + impl SysControlAddr { + pub const fn new(id: u32, unit: u32) -> SysControlAddr { + let addr = libc::sockaddr_ctl { + sc_len: mem::size_of::() as c_uchar, + sc_family: AddressFamily::System as c_uchar, + ss_sysaddr: libc::AF_SYS_CONTROL as u16, + sc_id: id, + sc_unit: unit, + sc_reserved: [0; 5] + }; + + SysControlAddr(addr) + } + + pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result { + if name.len() > MAX_KCTL_NAME { + return Err(Errno::ENAMETOOLONG); + } + + let mut ctl_name = [0; MAX_KCTL_NAME]; + ctl_name[..name.len()].clone_from_slice(name.as_bytes()); + let mut info = ctl_ioc_info { ctl_id: 0, ctl_name }; + + unsafe { ctl_info(sockfd, &mut info)?; } + + Ok(SysControlAddr::new(info.ctl_id, unit)) + } + + pub const fn id(&self) -> u32 { + self.0.sc_id + } + + pub const fn unit(&self) -> u32 { + self.0.sc_unit + } + } + + impl fmt::Display for SysControlAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } + } +} + + +#[cfg(any(target_os = "android", target_os = "linux", target_os = "fuchsia"))] +mod datalink { + use super::{fmt, AddressFamily}; + + /// Hardware Address + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct LinkAddr(pub libc::sockaddr_ll); + + impl LinkAddr { + /// Always AF_PACKET + pub fn family(&self) -> AddressFamily { + assert_eq!(self.0.sll_family as i32, libc::AF_PACKET); + AddressFamily::Packet + } + + /// Physical-layer protocol + pub fn protocol(&self) -> u16 { + self.0.sll_protocol + } + + /// Interface number + pub fn ifindex(&self) -> usize { + self.0.sll_ifindex as usize + } + + /// ARP hardware type + pub fn hatype(&self) -> u16 { + self.0.sll_hatype + } + + /// Packet type + pub fn pkttype(&self) -> u8 { + self.0.sll_pkttype + } + + /// Length of MAC address + pub fn halen(&self) -> usize { + self.0.sll_halen as usize + } + + /// Physical-layer address (MAC) + pub fn addr(&self) -> [u8; 6] { + [ + self.0.sll_addr[0] as u8, + self.0.sll_addr[1] as u8, + self.0.sll_addr[2] as u8, + self.0.sll_addr[3] as u8, + self.0.sll_addr[4] as u8, + self.0.sll_addr[5] as u8, + ] + } + } + + impl fmt::Display for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let addr = self.addr(); + write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + addr[0], + addr[1], + addr[2], + addr[3], + addr[4], + addr[5]) + } + } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd"))] +mod datalink { + use super::{fmt, AddressFamily}; + + /// Hardware Address + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct LinkAddr(pub libc::sockaddr_dl); + + impl LinkAddr { + /// Total length of sockaddr + #[cfg(not(target_os = "illumos"))] + pub fn len(&self) -> usize { + self.0.sdl_len as usize + } + + /// always == AF_LINK + pub fn family(&self) -> AddressFamily { + assert_eq!(i32::from(self.0.sdl_family), libc::AF_LINK); + AddressFamily::Link + } + + /// interface index, if != 0, system given index for interface + pub fn ifindex(&self) -> usize { + self.0.sdl_index as usize + } + + /// Datalink type + pub fn datalink_type(&self) -> u8 { + self.0.sdl_type + } + + // MAC address start position + pub fn nlen(&self) -> usize { + self.0.sdl_nlen as usize + } + + /// link level address length + pub fn alen(&self) -> usize { + self.0.sdl_alen as usize + } + + /// link layer selector length + pub fn slen(&self) -> usize { + self.0.sdl_slen as usize + } + + /// if link level address length == 0, + /// or `sdl_data` not be larger. + pub fn is_empty(&self) -> bool { + let nlen = self.nlen(); + let alen = self.alen(); + let data_len = self.0.sdl_data.len(); + + alen == 0 || nlen + alen >= data_len + } + + /// Physical-layer address (MAC) + pub fn addr(&self) -> [u8; 6] { + let nlen = self.nlen(); + let data = self.0.sdl_data; + + assert!(!self.is_empty()); + + [ + data[nlen] as u8, + data[nlen + 1] as u8, + data[nlen + 2] as u8, + data[nlen + 3] as u8, + data[nlen + 4] as u8, + data[nlen + 5] as u8, + ] + } + } + + impl fmt::Display for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let addr = self.addr(); + write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + addr[0], + addr[1], + addr[2], + addr[3], + addr[4], + addr[5]) + } + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod vsock { + use crate::sys::socket::addr::AddressFamily; + use libc::{sa_family_t, sockaddr_vm}; + use std::{fmt, mem}; + use std::hash::{Hash, Hasher}; + + #[derive(Copy, Clone)] + pub struct VsockAddr(pub sockaddr_vm); + + impl PartialEq for VsockAddr { + fn eq(&self, other: &Self) -> bool { + let (inner, other) = (self.0, other.0); + (inner.svm_family, inner.svm_cid, inner.svm_port) == + (other.svm_family, other.svm_cid, other.svm_port) + } + } + + impl Eq for VsockAddr {} + + impl Hash for VsockAddr { + fn hash(&self, s: &mut H) { + let inner = self.0; + (inner.svm_family, inner.svm_cid, inner.svm_port).hash(s); + } + } + + /// VSOCK Address + /// + /// The address for AF_VSOCK socket is defined as a combination of a + /// 32-bit Context Identifier (CID) and a 32-bit port number. + impl VsockAddr { + pub fn new(cid: u32, port: u32) -> VsockAddr { + let mut addr: sockaddr_vm = unsafe { mem::zeroed() }; + addr.svm_family = AddressFamily::Vsock as sa_family_t; + addr.svm_cid = cid; + addr.svm_port = port; + + VsockAddr(addr) + } + + /// Context Identifier (CID) + pub fn cid(&self) -> u32 { + self.0.svm_cid + } + + /// Port number + pub fn port(&self) -> u32 { + self.0.svm_port + } + } + + impl fmt::Display for VsockAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "cid: {} port: {}", self.cid(), self.port()) + } + } + + impl fmt::Debug for VsockAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } +} + +#[cfg(test)] +mod tests { + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "illumos", + target_os = "openbsd"))] + use super::*; + + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + #[test] + fn test_macos_loopback_datalink_addr() { + let bytes = [20i8, 18, 1, 0, 24, 3, 0, 0, 108, 111, 48, 0, 0, 0, 0, 0]; + let sa = bytes.as_ptr() as *const libc::sockaddr; + let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) }; + assert!(_sock_addr.is_none()); + } + + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + #[test] + fn test_macos_tap_datalink_addr() { + let bytes = [20i8, 18, 7, 0, 6, 3, 6, 0, 101, 110, 48, 24, 101, -112, -35, 76, -80]; + let ptr = bytes.as_ptr(); + let sa = ptr as *const libc::sockaddr; + let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) }; + + assert!(_sock_addr.is_some()); + + let sock_addr = _sock_addr.unwrap(); + + assert_eq!(sock_addr.family(), AddressFamily::Link); + + match sock_addr { + SockAddr::Link(ether_addr) => { + assert_eq!(ether_addr.addr(), [24u8, 101, 144, 221, 76, 176]); + }, + _ => { unreachable!() } + }; + } + + #[cfg(target_os = "illumos")] + #[test] + fn test_illumos_tap_datalink_addr() { + let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176]; + let ptr = bytes.as_ptr(); + let sa = ptr as *const libc::sockaddr; + let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) }; + + assert!(_sock_addr.is_some()); + + let sock_addr = _sock_addr.unwrap(); + + assert_eq!(sock_addr.family(), AddressFamily::Link); + + match sock_addr { + SockAddr::Link(ether_addr) => { + assert_eq!(ether_addr.addr(), [24u8, 101, 144, 221, 76, 176]); + }, + _ => { unreachable!() } + }; + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + #[test] + fn test_abstract_sun_path() { + let name = String::from("nix\0abstract\0test"); + let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + + let sun_path1 = unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] }; + let sun_path2 = [0, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116]; + assert_eq!(sun_path1, sun_path2); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/socket/mod.rs b/vendor/nix-v0.23.1-patched/src/sys/socket/mod.rs new file mode 100644 index 000000000..97eea3dcb --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/socket/mod.rs @@ -0,0 +1,1916 @@ +//! Socket interface functions +//! +//! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html) +use cfg_if::cfg_if; +use crate::{Result, errno::Errno}; +use libc::{self, c_void, c_int, iovec, socklen_t, size_t, + CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_LEN}; +use memoffset::offset_of; +use std::{mem, ptr, slice}; +use std::os::unix::io::RawFd; +#[cfg(all(target_os = "linux"))] +use crate::sys::time::TimeSpec; +use crate::sys::time::TimeVal; +use crate::sys::uio::IoVec; + +mod addr; +#[deny(missing_docs)] +pub mod sockopt; + +/* + * + * ===== Re-exports ===== + * + */ + +#[cfg(not(any(target_os = "illumos", target_os = "solaris")))] +pub use self::addr::{ + AddressFamily, + SockAddr, + InetAddr, + UnixAddr, + IpAddr, + Ipv4Addr, + Ipv6Addr, + LinkAddr, +}; +#[cfg(any(target_os = "illumos", target_os = "solaris"))] +pub use self::addr::{ + AddressFamily, + SockAddr, + InetAddr, + UnixAddr, + IpAddr, + Ipv4Addr, + Ipv6Addr, +}; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use crate::sys::socket::addr::netlink::NetlinkAddr; +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use crate::sys::socket::addr::alg::AlgAddr; +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use crate::sys::socket::addr::vsock::VsockAddr; + +pub use libc::{ + cmsghdr, + msghdr, + sa_family_t, + sockaddr, + sockaddr_in, + sockaddr_in6, + sockaddr_storage, + sockaddr_un, +}; + +// Needed by the cmsg_space macro +#[doc(hidden)] +pub use libc::{c_uint, CMSG_SPACE}; + +/// These constants are used to specify the communication semantics +/// when creating a socket with [`socket()`](fn.socket.html) +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[repr(i32)] +#[non_exhaustive] +pub enum SockType { + /// Provides sequenced, reliable, two-way, connection- + /// based byte streams. An out-of-band data transmission + /// mechanism may be supported. + Stream = libc::SOCK_STREAM, + /// Supports datagrams (connectionless, unreliable + /// messages of a fixed maximum length). + Datagram = libc::SOCK_DGRAM, + /// Provides a sequenced, reliable, two-way connection- + /// based data transmission path for datagrams of fixed + /// maximum length; a consumer is required to read an + /// entire packet with each input system call. + SeqPacket = libc::SOCK_SEQPACKET, + /// Provides raw network protocol access. + Raw = libc::SOCK_RAW, + /// Provides a reliable datagram layer that does not + /// guarantee ordering. + Rdm = libc::SOCK_RDM, +} + +/// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html) +/// to specify the protocol to use. +#[repr(i32)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[non_exhaustive] +pub enum SockProtocol { + /// TCP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html)) + Tcp = libc::IPPROTO_TCP, + /// UDP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html)) + Udp = libc::IPPROTO_UDP, + /// Allows applications and other KEXTs to be notified when certain kernel events occur + /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html)) + #[cfg(any(target_os = "ios", target_os = "macos"))] + KextEvent = libc::SYSPROTO_EVENT, + /// Allows applications to configure and control a KEXT + /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html)) + #[cfg(any(target_os = "ios", target_os = "macos"))] + KextControl = libc::SYSPROTO_CONTROL, + /// Receives routing and link updates and may be used to modify the routing tables (both IPv4 and IPv6), IP addresses, link + // parameters, neighbor setups, queueing disciplines, traffic classes and packet classifiers + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkRoute = libc::NETLINK_ROUTE, + /// Reserved for user-mode socket protocols + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkUserSock = libc::NETLINK_USERSOCK, + /// Query information about sockets of various protocol families from the kernel + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkSockDiag = libc::NETLINK_SOCK_DIAG, + /// SELinux event notifications. + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkSELinux = libc::NETLINK_SELINUX, + /// Open-iSCSI + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkISCSI = libc::NETLINK_ISCSI, + /// Auditing + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkAudit = libc::NETLINK_AUDIT, + /// Access to FIB lookup from user space + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkFIBLookup = libc::NETLINK_FIB_LOOKUP, + /// Netfilter subsystem + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkNetFilter = libc::NETLINK_NETFILTER, + /// SCSI Transports + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkSCSITransport = libc::NETLINK_SCSITRANSPORT, + /// Infiniband RDMA + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkRDMA = libc::NETLINK_RDMA, + /// Transport IPv6 packets from netfilter to user space. Used by ip6_queue kernel module. + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkIPv6Firewall = libc::NETLINK_IP6_FW, + /// DECnet routing messages + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkDECNetRoutingMessage = libc::NETLINK_DNRTMSG, + /// Kernel messages to user space + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkKObjectUEvent = libc::NETLINK_KOBJECT_UEVENT, + /// Netlink interface to request information about ciphers registered with the kernel crypto API as well as allow + /// configuration of the kernel crypto API. + /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + NetlinkCrypto = libc::NETLINK_CRYPTO, +} + +libc_bitflags!{ + /// Additional socket options + pub struct SockFlag: c_int { + /// Set non-blocking mode on the new socket + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + SOCK_NONBLOCK; + /// Set close-on-exec on the new descriptor + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + SOCK_CLOEXEC; + /// Return `EPIPE` instead of raising `SIGPIPE` + #[cfg(target_os = "netbsd")] + SOCK_NOSIGPIPE; + /// For domains `AF_INET(6)`, only allow `connect(2)`, `sendto(2)`, or `sendmsg(2)` + /// to the DNS port (typically 53) + #[cfg(target_os = "openbsd")] + SOCK_DNS; + } +} + +libc_bitflags!{ + /// Flags for send/recv and their relatives + pub struct MsgFlags: c_int { + /// Sends or requests out-of-band data on sockets that support this notion + /// (e.g., of type [`Stream`](enum.SockType.html)); the underlying protocol must also + /// support out-of-band data. + MSG_OOB; + /// Peeks at an incoming message. The data is treated as unread and the next + /// [`recv()`](fn.recv.html) + /// or similar function shall still return this data. + MSG_PEEK; + /// Receive operation blocks until the full amount of data can be + /// returned. The function may return smaller amount of data if a signal + /// is caught, an error or disconnect occurs. + MSG_WAITALL; + /// Enables nonblocking operation; if the operation would block, + /// `EAGAIN` or `EWOULDBLOCK` is returned. This provides similar + /// behavior to setting the `O_NONBLOCK` flag + /// (via the [`fcntl`](../../fcntl/fn.fcntl.html) + /// `F_SETFL` operation), but differs in that `MSG_DONTWAIT` is a per- + /// call option, whereas `O_NONBLOCK` is a setting on the open file + /// description (see [open(2)](https://man7.org/linux/man-pages/man2/open.2.html)), + /// which will affect all threads in + /// the calling process and as well as other processes that hold + /// file descriptors referring to the same open file description. + MSG_DONTWAIT; + /// Receive flags: Control Data was discarded (buffer too small) + MSG_CTRUNC; + /// For raw ([`Packet`](addr/enum.AddressFamily.html)), Internet datagram + /// (since Linux 2.4.27/2.6.8), + /// netlink (since Linux 2.6.22) and UNIX datagram (since Linux 3.4) + /// sockets: return the real length of the packet or datagram, even + /// when it was longer than the passed buffer. Not implemented for UNIX + /// domain ([unix(7)](https://linux.die.net/man/7/unix)) sockets. + /// + /// For use with Internet stream sockets, see [tcp(7)](https://linux.die.net/man/7/tcp). + MSG_TRUNC; + /// Terminates a record (when this notion is supported, as for + /// sockets of type [`SeqPacket`](enum.SockType.html)). + MSG_EOR; + /// This flag specifies that queued errors should be received from + /// the socket error queue. (For more details, see + /// [recvfrom(2)](https://linux.die.net/man/2/recvfrom)) + #[cfg(any(target_os = "android", target_os = "linux"))] + MSG_ERRQUEUE; + /// Set the `close-on-exec` flag for the file descriptor received via a UNIX domain + /// file descriptor using the `SCM_RIGHTS` operation (described in + /// [unix(7)](https://linux.die.net/man/7/unix)). + /// This flag is useful for the same reasons as the `O_CLOEXEC` flag of + /// [open(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html). + /// + /// Only used in [`recvmsg`](fn.recvmsg.html) function. + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + MSG_CMSG_CLOEXEC; + } +} + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + /// Unix credentials of the sending process. + /// + /// This struct is used with the `SO_PEERCRED` ancillary message + /// and the `SCM_CREDENTIALS` control message for UNIX sockets. + #[repr(transparent)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct UnixCredentials(libc::ucred); + + impl UnixCredentials { + /// Creates a new instance with the credentials of the current process + pub fn new() -> Self { + UnixCredentials(libc::ucred { + pid: crate::unistd::getpid().as_raw(), + uid: crate::unistd::getuid().as_raw(), + gid: crate::unistd::getgid().as_raw(), + }) + } + + /// Returns the process identifier + pub fn pid(&self) -> libc::pid_t { + self.0.pid + } + + /// Returns the user identifier + pub fn uid(&self) -> libc::uid_t { + self.0.uid + } + + /// Returns the group identifier + pub fn gid(&self) -> libc::gid_t { + self.0.gid + } + } + + impl Default for UnixCredentials { + fn default() -> Self { + Self::new() + } + } + + impl From for UnixCredentials { + fn from(cred: libc::ucred) -> Self { + UnixCredentials(cred) + } + } + + impl From for libc::ucred { + fn from(uc: UnixCredentials) -> Self { + uc.0 + } + } + } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] { + /// Unix credentials of the sending process. + /// + /// This struct is used with the `SCM_CREDS` ancillary message for UNIX sockets. + #[repr(transparent)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct UnixCredentials(libc::cmsgcred); + + impl UnixCredentials { + /// Returns the process identifier + pub fn pid(&self) -> libc::pid_t { + self.0.cmcred_pid + } + + /// Returns the real user identifier + pub fn uid(&self) -> libc::uid_t { + self.0.cmcred_uid + } + + /// Returns the effective user identifier + pub fn euid(&self) -> libc::uid_t { + self.0.cmcred_euid + } + + /// Returns the real group identifier + pub fn gid(&self) -> libc::gid_t { + self.0.cmcred_gid + } + + /// Returns a list group identifiers (the first one being the effective GID) + pub fn groups(&self) -> &[libc::gid_t] { + unsafe { slice::from_raw_parts(self.0.cmcred_groups.as_ptr() as *const libc::gid_t, self.0.cmcred_ngroups as _) } + } + } + + impl From for UnixCredentials { + fn from(cred: libc::cmsgcred) -> Self { + UnixCredentials(cred) + } + } + } +} + +cfg_if!{ + if #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "ios" + ))] { + /// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred) + #[repr(transparent)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct XuCred(libc::xucred); + + impl XuCred { + /// Structure layout version + pub fn version(&self) -> u32 { + self.0.cr_version + } + + /// Effective user ID + pub fn uid(&self) -> libc::uid_t { + self.0.cr_uid + } + + /// Returns a list of group identifiers (the first one being the + /// effective GID) + pub fn groups(&self) -> &[libc::gid_t] { + &self.0.cr_groups + } + } + } +} + +/// Request for multicast socket operations +/// +/// This is a wrapper type around `ip_mreq`. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct IpMembershipRequest(libc::ip_mreq); + +impl IpMembershipRequest { + /// Instantiate a new `IpMembershipRequest` + /// + /// If `interface` is `None`, then `Ipv4Addr::any()` will be used for the interface. + pub fn new(group: Ipv4Addr, interface: Option) -> Self { + IpMembershipRequest(libc::ip_mreq { + imr_multiaddr: group.0, + imr_interface: interface.unwrap_or_else(Ipv4Addr::any).0, + }) + } +} + +/// Request for ipv6 multicast socket operations +/// +/// This is a wrapper type around `ipv6_mreq`. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Ipv6MembershipRequest(libc::ipv6_mreq); + +impl Ipv6MembershipRequest { + /// Instantiate a new `Ipv6MembershipRequest` + pub const fn new(group: Ipv6Addr) -> Self { + Ipv6MembershipRequest(libc::ipv6_mreq { + ipv6mr_multiaddr: group.0, + ipv6mr_interface: 0, + }) + } +} + +/// Create a buffer large enough for storing some control messages as returned +/// by [`recvmsg`](fn.recvmsg.html). +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// # use nix::sys::time::TimeVal; +/// # use std::os::unix::io::RawFd; +/// # fn main() { +/// // Create a buffer for a `ControlMessageOwned::ScmTimestamp` message +/// let _ = cmsg_space!(TimeVal); +/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message +/// // with two file descriptors +/// let _ = cmsg_space!([RawFd; 2]); +/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message +/// // and a `ControlMessageOwned::ScmTimestamp` message +/// let _ = cmsg_space!(RawFd, TimeVal); +/// # } +/// ``` +// Unfortunately, CMSG_SPACE isn't a const_fn, or else we could return a +// stack-allocated array. +#[macro_export] +macro_rules! cmsg_space { + ( $( $x:ty ),* ) => { + { + let mut space = 0; + $( + // CMSG_SPACE is always safe + space += unsafe { + $crate::sys::socket::CMSG_SPACE(::std::mem::size_of::<$x>() as $crate::sys::socket::c_uint) + } as usize; + )* + Vec::::with_capacity(space) + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct RecvMsg<'a> { + pub bytes: usize, + cmsghdr: Option<&'a cmsghdr>, + pub address: Option, + pub flags: MsgFlags, + mhdr: msghdr, +} + +impl<'a> RecvMsg<'a> { + /// Iterate over the valid control messages pointed to by this + /// msghdr. + pub fn cmsgs(&self) -> CmsgIterator { + CmsgIterator { + cmsghdr: self.cmsghdr, + mhdr: &self.mhdr + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct CmsgIterator<'a> { + /// Control message buffer to decode from. Must adhere to cmsg alignment. + cmsghdr: Option<&'a cmsghdr>, + mhdr: &'a msghdr +} + +impl<'a> Iterator for CmsgIterator<'a> { + type Item = ControlMessageOwned; + + fn next(&mut self) -> Option { + match self.cmsghdr { + None => None, // No more messages + Some(hdr) => { + // Get the data. + // Safe if cmsghdr points to valid data returned by recvmsg(2) + let cm = unsafe { Some(ControlMessageOwned::decode_from(hdr))}; + // Advance the internal pointer. Safe if mhdr and cmsghdr point + // to valid data returned by recvmsg(2) + self.cmsghdr = unsafe { + let p = CMSG_NXTHDR(self.mhdr as *const _, hdr as *const _); + p.as_ref() + }; + cm + } + } + } +} + +/// A type-safe wrapper around a single control message, as used with +/// [`recvmsg`](#fn.recvmsg). +/// +/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html) +// Nix version 0.13.0 and earlier used ControlMessage for both recvmsg and +// sendmsg. However, on some platforms the messages returned by recvmsg may be +// unaligned. ControlMessageOwned takes those messages by copy, obviating any +// alignment issues. +// +// See https://github.com/nix-rust/nix/issues/999 +#[derive(Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub enum ControlMessageOwned { + /// Received version of [`ControlMessage::ScmRights`] + ScmRights(Vec), + /// Received version of [`ControlMessage::ScmCredentials`] + #[cfg(any(target_os = "android", target_os = "linux"))] + ScmCredentials(UnixCredentials), + /// Received version of [`ControlMessage::ScmCreds`] + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + ScmCreds(UnixCredentials), + /// A message of type `SCM_TIMESTAMP`, containing the time the + /// packet was received by the kernel. + /// + /// See the kernel's explanation in "SO_TIMESTAMP" of + /// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt). + /// + /// # Examples + /// + /// ``` + /// # #[macro_use] extern crate nix; + /// # use nix::sys::socket::*; + /// # use nix::sys::uio::IoVec; + /// # use nix::sys::time::*; + /// # use std::time::*; + /// # fn main() { + /// // Set up + /// let message = "Ohayō!".as_bytes(); + /// let in_socket = socket( + /// AddressFamily::Inet, + /// SockType::Datagram, + /// SockFlag::empty(), + /// None).unwrap(); + /// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap(); + /// let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0); + /// bind(in_socket, &SockAddr::new_inet(localhost)).unwrap(); + /// let address = getsockname(in_socket).unwrap(); + /// // Get initial time + /// let time0 = SystemTime::now(); + /// // Send the message + /// let iov = [IoVec::from_slice(message)]; + /// let flags = MsgFlags::empty(); + /// let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); + /// assert_eq!(message.len(), l); + /// // Receive the message + /// let mut buffer = vec![0u8; message.len()]; + /// let mut cmsgspace = cmsg_space!(TimeVal); + /// let iov = [IoVec::from_mut_slice(&mut buffer)]; + /// let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap(); + /// let rtime = match r.cmsgs().next() { + /// Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime, + /// Some(_) => panic!("Unexpected control message"), + /// None => panic!("No control message") + /// }; + /// // Check the final time + /// let time1 = SystemTime::now(); + /// // the packet's received timestamp should lie in-between the two system + /// // times, unless the system clock was adjusted in the meantime. + /// let rduration = Duration::new(rtime.tv_sec() as u64, + /// rtime.tv_usec() as u32 * 1000); + /// assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); + /// assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); + /// // Close socket + /// nix::unistd::close(in_socket).unwrap(); + /// # } + /// ``` + ScmTimestamp(TimeVal), + /// Nanoseconds resolution timestamp + /// + /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html) + #[cfg(all(target_os = "linux"))] + ScmTimestampns(TimeSpec), + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + ))] + Ipv4PacketInfo(libc::in_pktinfo), + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd", + target_os = "netbsd", + ))] + Ipv6PacketInfo(libc::in6_pktinfo), + #[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + ))] + Ipv4RecvIf(libc::sockaddr_dl), + #[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + ))] + Ipv4RecvDstAddr(libc::in_addr), + + /// UDP Generic Receive Offload (GRO) allows receiving multiple UDP + /// packets from a single sender. + /// Fixed-size payloads are following one by one in a receive buffer. + /// This Control Message indicates the size of all smaller packets, + /// except, maybe, the last one. + /// + /// `UdpGroSegment` socket option should be enabled on a socket + /// to allow receiving GRO packets. + #[cfg(target_os = "linux")] + UdpGroSegments(u16), + + /// SO_RXQ_OVFL indicates that an unsigned 32 bit value + /// ancilliary msg (cmsg) should be attached to recieved + /// skbs indicating the number of packets dropped by the + /// socket between the last recieved packet and this + /// received packet. + /// + /// `RxqOvfl` socket option should be enabled on a socket + /// to allow receiving the drop counter. + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + RxqOvfl(u32), + + /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag. + #[cfg(any(target_os = "android", target_os = "linux"))] + Ipv4RecvErr(libc::sock_extended_err, Option), + /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag. + #[cfg(any(target_os = "android", target_os = "linux"))] + Ipv6RecvErr(libc::sock_extended_err, Option), + + /// Catch-all variant for unimplemented cmsg types. + #[doc(hidden)] + Unknown(UnknownCmsg), +} + +impl ControlMessageOwned { + /// Decodes a `ControlMessageOwned` from raw bytes. + /// + /// This is only safe to call if the data is correct for the message type + /// specified in the header. Normally, the kernel ensures that this is the + /// case. "Correct" in this case includes correct length, alignment and + /// actual content. + // Clippy complains about the pointer alignment of `p`, not understanding + // that it's being fed to a function that can handle that. + #[allow(clippy::cast_ptr_alignment)] + unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned + { + let p = CMSG_DATA(header); + let len = header as *const _ as usize + header.cmsg_len as usize + - p as usize; + match (header.cmsg_level, header.cmsg_type) { + (libc::SOL_SOCKET, libc::SCM_RIGHTS) => { + let n = len / mem::size_of::(); + let mut fds = Vec::with_capacity(n); + for i in 0..n { + let fdp = (p as *const RawFd).add(i); + fds.push(ptr::read_unaligned(fdp)); + } + ControlMessageOwned::ScmRights(fds) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + (libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => { + let cred: libc::ucred = ptr::read_unaligned(p as *const _); + ControlMessageOwned::ScmCredentials(cred.into()) + } + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + (libc::SOL_SOCKET, libc::SCM_CREDS) => { + let cred: libc::cmsgcred = ptr::read_unaligned(p as *const _); + ControlMessageOwned::ScmCreds(cred.into()) + } + (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => { + let tv: libc::timeval = ptr::read_unaligned(p as *const _); + ControlMessageOwned::ScmTimestamp(TimeVal::from(tv)) + }, + #[cfg(all(target_os = "linux"))] + (libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => { + let ts: libc::timespec = ptr::read_unaligned(p as *const _); + ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts)) + } + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => { + let info = ptr::read_unaligned(p as *const libc::in6_pktinfo); + ControlMessageOwned::Ipv6PacketInfo(info) + } + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + ))] + (libc::IPPROTO_IP, libc::IP_PKTINFO) => { + let info = ptr::read_unaligned(p as *const libc::in_pktinfo); + ControlMessageOwned::Ipv4PacketInfo(info) + } + #[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + ))] + (libc::IPPROTO_IP, libc::IP_RECVIF) => { + let dl = ptr::read_unaligned(p as *const libc::sockaddr_dl); + ControlMessageOwned::Ipv4RecvIf(dl) + }, + #[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + ))] + (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => { + let dl = ptr::read_unaligned(p as *const libc::in_addr); + ControlMessageOwned::Ipv4RecvDstAddr(dl) + }, + #[cfg(target_os = "linux")] + (libc::SOL_UDP, libc::UDP_GRO) => { + let gso_size: u16 = ptr::read_unaligned(p as *const _); + ControlMessageOwned::UdpGroSegments(gso_size) + }, + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + (libc::SOL_SOCKET, libc::SO_RXQ_OVFL) => { + let drop_counter = ptr::read_unaligned(p as *const u32); + ControlMessageOwned::RxqOvfl(drop_counter) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + (libc::IPPROTO_IP, libc::IP_RECVERR) => { + let (err, addr) = Self::recv_err_helper::(p, len); + ControlMessageOwned::Ipv4RecvErr(err, addr) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => { + let (err, addr) = Self::recv_err_helper::(p, len); + ControlMessageOwned::Ipv6RecvErr(err, addr) + }, + (_, _) => { + let sl = slice::from_raw_parts(p, len); + let ucmsg = UnknownCmsg(*header, Vec::::from(sl)); + ControlMessageOwned::Unknown(ucmsg) + } + } + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + unsafe fn recv_err_helper(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option) { + let ee = p as *const libc::sock_extended_err; + let err = ptr::read_unaligned(ee); + + // For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len] + // CMSG_DATA buffer. For local errors, there is no address included in the control + // message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer. So, we need to + // validate that the address object is in-bounds before we attempt to copy it. + let addrp = libc::SO_EE_OFFENDER(ee) as *const T; + + if addrp.offset(1) as usize - (p as usize) > len { + (err, None) + } else { + (err, Some(ptr::read_unaligned(addrp))) + } + } +} + +/// A type-safe zero-copy wrapper around a single control message, as used wih +/// [`sendmsg`](#fn.sendmsg). More types may be added to this enum; do not +/// exhaustively pattern-match it. +/// +/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html) +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub enum ControlMessage<'a> { + /// A message of type `SCM_RIGHTS`, containing an array of file + /// descriptors passed between processes. + /// + /// See the description in the "Ancillary messages" section of the + /// [unix(7) man page](https://man7.org/linux/man-pages/man7/unix.7.html). + /// + /// Using multiple `ScmRights` messages for a single `sendmsg` call isn't + /// recommended since it causes platform-dependent behaviour: It might + /// swallow all but the first `ScmRights` message or fail with `EINVAL`. + /// Instead, you can put all fds to be passed into a single `ScmRights` + /// message. + ScmRights(&'a [RawFd]), + /// A message of type `SCM_CREDENTIALS`, containing the pid, uid and gid of + /// a process connected to the socket. + /// + /// This is similar to the socket option `SO_PEERCRED`, but requires a + /// process to explicitly send its credentials. A process running as root is + /// allowed to specify any credentials, while credentials sent by other + /// processes are verified by the kernel. + /// + /// For further information, please refer to the + /// [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html) man page. + #[cfg(any(target_os = "android", target_os = "linux"))] + ScmCredentials(&'a UnixCredentials), + /// A message of type `SCM_CREDS`, containing the pid, uid, euid, gid and groups of + /// a process connected to the socket. + /// + /// This is similar to the socket options `LOCAL_CREDS` and `LOCAL_PEERCRED`, but + /// requires a process to explicitly send its credentials. + /// + /// Credentials are always overwritten by the kernel, so this variant does have + /// any data, unlike the receive-side + /// [`ControlMessageOwned::ScmCreds`]. + /// + /// For further information, please refer to the + /// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page. + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + ScmCreds, + + /// Set IV for `AF_ALG` crypto API. + /// + /// For further information, please refer to the + /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html) + #[cfg(any( + target_os = "android", + target_os = "linux", + ))] + AlgSetIv(&'a [u8]), + /// Set crypto operation for `AF_ALG` crypto API. It may be one of + /// `ALG_OP_ENCRYPT` or `ALG_OP_DECRYPT` + /// + /// For further information, please refer to the + /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html) + #[cfg(any( + target_os = "android", + target_os = "linux", + ))] + AlgSetOp(&'a libc::c_int), + /// Set the length of associated authentication data (AAD) (applicable only to AEAD algorithms) + /// for `AF_ALG` crypto API. + /// + /// For further information, please refer to the + /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html) + #[cfg(any( + target_os = "android", + target_os = "linux", + ))] + AlgSetAeadAssoclen(&'a u32), + + /// UDP GSO makes it possible for applications to generate network packets + /// for a virtual MTU much greater than the real one. + /// The length of the send data no longer matches the expected length on + /// the wire. + /// The size of the datagram payload as it should appear on the wire may be + /// passed through this control message. + /// Send buffer should consist of multiple fixed-size wire payloads + /// following one by one, and the last, possibly smaller one. + #[cfg(target_os = "linux")] + UdpGsoSegments(&'a u16), + + /// Configure the sending addressing and interface for v4 + /// + /// For further information, please refer to the + /// [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html) man page. + #[cfg(any(target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "android", + target_os = "ios",))] + Ipv4PacketInfo(&'a libc::in_pktinfo), + + /// Configure the sending addressing and interface for v6 + /// + /// For further information, please refer to the + /// [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html) man page. + #[cfg(any(target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "freebsd", + target_os = "android", + target_os = "ios",))] + Ipv6PacketInfo(&'a libc::in6_pktinfo), + + /// SO_RXQ_OVFL indicates that an unsigned 32 bit value + /// ancilliary msg (cmsg) should be attached to recieved + /// skbs indicating the number of packets dropped by the + /// socket between the last recieved packet and this + /// received packet. + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + RxqOvfl(&'a u32), +} + +// An opaque structure used to prevent cmsghdr from being a public type +#[doc(hidden)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct UnknownCmsg(cmsghdr, Vec); + +impl<'a> ControlMessage<'a> { + /// The value of CMSG_SPACE on this message. + /// Safe because CMSG_SPACE is always safe + fn space(&self) -> usize { + unsafe{CMSG_SPACE(self.len() as libc::c_uint) as usize} + } + + /// The value of CMSG_LEN on this message. + /// Safe because CMSG_LEN is always safe + #[cfg(any(target_os = "android", + all(target_os = "linux", not(target_env = "musl"))))] + fn cmsg_len(&self) -> usize { + unsafe{CMSG_LEN(self.len() as libc::c_uint) as usize} + } + + #[cfg(not(any(target_os = "android", + all(target_os = "linux", not(target_env = "musl")))))] + fn cmsg_len(&self) -> libc::c_uint { + unsafe{CMSG_LEN(self.len() as libc::c_uint)} + } + + /// Return a reference to the payload data as a byte pointer + fn copy_to_cmsg_data(&self, cmsg_data: *mut u8) { + let data_ptr = match *self { + ControlMessage::ScmRights(fds) => { + fds as *const _ as *const u8 + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(creds) => { + &creds.0 as *const libc::ucred as *const u8 + } + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + ControlMessage::ScmCreds => { + // The kernel overwrites the data, we just zero it + // to make sure it's not uninitialized memory + unsafe { ptr::write_bytes(cmsg_data, 0, self.len()) }; + return + } + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::AlgSetIv(iv) => { + #[allow(deprecated)] // https://github.com/rust-lang/libc/issues/1501 + let af_alg_iv = libc::af_alg_iv { + ivlen: iv.len() as u32, + iv: [0u8; 0], + }; + + let size = mem::size_of_val(&af_alg_iv); + + unsafe { + ptr::copy_nonoverlapping( + &af_alg_iv as *const _ as *const u8, + cmsg_data, + size, + ); + ptr::copy_nonoverlapping( + iv.as_ptr(), + cmsg_data.add(size), + iv.len() + ); + }; + + return + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::AlgSetOp(op) => { + op as *const _ as *const u8 + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::AlgSetAeadAssoclen(len) => { + len as *const _ as *const u8 + }, + #[cfg(target_os = "linux")] + ControlMessage::UdpGsoSegments(gso_size) => { + gso_size as *const _ as *const u8 + }, + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "netbsd", target_os = "android", + target_os = "ios",))] + ControlMessage::Ipv4PacketInfo(info) => info as *const _ as *const u8, + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "netbsd", target_os = "freebsd", + target_os = "android", target_os = "ios",))] + ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8, + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + ControlMessage::RxqOvfl(drop_count) => { + drop_count as *const _ as *const u8 + }, + }; + unsafe { + ptr::copy_nonoverlapping( + data_ptr, + cmsg_data, + self.len() + ) + }; + } + + /// The size of the payload, excluding its cmsghdr + fn len(&self) -> usize { + match *self { + ControlMessage::ScmRights(fds) => { + mem::size_of_val(fds) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(creds) => { + mem::size_of_val(creds) + } + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + ControlMessage::ScmCreds => { + mem::size_of::() + } + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::AlgSetIv(iv) => { + mem::size_of_val(&iv) + iv.len() + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::AlgSetOp(op) => { + mem::size_of_val(op) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::AlgSetAeadAssoclen(len) => { + mem::size_of_val(len) + }, + #[cfg(target_os = "linux")] + ControlMessage::UdpGsoSegments(gso_size) => { + mem::size_of_val(gso_size) + }, + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "netbsd", target_os = "android", + target_os = "ios",))] + ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info), + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "netbsd", target_os = "freebsd", + target_os = "android", target_os = "ios",))] + ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info), + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + ControlMessage::RxqOvfl(drop_count) => { + mem::size_of_val(drop_count) + }, + } + } + + /// Returns the value to put into the `cmsg_level` field of the header. + fn cmsg_level(&self) -> libc::c_int { + match *self { + ControlMessage::ScmRights(_) => libc::SOL_SOCKET, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET, + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + ControlMessage::ScmCreds => libc::SOL_SOCKET, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) | + ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG, + #[cfg(target_os = "linux")] + ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP, + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "netbsd", target_os = "android", + target_os = "ios",))] + ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP, + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "netbsd", target_os = "freebsd", + target_os = "android", target_os = "ios",))] + ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6, + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET, + } + } + + /// Returns the value to put into the `cmsg_type` field of the header. + fn cmsg_type(&self) -> libc::c_int { + match *self { + ControlMessage::ScmRights(_) => libc::SCM_RIGHTS, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS, + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + ControlMessage::ScmCreds => libc::SCM_CREDS, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::AlgSetIv(_) => { + libc::ALG_SET_IV + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::AlgSetOp(_) => { + libc::ALG_SET_OP + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::AlgSetAeadAssoclen(_) => { + libc::ALG_SET_AEAD_ASSOCLEN + }, + #[cfg(target_os = "linux")] + ControlMessage::UdpGsoSegments(_) => { + libc::UDP_SEGMENT + }, + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "netbsd", target_os = "android", + target_os = "ios",))] + ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO, + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "netbsd", target_os = "freebsd", + target_os = "android", target_os = "ios",))] + ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO, + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + ControlMessage::RxqOvfl(_) => { + libc::SO_RXQ_OVFL + }, + } + } + + // Unsafe: cmsg must point to a valid cmsghdr with enough space to + // encode self. + unsafe fn encode_into(&self, cmsg: *mut cmsghdr) { + (*cmsg).cmsg_level = self.cmsg_level(); + (*cmsg).cmsg_type = self.cmsg_type(); + (*cmsg).cmsg_len = self.cmsg_len(); + self.copy_to_cmsg_data(CMSG_DATA(cmsg)); + } +} + + +/// Send data in scatter-gather vectors to a socket, possibly accompanied +/// by ancillary data. Optionally direct the message at the given address, +/// as with sendto. +/// +/// Allocates if cmsgs is nonempty. +pub fn sendmsg(fd: RawFd, iov: &[IoVec<&[u8]>], cmsgs: &[ControlMessage], + flags: MsgFlags, addr: Option<&SockAddr>) -> Result +{ + let capacity = cmsgs.iter().map(|c| c.space()).sum(); + + // First size the buffer needed to hold the cmsgs. It must be zeroed, + // because subsequent code will not clear the padding bytes. + let mut cmsg_buffer = vec![0u8; capacity]; + + let mhdr = pack_mhdr_to_send(&mut cmsg_buffer[..], &iov, &cmsgs, addr); + + let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) }; + + Errno::result(ret).map(|r| r as usize) +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +#[derive(Debug)] +pub struct SendMmsgData<'a, I, C> + where + I: AsRef<[IoVec<&'a [u8]>]>, + C: AsRef<[ControlMessage<'a>]> +{ + pub iov: I, + pub cmsgs: C, + pub addr: Option, + pub _lt: std::marker::PhantomData<&'a I>, +} + +/// An extension of `sendmsg` that allows the caller to transmit multiple +/// messages on a socket using a single system call. This has performance +/// benefits for some applications. +/// +/// Allocations are performed for cmsgs and to build `msghdr` buffer +/// +/// # Arguments +/// +/// * `fd`: Socket file descriptor +/// * `data`: Struct that implements `IntoIterator` with `SendMmsgData` items +/// * `flags`: Optional flags passed directly to the operating system. +/// +/// # Returns +/// `Vec` with numbers of sent bytes on each sent message. +/// +/// # References +/// [`sendmsg`](fn.sendmsg.html) +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +pub fn sendmmsg<'a, I, C>( + fd: RawFd, + data: impl std::iter::IntoIterator>, + flags: MsgFlags +) -> Result> + where + I: AsRef<[IoVec<&'a [u8]>]> + 'a, + C: AsRef<[ControlMessage<'a>]> + 'a, +{ + let iter = data.into_iter(); + + let size_hint = iter.size_hint(); + let reserve_items = size_hint.1.unwrap_or(size_hint.0); + + let mut output = Vec::::with_capacity(reserve_items); + + let mut cmsgs_buffers = Vec::>::with_capacity(reserve_items); + + for d in iter { + let capacity: usize = d.cmsgs.as_ref().iter().map(|c| c.space()).sum(); + let mut cmsgs_buffer = vec![0u8; capacity]; + + output.push(libc::mmsghdr { + msg_hdr: pack_mhdr_to_send( + &mut cmsgs_buffer, + &d.iov, + &d.cmsgs, + d.addr.as_ref() + ), + msg_len: 0, + }); + cmsgs_buffers.push(cmsgs_buffer); + }; + + let ret = unsafe { libc::sendmmsg(fd, output.as_mut_ptr(), output.len() as _, flags.bits() as _) }; + + let sent_messages = Errno::result(ret)? as usize; + let mut sent_bytes = Vec::with_capacity(sent_messages); + + for item in &output { + sent_bytes.push(item.msg_len as usize); + } + + Ok(sent_bytes) +} + + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +#[derive(Debug)] +pub struct RecvMmsgData<'a, I> + where + I: AsRef<[IoVec<&'a mut [u8]>]> + 'a, +{ + pub iov: I, + pub cmsg_buffer: Option<&'a mut Vec>, +} + +/// An extension of `recvmsg` that allows the caller to receive multiple +/// messages from a socket using a single system call. This has +/// performance benefits for some applications. +/// +/// `iov` and `cmsg_buffer` should be constructed similarly to `recvmsg` +/// +/// Multiple allocations are performed +/// +/// # Arguments +/// +/// * `fd`: Socket file descriptor +/// * `data`: Struct that implements `IntoIterator` with `RecvMmsgData` items +/// * `flags`: Optional flags passed directly to the operating system. +/// +/// # RecvMmsgData +/// +/// * `iov`: Scatter-gather list of buffers to receive the message +/// * `cmsg_buffer`: Space to receive ancillary data. Should be created by +/// [`cmsg_space!`](macro.cmsg_space.html) +/// +/// # Returns +/// A `Vec` with multiple `RecvMsg`, one per received message +/// +/// # References +/// - [`recvmsg`](fn.recvmsg.html) +/// - [`RecvMsg`](struct.RecvMsg.html) +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +#[allow(clippy::needless_collect)] // Complicated false positive +pub fn recvmmsg<'a, I>( + fd: RawFd, + data: impl std::iter::IntoIterator, + IntoIter=impl ExactSizeIterator + Iterator>>, + flags: MsgFlags, + timeout: Option +) -> Result>> + where + I: AsRef<[IoVec<&'a mut [u8]>]> + 'a, +{ + let iter = data.into_iter(); + + let num_messages = iter.len(); + + let mut output: Vec = Vec::with_capacity(num_messages); + + // Addresses should be pre-allocated. pack_mhdr_to_receive will store them + // as raw pointers, so we may not move them. Turn the vec into a boxed + // slice so we won't inadvertently reallocate the vec. + let mut addresses = vec![mem::MaybeUninit::uninit(); num_messages] + .into_boxed_slice(); + + let results: Vec<_> = iter.enumerate().map(|(i, d)| { + let (msg_controllen, mhdr) = unsafe { + pack_mhdr_to_receive( + d.iov.as_ref(), + &mut d.cmsg_buffer, + addresses[i].as_mut_ptr(), + ) + }; + + output.push( + libc::mmsghdr { + msg_hdr: mhdr, + msg_len: 0, + } + ); + + (msg_controllen as usize, &mut d.cmsg_buffer) + }).collect(); + + let timeout = if let Some(mut t) = timeout { + t.as_mut() as *mut libc::timespec + } else { + ptr::null_mut() + }; + + let ret = unsafe { libc::recvmmsg(fd, output.as_mut_ptr(), output.len() as _, flags.bits() as _, timeout) }; + + let _ = Errno::result(ret)?; + + Ok(output + .into_iter() + .take(ret as usize) + .zip(addresses.iter().map(|addr| unsafe{addr.assume_init()})) + .zip(results.into_iter()) + .map(|((mmsghdr, address), (msg_controllen, cmsg_buffer))| { + unsafe { + read_mhdr( + mmsghdr.msg_hdr, + mmsghdr.msg_len as isize, + msg_controllen, + address, + cmsg_buffer + ) + } + }) + .collect()) +} + +unsafe fn read_mhdr<'a, 'b>( + mhdr: msghdr, + r: isize, + msg_controllen: usize, + address: sockaddr_storage, + cmsg_buffer: &'a mut Option<&'b mut Vec> +) -> RecvMsg<'b> { + let cmsghdr = { + if mhdr.msg_controllen > 0 { + // got control message(s) + cmsg_buffer + .as_mut() + .unwrap() + .set_len(mhdr.msg_controllen as usize); + debug_assert!(!mhdr.msg_control.is_null()); + debug_assert!(msg_controllen >= mhdr.msg_controllen as usize); + CMSG_FIRSTHDR(&mhdr as *const msghdr) + } else { + ptr::null() + }.as_ref() + }; + + let address = sockaddr_storage_to_addr( + &address , + mhdr.msg_namelen as usize + ).ok(); + + RecvMsg { + bytes: r as usize, + cmsghdr, + address, + flags: MsgFlags::from_bits_truncate(mhdr.msg_flags), + mhdr, + } +} + +unsafe fn pack_mhdr_to_receive<'a, I>( + iov: I, + cmsg_buffer: &mut Option<&mut Vec>, + address: *mut sockaddr_storage, +) -> (usize, msghdr) + where + I: AsRef<[IoVec<&'a mut [u8]>]> + 'a, +{ + let (msg_control, msg_controllen) = cmsg_buffer.as_mut() + .map(|v| (v.as_mut_ptr(), v.capacity())) + .unwrap_or((ptr::null_mut(), 0)); + + let mhdr = { + // Musl's msghdr has private fields, so this is the only way to + // initialize it. + let mut mhdr = mem::MaybeUninit::::zeroed(); + let p = mhdr.as_mut_ptr(); + (*p).msg_name = address as *mut c_void; + (*p).msg_namelen = mem::size_of::() as socklen_t; + (*p).msg_iov = iov.as_ref().as_ptr() as *mut iovec; + (*p).msg_iovlen = iov.as_ref().len() as _; + (*p).msg_control = msg_control as *mut c_void; + (*p).msg_controllen = msg_controllen as _; + (*p).msg_flags = 0; + mhdr.assume_init() + }; + + (msg_controllen, mhdr) +} + +fn pack_mhdr_to_send<'a, I, C>( + cmsg_buffer: &mut [u8], + iov: I, + cmsgs: C, + addr: Option<&SockAddr> +) -> msghdr + where + I: AsRef<[IoVec<&'a [u8]>]>, + C: AsRef<[ControlMessage<'a>]> +{ + let capacity = cmsg_buffer.len(); + + // Next encode the sending address, if provided + let (name, namelen) = match addr { + Some(addr) => { + let (x, y) = addr.as_ffi_pair(); + (x as *const _, y) + }, + None => (ptr::null(), 0), + }; + + // The message header must be initialized before the individual cmsgs. + let cmsg_ptr = if capacity > 0 { + cmsg_buffer.as_ptr() as *mut c_void + } else { + ptr::null_mut() + }; + + let mhdr = unsafe { + // Musl's msghdr has private fields, so this is the only way to + // initialize it. + let mut mhdr = mem::MaybeUninit::::zeroed(); + let p = mhdr.as_mut_ptr(); + (*p).msg_name = name as *mut _; + (*p).msg_namelen = namelen; + // transmute iov into a mutable pointer. sendmsg doesn't really mutate + // the buffer, but the standard says that it takes a mutable pointer + (*p).msg_iov = iov.as_ref().as_ptr() as *mut _; + (*p).msg_iovlen = iov.as_ref().len() as _; + (*p).msg_control = cmsg_ptr; + (*p).msg_controllen = capacity as _; + (*p).msg_flags = 0; + mhdr.assume_init() + }; + + // Encode each cmsg. This must happen after initializing the header because + // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields. + // CMSG_FIRSTHDR is always safe + let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(&mhdr as *const msghdr) }; + for cmsg in cmsgs.as_ref() { + assert_ne!(pmhdr, ptr::null_mut()); + // Safe because we know that pmhdr is valid, and we initialized it with + // sufficient space + unsafe { cmsg.encode_into(pmhdr) }; + // Safe because mhdr is valid + pmhdr = unsafe { CMSG_NXTHDR(&mhdr as *const msghdr, pmhdr) }; + } + + mhdr +} + +/// Receive message in scatter-gather vectors from a socket, and +/// optionally receive ancillary data into the provided buffer. +/// If no ancillary data is desired, use () as the type parameter. +/// +/// # Arguments +/// +/// * `fd`: Socket file descriptor +/// * `iov`: Scatter-gather list of buffers to receive the message +/// * `cmsg_buffer`: Space to receive ancillary data. Should be created by +/// [`cmsg_space!`](macro.cmsg_space.html) +/// * `flags`: Optional flags passed directly to the operating system. +/// +/// # References +/// [recvmsg(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html) +pub fn recvmsg<'a>(fd: RawFd, iov: &[IoVec<&mut [u8]>], + mut cmsg_buffer: Option<&'a mut Vec>, + flags: MsgFlags) -> Result> +{ + let mut address = mem::MaybeUninit::uninit(); + + let (msg_controllen, mut mhdr) = unsafe { + pack_mhdr_to_receive(&iov, &mut cmsg_buffer, address.as_mut_ptr()) + }; + + let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) }; + + let r = Errno::result(ret)?; + + Ok(unsafe { read_mhdr(mhdr, r, msg_controllen, address.assume_init(), &mut cmsg_buffer) }) +} + + +/// Create an endpoint for communication +/// +/// The `protocol` specifies a particular protocol to be used with the +/// socket. Normally only a single protocol exists to support a +/// particular socket type within a given protocol family, in which case +/// protocol can be specified as `None`. However, it is possible that many +/// protocols may exist, in which case a particular protocol must be +/// specified in this manner. +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html) +pub fn socket>>(domain: AddressFamily, ty: SockType, flags: SockFlag, protocol: T) -> Result { + let protocol = match protocol.into() { + None => 0, + Some(p) => p as c_int, + }; + + // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a + // little easier to understand by separating it out. So we have to merge these bitfields + // here. + let mut ty = ty as c_int; + ty |= flags.bits(); + + let res = unsafe { libc::socket(domain as c_int, ty, protocol) }; + + Errno::result(res) +} + +/// Create a pair of connected sockets +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html) +pub fn socketpair>>(domain: AddressFamily, ty: SockType, protocol: T, + flags: SockFlag) -> Result<(RawFd, RawFd)> { + let protocol = match protocol.into() { + None => 0, + Some(p) => p as c_int, + }; + + // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a + // little easier to understand by separating it out. So we have to merge these bitfields + // here. + let mut ty = ty as c_int; + ty |= flags.bits(); + + let mut fds = [-1, -1]; + + let res = unsafe { libc::socketpair(domain as c_int, ty, protocol, fds.as_mut_ptr()) }; + Errno::result(res)?; + + Ok((fds[0], fds[1])) +} + +/// Listen for connections on a socket +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html) +pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> { + let res = unsafe { libc::listen(sockfd, backlog as c_int) }; + + Errno::result(res).map(drop) +} + +/// Bind a name to a socket +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html) +pub fn bind(fd: RawFd, addr: &SockAddr) -> Result<()> { + let res = unsafe { + let (ptr, len) = addr.as_ffi_pair(); + libc::bind(fd, ptr, len) + }; + + Errno::result(res).map(drop) +} + +/// Accept a connection on a socket +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html) +pub fn accept(sockfd: RawFd) -> Result { + let res = unsafe { libc::accept(sockfd, ptr::null_mut(), ptr::null_mut()) }; + + Errno::result(res) +} + +/// Accept a connection on a socket +/// +/// [Further reading](https://man7.org/linux/man-pages/man2/accept.2.html) +#[cfg(any(all( + target_os = "android", + any( + target_arch = "aarch64", + target_arch = "x86", + target_arch = "x86_64" + ) + ), + target_os = "freebsd", + target_os = "linux", + target_os = "openbsd"))] +pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result { + let res = unsafe { libc::accept4(sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits()) }; + + Errno::result(res) +} + +/// Initiate a connection on a socket +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html) +pub fn connect(fd: RawFd, addr: &SockAddr) -> Result<()> { + let res = unsafe { + let (ptr, len) = addr.as_ffi_pair(); + libc::connect(fd, ptr, len) + }; + + Errno::result(res).map(drop) +} + +/// Receive data from a connection-oriented socket. Returns the number of +/// bytes read +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html) +pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result { + unsafe { + let ret = libc::recv( + sockfd, + buf.as_ptr() as *mut c_void, + buf.len() as size_t, + flags.bits()); + + Errno::result(ret).map(|r| r as usize) + } +} + +/// Receive data from a connectionless or connection-oriented socket. Returns +/// the number of bytes read and, for connectionless sockets, the socket +/// address of the sender. +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html) +pub fn recvfrom(sockfd: RawFd, buf: &mut [u8]) + -> Result<(usize, Option)> +{ + unsafe { + let mut addr: sockaddr_storage = mem::zeroed(); + let mut len = mem::size_of::() as socklen_t; + + let ret = Errno::result(libc::recvfrom( + sockfd, + buf.as_ptr() as *mut c_void, + buf.len() as size_t, + 0, + &mut addr as *mut libc::sockaddr_storage as *mut libc::sockaddr, + &mut len as *mut socklen_t))? as usize; + + match sockaddr_storage_to_addr(&addr, len as usize) { + Err(Errno::ENOTCONN) => Ok((ret, None)), + Ok(addr) => Ok((ret, Some(addr))), + Err(e) => Err(e) + } + } +} + +/// Send a message to a socket +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html) +pub fn sendto(fd: RawFd, buf: &[u8], addr: &SockAddr, flags: MsgFlags) -> Result { + let ret = unsafe { + let (ptr, len) = addr.as_ffi_pair(); + libc::sendto(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags.bits(), ptr, len) + }; + + Errno::result(ret).map(|r| r as usize) +} + +/// Send data to a connection-oriented socket. Returns the number of bytes read +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html) +pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result { + let ret = unsafe { + libc::send(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags.bits()) + }; + + Errno::result(ret).map(|r| r as usize) +} + +/* + * + * ===== Socket Options ===== + * + */ + +/// Represents a socket option that can be retrieved. +pub trait GetSockOpt : Copy { + type Val; + + /// Look up the value of this socket option on the given socket. + fn get(&self, fd: RawFd) -> Result; +} + +/// Represents a socket option that can be set. +pub trait SetSockOpt : Clone { + type Val; + + /// Set the value of this socket option on the given socket. + fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>; +} + +/// Get the current value for the requested socket option +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html) +pub fn getsockopt(fd: RawFd, opt: O) -> Result { + opt.get(fd) +} + +/// Sets the value for the requested socket option +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html) +/// +/// # Examples +/// +/// ``` +/// use nix::sys::socket::setsockopt; +/// use nix::sys::socket::sockopt::KeepAlive; +/// use std::net::TcpListener; +/// use std::os::unix::io::AsRawFd; +/// +/// let listener = TcpListener::bind("0.0.0.0:0").unwrap(); +/// let fd = listener.as_raw_fd(); +/// let res = setsockopt(fd, KeepAlive, &true); +/// assert!(res.is_ok()); +/// ``` +pub fn setsockopt(fd: RawFd, opt: O, val: &O::Val) -> Result<()> { + opt.set(fd, val) +} + +/// Get the address of the peer connected to the socket `fd`. +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html) +pub fn getpeername(fd: RawFd) -> Result { + unsafe { + let mut addr = mem::MaybeUninit::uninit(); + let mut len = mem::size_of::() as socklen_t; + + let ret = libc::getpeername( + fd, + addr.as_mut_ptr() as *mut libc::sockaddr, + &mut len + ); + + Errno::result(ret)?; + + sockaddr_storage_to_addr(&addr.assume_init(), len as usize) + } +} + +/// Get the current address to which the socket `fd` is bound. +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html) +pub fn getsockname(fd: RawFd) -> Result { + unsafe { + let mut addr = mem::MaybeUninit::uninit(); + let mut len = mem::size_of::() as socklen_t; + + let ret = libc::getsockname( + fd, + addr.as_mut_ptr() as *mut libc::sockaddr, + &mut len + ); + + Errno::result(ret)?; + + sockaddr_storage_to_addr(&addr.assume_init(), len as usize) + } +} + +/// Return the appropriate `SockAddr` type from a `sockaddr_storage` of a +/// certain size. +/// +/// In C this would usually be done by casting. The `len` argument +/// should be the number of bytes in the `sockaddr_storage` that are actually +/// allocated and valid. It must be at least as large as all the useful parts +/// of the structure. Note that in the case of a `sockaddr_un`, `len` need not +/// include the terminating null. +pub fn sockaddr_storage_to_addr( + addr: &sockaddr_storage, + len: usize) -> Result { + + assert!(len <= mem::size_of::()); + if len < mem::size_of_val(&addr.ss_family) { + return Err(Errno::ENOTCONN); + } + + match c_int::from(addr.ss_family) { + libc::AF_INET => { + assert!(len as usize >= mem::size_of::()); + let sin = unsafe { + *(addr as *const sockaddr_storage as *const sockaddr_in) + }; + Ok(SockAddr::Inet(InetAddr::V4(sin))) + } + libc::AF_INET6 => { + assert!(len as usize >= mem::size_of::()); + let sin6 = unsafe { + *(addr as *const _ as *const sockaddr_in6) + }; + Ok(SockAddr::Inet(InetAddr::V6(sin6))) + } + libc::AF_UNIX => { + let pathlen = len - offset_of!(sockaddr_un, sun_path); + unsafe { + let sun = *(addr as *const _ as *const sockaddr_un); + Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, pathlen))) + } + } + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_PACKET => { + use libc::sockaddr_ll; + // Don't assert anything about the size. + // Apparently the Linux kernel can return smaller sizes when + // the value in the last element of sockaddr_ll (`sll_addr`) is + // smaller than the declared size of that field + let sll = unsafe { + *(addr as *const _ as *const sockaddr_ll) + }; + Ok(SockAddr::Link(LinkAddr(sll))) + } + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_NETLINK => { + use libc::sockaddr_nl; + let snl = unsafe { + *(addr as *const _ as *const sockaddr_nl) + }; + Ok(SockAddr::Netlink(NetlinkAddr(snl))) + } + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_ALG => { + use libc::sockaddr_alg; + let salg = unsafe { + *(addr as *const _ as *const sockaddr_alg) + }; + Ok(SockAddr::Alg(AlgAddr(salg))) + } + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_VSOCK => { + use libc::sockaddr_vm; + let svm = unsafe { + *(addr as *const _ as *const sockaddr_vm) + }; + Ok(SockAddr::Vsock(VsockAddr(svm))) + } + af => panic!("unexpected address family {}", af), + } +} + + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Shutdown { + /// Further receptions will be disallowed. + Read, + /// Further transmissions will be disallowed. + Write, + /// Further receptions and transmissions will be disallowed. + Both, +} + +/// Shut down part of a full-duplex connection. +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html) +pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> { + unsafe { + use libc::shutdown; + + let how = match how { + Shutdown::Read => libc::SHUT_RD, + Shutdown::Write => libc::SHUT_WR, + Shutdown::Both => libc::SHUT_RDWR, + }; + + Errno::result(shutdown(df, how)).map(drop) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn can_use_cmsg_space() { + let _ = cmsg_space!(u8); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/socket/sockopt.rs b/vendor/nix-v0.23.1-patched/src/sys/socket/sockopt.rs new file mode 100644 index 000000000..fcb4be81b --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/socket/sockopt.rs @@ -0,0 +1,930 @@ +//! Socket options as used by `setsockopt` and `getsockopt`. +use cfg_if::cfg_if; +use super::{GetSockOpt, SetSockOpt}; +use crate::Result; +use crate::errno::Errno; +use crate::sys::time::TimeVal; +use libc::{self, c_int, c_void, socklen_t}; +use std::mem::{ + self, + MaybeUninit +}; +use std::os::unix::io::RawFd; +use std::ffi::{OsStr, OsString}; +#[cfg(target_family = "unix")] +use std::os::unix::ffi::OsStrExt; + +// Constants +// TCP_CA_NAME_MAX isn't defined in user space include files +#[cfg(any(target_os = "freebsd", target_os = "linux"))] +const TCP_CA_NAME_MAX: usize = 16; + +/// Helper for implementing `SetSockOpt` for a given socket option. See +/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html). +/// +/// This macro aims to help implementing `SetSockOpt` for different socket options that accept +/// different kinds of data to be used with `setsockopt`. +/// +/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option +/// you are implementing represents a simple type. +/// +/// # Arguments +/// +/// * `$name:ident`: name of the type you want to implement `SetSockOpt` for. +/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets* +/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), +/// and more. Please refer to your system manual for more options. Will be passed as the second +/// argument (`level`) to the `setsockopt` call. +/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, +/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`) +/// to the `setsockopt` call. +/// * Type of the value that you are going to set. +/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for +/// `bool`, `SetUsize` for `usize`, etc.). +macro_rules! setsockopt_impl { + ($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => { + impl SetSockOpt for $name { + type Val = $ty; + + fn set(&self, fd: RawFd, val: &$ty) -> Result<()> { + unsafe { + let setter: $setter = Set::new(val); + + let res = libc::setsockopt(fd, $level, $flag, + setter.ffi_ptr(), + setter.ffi_len()); + Errno::result(res).map(drop) + } + } + } + } +} + +/// Helper for implementing `GetSockOpt` for a given socket option. See +/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html). +/// +/// This macro aims to help implementing `GetSockOpt` for different socket options that accept +/// different kinds of data to be use with `getsockopt`. +/// +/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option +/// you are implementing represents a simple type. +/// +/// # Arguments +/// +/// * Name of the type you want to implement `GetSockOpt` for. +/// * Socket layer, or a `protocol level`: could be *raw sockets* (`lic::SOL_SOCKET`), *ip +/// protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), and more. Please refer +/// to your system manual for more options. Will be passed as the second argument (`level`) to +/// the `getsockopt` call. +/// * A flag to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, +/// `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to +/// the `getsockopt` call. +/// * Type of the value that you are going to get. +/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for +/// `bool`, `GetUsize` for `usize`, etc.). +macro_rules! getsockopt_impl { + ($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => { + impl GetSockOpt for $name { + type Val = $ty; + + fn get(&self, fd: RawFd) -> Result<$ty> { + unsafe { + let mut getter: $getter = Get::uninit(); + + let res = libc::getsockopt(fd, $level, $flag, + getter.ffi_ptr(), + getter.ffi_len()); + Errno::result(res)?; + + Ok(getter.assume_init()) + } + } + } + } +} + +/// Helper to generate the sockopt accessors. See +/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html) and +/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html). +/// +/// This macro aims to help implementing `GetSockOpt` and `SetSockOpt` for different socket options +/// that accept different kinds of data to be use with `getsockopt` and `setsockopt` respectively. +/// +/// Basically this macro wraps up the [`getsockopt_impl!`](macro.getsockopt_impl.html) and +/// [`setsockopt_impl!`](macro.setsockopt_impl.html) macros. +/// +/// # Arguments +/// +/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or +/// both of them. +/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for. +/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets* +/// (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), +/// and more. Please refer to your system manual for more options. Will be passed as the second +/// argument (`level`) to the `getsockopt`/`setsockopt` call. +/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, +/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`) +/// to the `setsockopt`/`getsockopt` call. +/// * `$ty:ty`: type of the value that will be get/set. +/// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`. +/// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`. +macro_rules! sockopt_impl { + ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => { + sockopt_impl!($(#[$attr])* + $name, GetOnly, $level, $flag, bool, GetBool); + }; + + ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => { + sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8); + }; + + ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, usize) => + { + sockopt_impl!($(#[$attr])* + $name, GetOnly, $level, $flag, usize, GetUsize); + }; + + ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => { + sockopt_impl!($(#[$attr])* + $name, SetOnly, $level, $flag, bool, SetBool); + }; + + ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => { + sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8); + }; + + ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, usize) => + { + sockopt_impl!($(#[$attr])* + $name, SetOnly, $level, $flag, usize, SetUsize); + }; + + ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => { + sockopt_impl!($(#[$attr])* + $name, Both, $level, $flag, bool, GetBool, SetBool); + }; + + ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, u8) => { + sockopt_impl!($(#[$attr])* + $name, Both, $level, $flag, u8, GetU8, SetU8); + }; + + ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, usize) => { + sockopt_impl!($(#[$attr])* + $name, Both, $level, $flag, usize, GetUsize, SetUsize); + }; + + ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, + OsString<$array:ty>) => + { + sockopt_impl!($(#[$attr])* + $name, Both, $level, $flag, OsString, GetOsString<$array>, + SetOsString); + }; + + /* + * Matchers with generic getter types must be placed at the end, so + * they'll only match _after_ specialized matchers fail + */ + ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty) => + { + sockopt_impl!($(#[$attr])* + $name, GetOnly, $level, $flag, $ty, GetStruct<$ty>); + }; + + ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty, + $getter:ty) => + { + $(#[$attr])* + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct $name; + + getsockopt_impl!($name, $level, $flag, $ty, $getter); + }; + + ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty) => + { + sockopt_impl!($(#[$attr])* + $name, SetOnly, $level, $flag, $ty, SetStruct<$ty>); + }; + + ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty, + $setter:ty) => + { + $(#[$attr])* + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct $name; + + setsockopt_impl!($name, $level, $flag, $ty, $setter); + }; + + ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty, + $getter:ty, $setter:ty) => + { + $(#[$attr])* + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct $name; + + setsockopt_impl!($name, $level, $flag, $ty, $setter); + getsockopt_impl!($name, $level, $flag, $ty, $getter); + }; + + ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty) => { + sockopt_impl!($(#[$attr])* + $name, Both, $level, $flag, $ty, GetStruct<$ty>, + SetStruct<$ty>); + }; +} + +/* + * + * ===== Define sockopts ===== + * + */ + +sockopt_impl!( + /// Enables local address reuse + ReuseAddr, Both, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool +); +#[cfg(not(any(target_os = "illumos", target_os = "solaris")))] +sockopt_impl!( + /// Permits multiple AF_INET or AF_INET6 sockets to be bound to an + /// identical socket address. + ReusePort, Both, libc::SOL_SOCKET, libc::SO_REUSEPORT, bool); +sockopt_impl!( + /// Under most circumstances, TCP sends data when it is presented; when + /// outstanding data has not yet been acknowledged, it gathers small amounts + /// of output to be sent in a single packet once an acknowledgement is + /// received. For a small number of clients, such as window systems that + /// send a stream of mouse events which receive no replies, this + /// packetization may cause significant delays. The boolean option + /// TCP_NODELAY defeats this algorithm. + TcpNoDelay, Both, libc::IPPROTO_TCP, libc::TCP_NODELAY, bool); +sockopt_impl!( + /// When enabled, a close(2) or shutdown(2) will not return until all + /// queued messages for the socket have been successfully sent or the + /// linger timeout has been reached. + Linger, Both, libc::SOL_SOCKET, libc::SO_LINGER, libc::linger); +sockopt_impl!( + /// Join a multicast group + IpAddMembership, SetOnly, libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP, + super::IpMembershipRequest); +sockopt_impl!( + /// Leave a multicast group. + IpDropMembership, SetOnly, libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP, + super::IpMembershipRequest); +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + sockopt_impl!( + /// Join an IPv6 multicast group. + Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest); + sockopt_impl!( + /// Leave an IPv6 multicast group. + Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest); + } else if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris"))] { + sockopt_impl!( + /// Join an IPv6 multicast group. + Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, + libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest); + sockopt_impl!( + /// Leave an IPv6 multicast group. + Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, + libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest); + } +} +sockopt_impl!( + /// Set or read the time-to-live value of outgoing multicast packets for + /// this socket. + IpMulticastTtl, Both, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, u8); +sockopt_impl!( + /// Set or read a boolean integer argument that determines whether sent + /// multicast packets should be looped back to the local sockets. + IpMulticastLoop, Both, libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP, bool); +#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] +sockopt_impl!( + /// If enabled, this boolean option allows binding to an IP address that + /// is nonlocal or does not (yet) exist. + IpFreebind, Both, libc::IPPROTO_IP, libc::IP_FREEBIND, bool); +sockopt_impl!( + /// Specify the receiving timeout until reporting an error. + ReceiveTimeout, Both, libc::SOL_SOCKET, libc::SO_RCVTIMEO, TimeVal); +sockopt_impl!( + /// Specify the sending timeout until reporting an error. + SendTimeout, Both, libc::SOL_SOCKET, libc::SO_SNDTIMEO, TimeVal); +sockopt_impl!( + /// Set or get the broadcast flag. + Broadcast, Both, libc::SOL_SOCKET, libc::SO_BROADCAST, bool); +sockopt_impl!( + /// If this option is enabled, out-of-band data is directly placed into + /// the receive data stream. + OobInline, Both, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool); +sockopt_impl!( + /// Get and clear the pending socket error. + SocketError, GetOnly, libc::SOL_SOCKET, libc::SO_ERROR, i32); +sockopt_impl!( + /// Enable sending of keep-alive messages on connection-oriented sockets. + KeepAlive, Both, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool); +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "ios" +))] +sockopt_impl!( + /// Get the credentials of the peer process of a connected unix domain + /// socket. + LocalPeerCred, GetOnly, 0, libc::LOCAL_PEERCRED, super::XuCred); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Return the credentials of the foreign process connected to this socket. + PeerCredentials, GetOnly, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials); +#[cfg(any(target_os = "ios", + target_os = "macos"))] +sockopt_impl!( + /// Specify the amount of time, in seconds, that the connection must be idle + /// before keepalive probes (if enabled) are sent. + TcpKeepAlive, Both, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32); +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "nacl"))] +sockopt_impl!( + /// The time (in seconds) the connection needs to remain idle before TCP + /// starts sending keepalive probes + TcpKeepIdle, Both, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32); +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + sockopt_impl!( + /// The maximum segment size for outgoing TCP packets. + TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32); + } else { + sockopt_impl!( + /// The maximum segment size for outgoing TCP packets. + TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32); + } +} +#[cfg(not(target_os = "openbsd"))] +sockopt_impl!( + /// The maximum number of keepalive probes TCP should send before + /// dropping the connection. + TcpKeepCount, Both, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, u32); +#[cfg(any(target_os = "android", + target_os = "fuchsia", + target_os = "linux"))] +sockopt_impl!( + #[allow(missing_docs)] + // Not documented by Linux! + TcpRepair, Both, libc::IPPROTO_TCP, libc::TCP_REPAIR, u32); +#[cfg(not(target_os = "openbsd"))] +sockopt_impl!( + /// The time (in seconds) between individual keepalive probes. + TcpKeepInterval, Both, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, u32); +#[cfg(any(target_os = "fuchsia", target_os = "linux"))] +sockopt_impl!( + /// Specifies the maximum amount of time in milliseconds that transmitted + /// data may remain unacknowledged before TCP will forcibly close the + /// corresponding connection + TcpUserTimeout, Both, libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT, u32); +sockopt_impl!( + /// Sets or gets the maximum socket receive buffer in bytes. + RcvBuf, Both, libc::SOL_SOCKET, libc::SO_RCVBUF, usize); +sockopt_impl!( + /// Sets or gets the maximum socket send buffer in bytes. + SndBuf, Both, libc::SOL_SOCKET, libc::SO_SNDBUF, usize); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can + /// perform the same task as `SO_RCVBUF`, but the `rmem_max limit` can be + /// overridden. + RcvBufForce, SetOnly, libc::SOL_SOCKET, libc::SO_RCVBUFFORCE, usize); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can + /// perform the same task as `SO_SNDBUF`, but the `wmem_max` limit can be + /// overridden. + SndBufForce, SetOnly, libc::SOL_SOCKET, libc::SO_SNDBUFFORCE, usize); +sockopt_impl!( + /// Gets the socket type as an integer. + SockType, GetOnly, libc::SOL_SOCKET, libc::SO_TYPE, super::SockType); +sockopt_impl!( + /// Returns a value indicating whether or not this socket has been marked to + /// accept connections with `listen(2)`. + AcceptConn, GetOnly, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Bind this socket to a particular device like “eth0”. + BindToDevice, Both, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsString<[u8; libc::IFNAMSIZ]>); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + #[allow(missing_docs)] + // Not documented by Linux! + OriginalDst, GetOnly, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + #[allow(missing_docs)] + // Not documented by Linux! + Ip6tOriginalDst, GetOnly, libc::SOL_IPV6, libc::IP6T_SO_ORIGINAL_DST, libc::sockaddr_in6); +sockopt_impl!( + /// Enable or disable the receiving of the `SO_TIMESTAMP` control message. + ReceiveTimestamp, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool); +#[cfg(all(target_os = "linux"))] +sockopt_impl!( + /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message. + ReceiveTimestampns, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Setting this boolean option enables transparent proxying on this socket. + IpTransparent, Both, libc::SOL_IP, libc::IP_TRANSPARENT, bool); +#[cfg(target_os = "openbsd")] +sockopt_impl!( + /// Allows the socket to be bound to addresses which are not local to the + /// machine, so it can be used to make a transparent proxy. + BindAny, Both, libc::SOL_SOCKET, libc::SO_BINDANY, bool); +#[cfg(target_os = "freebsd")] +sockopt_impl!( + /// Can `bind(2)` to any address, even one not bound to any available + /// network interface in the system. + BindAny, Both, libc::IPPROTO_IP, libc::IP_BINDANY, bool); +#[cfg(target_os = "linux")] +sockopt_impl!( + /// Set the mark for each packet sent through this socket (similar to the + /// netfilter MARK target but socket-based). + Mark, Both, libc::SOL_SOCKET, libc::SO_MARK, u32); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Enable or disable the receiving of the `SCM_CREDENTIALS` control + /// message. + PassCred, Both, libc::SOL_SOCKET, libc::SO_PASSCRED, bool); +#[cfg(any(target_os = "freebsd", target_os = "linux"))] +sockopt_impl!( + /// This option allows the caller to set the TCP congestion control + /// algorithm to be used, on a per-socket basis. + TcpCongestion, Both, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>); +#[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", +))] +sockopt_impl!( + /// Pass an `IP_PKTINFO` ancillary message that contains a pktinfo + /// structure that supplies some information about the incoming packet. + Ipv4PacketInfo, Both, libc::IPPROTO_IP, libc::IP_PKTINFO, bool); +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +))] +sockopt_impl!( + /// Set delivery of the `IPV6_PKTINFO` control message on incoming + /// datagrams. + Ipv6RecvPacketInfo, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool); +#[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +))] +sockopt_impl!( + /// The `recvmsg(2)` call returns a `struct sockaddr_dl` corresponding to + /// the interface on which the packet was received. + Ipv4RecvIf, Both, libc::IPPROTO_IP, libc::IP_RECVIF, bool); +#[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +))] +sockopt_impl!( + /// The `recvmsg(2)` call will return the destination IP address for a UDP + /// datagram. + Ipv4RecvDstAddr, Both, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool); +#[cfg(target_os = "linux")] +sockopt_impl!( + #[allow(missing_docs)] + // Not documented by Linux! + UdpGsoSegment, Both, libc::SOL_UDP, libc::UDP_SEGMENT, libc::c_int); +#[cfg(target_os = "linux")] +sockopt_impl!( + #[allow(missing_docs)] + // Not documented by Linux! + UdpGroSegment, Both, libc::IPPROTO_UDP, libc::UDP_GRO, bool); +#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] +sockopt_impl!( + /// Indicates that an unsigned 32-bit value ancillary message (cmsg) should + /// be attached to received skbs indicating the number of packets dropped by + /// the socket since its creation. + RxqOvfl, Both, libc::SOL_SOCKET, libc::SO_RXQ_OVFL, libc::c_int); +sockopt_impl!( + /// The socket is restricted to sending and receiving IPv6 packets only. + Ipv6V6Only, Both, libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Enable extended reliable error message passing. + Ipv4RecvErr, Both, libc::IPPROTO_IP, libc::IP_RECVERR, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!( + /// Control receiving of asynchronous error options. + Ipv6RecvErr, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVERR, bool); +#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +sockopt_impl!( + /// Set or retrieve the current time-to-live field that is used in every + /// packet sent from this socket. + Ipv4Ttl, Both, libc::IPPROTO_IP, libc::IP_TTL, libc::c_int); +#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +sockopt_impl!( + /// Set the unicast hop limit for the socket. + Ipv6Ttl, Both, libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS, libc::c_int); + +#[allow(missing_docs)] +// Not documented by Linux! +#[cfg(any(target_os = "android", target_os = "linux"))] +#[derive(Copy, Clone, Debug)] +pub struct AlgSetAeadAuthSize; + +// ALG_SET_AEAD_AUTH_SIZE read the length from passed `option_len` +// See https://elixir.bootlin.com/linux/v4.4/source/crypto/af_alg.c#L222 +#[cfg(any(target_os = "android", target_os = "linux"))] +impl SetSockOpt for AlgSetAeadAuthSize { + type Val = usize; + + fn set(&self, fd: RawFd, val: &usize) -> Result<()> { + unsafe { + let res = libc::setsockopt(fd, + libc::SOL_ALG, + libc::ALG_SET_AEAD_AUTHSIZE, + ::std::ptr::null(), + *val as libc::socklen_t); + Errno::result(res).map(drop) + } + } +} + +#[allow(missing_docs)] +// Not documented by Linux! +#[cfg(any(target_os = "android", target_os = "linux"))] +#[derive(Clone, Debug)] +pub struct AlgSetKey(::std::marker::PhantomData); + +#[cfg(any(target_os = "android", target_os = "linux"))] +impl Default for AlgSetKey { + fn default() -> Self { + AlgSetKey(Default::default()) + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +impl SetSockOpt for AlgSetKey where T: AsRef<[u8]> + Clone { + type Val = T; + + fn set(&self, fd: RawFd, val: &T) -> Result<()> { + unsafe { + let res = libc::setsockopt(fd, + libc::SOL_ALG, + libc::ALG_SET_KEY, + val.as_ref().as_ptr() as *const _, + val.as_ref().len() as libc::socklen_t); + Errno::result(res).map(drop) + } + } +} + +/* + * + * ===== Accessor helpers ===== + * + */ + +/// Helper trait that describes what is expected from a `GetSockOpt` getter. +trait Get { + /// Returns an uninitialized value. + fn uninit() -> Self; + /// Returns a pointer to the stored value. This pointer will be passed to the system's + /// `getsockopt` call (`man 3p getsockopt`, argument `option_value`). + fn ffi_ptr(&mut self) -> *mut c_void; + /// Returns length of the stored value. This pointer will be passed to the system's + /// `getsockopt` call (`man 3p getsockopt`, argument `option_len`). + fn ffi_len(&mut self) -> *mut socklen_t; + /// Returns the hopefully initialized inner value. + unsafe fn assume_init(self) -> T; +} + +/// Helper trait that describes what is expected from a `SetSockOpt` setter. +trait Set<'a, T> { + /// Initialize the setter with a given value. + fn new(val: &'a T) -> Self; + /// Returns a pointer to the stored value. This pointer will be passed to the system's + /// `setsockopt` call (`man 3p setsockopt`, argument `option_value`). + fn ffi_ptr(&self) -> *const c_void; + /// Returns length of the stored value. This pointer will be passed to the system's + /// `setsockopt` call (`man 3p setsockopt`, argument `option_len`). + fn ffi_len(&self) -> socklen_t; +} + +/// Getter for an arbitrary `struct`. +struct GetStruct { + len: socklen_t, + val: MaybeUninit, +} + +impl Get for GetStruct { + fn uninit() -> Self { + GetStruct { + len: mem::size_of::() as socklen_t, + val: MaybeUninit::uninit(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + self.val.as_mut_ptr() as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn assume_init(self) -> T { + assert_eq!(self.len as usize, mem::size_of::(), "invalid getsockopt implementation"); + self.val.assume_init() + } +} + +/// Setter for an arbitrary `struct`. +struct SetStruct<'a, T: 'static> { + ptr: &'a T, +} + +impl<'a, T> Set<'a, T> for SetStruct<'a, T> { + fn new(ptr: &'a T) -> SetStruct<'a, T> { + SetStruct { ptr } + } + + fn ffi_ptr(&self) -> *const c_void { + self.ptr as *const T as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::() as socklen_t + } +} + +/// Getter for a boolean value. +struct GetBool { + len: socklen_t, + val: MaybeUninit, +} + +impl Get for GetBool { + fn uninit() -> Self { + GetBool { + len: mem::size_of::() as socklen_t, + val: MaybeUninit::uninit(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + self.val.as_mut_ptr() as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn assume_init(self) -> bool { + assert_eq!(self.len as usize, mem::size_of::(), "invalid getsockopt implementation"); + self.val.assume_init() != 0 + } +} + +/// Setter for a boolean value. +struct SetBool { + val: c_int, +} + +impl<'a> Set<'a, bool> for SetBool { + fn new(val: &'a bool) -> SetBool { + SetBool { val: if *val { 1 } else { 0 } } + } + + fn ffi_ptr(&self) -> *const c_void { + &self.val as *const c_int as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::() as socklen_t + } +} + +/// Getter for an `u8` value. +struct GetU8 { + len: socklen_t, + val: MaybeUninit, +} + +impl Get for GetU8 { + fn uninit() -> Self { + GetU8 { + len: mem::size_of::() as socklen_t, + val: MaybeUninit::uninit(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + self.val.as_mut_ptr() as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn assume_init(self) -> u8 { + assert_eq!(self.len as usize, mem::size_of::(), "invalid getsockopt implementation"); + self.val.assume_init() + } +} + +/// Setter for an `u8` value. +struct SetU8 { + val: u8, +} + +impl<'a> Set<'a, u8> for SetU8 { + fn new(val: &'a u8) -> SetU8 { + SetU8 { val: *val as u8 } + } + + fn ffi_ptr(&self) -> *const c_void { + &self.val as *const u8 as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::() as socklen_t + } +} + +/// Getter for an `usize` value. +struct GetUsize { + len: socklen_t, + val: MaybeUninit, +} + +impl Get for GetUsize { + fn uninit() -> Self { + GetUsize { + len: mem::size_of::() as socklen_t, + val: MaybeUninit::uninit(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + self.val.as_mut_ptr() as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn assume_init(self) -> usize { + assert_eq!(self.len as usize, mem::size_of::(), "invalid getsockopt implementation"); + self.val.assume_init() as usize + } +} + +/// Setter for an `usize` value. +struct SetUsize { + val: c_int, +} + +impl<'a> Set<'a, usize> for SetUsize { + fn new(val: &'a usize) -> SetUsize { + SetUsize { val: *val as c_int } + } + + fn ffi_ptr(&self) -> *const c_void { + &self.val as *const c_int as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::() as socklen_t + } +} + +/// Getter for a `OsString` value. +struct GetOsString> { + len: socklen_t, + val: MaybeUninit, +} + +impl> Get for GetOsString { + fn uninit() -> Self { + GetOsString { + len: mem::size_of::() as socklen_t, + val: MaybeUninit::uninit(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + self.val.as_mut_ptr() as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn assume_init(self) -> OsString { + let len = self.len as usize; + let mut v = self.val.assume_init(); + OsStr::from_bytes(&v.as_mut()[0..len]).to_owned() + } +} + +/// Setter for a `OsString` value. +struct SetOsString<'a> { + val: &'a OsStr, +} + +impl<'a> Set<'a, OsString> for SetOsString<'a> { + fn new(val: &'a OsString) -> SetOsString { + SetOsString { val: val.as_os_str() } + } + + fn ffi_ptr(&self) -> *const c_void { + self.val.as_bytes().as_ptr() as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + self.val.len() as socklen_t + } +} + + +#[cfg(test)] +mod test { + #[cfg(any(target_os = "android", target_os = "linux"))] + #[test] + fn can_get_peercred_on_unix_socket() { + use super::super::*; + + let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap(); + let a_cred = getsockopt(a, super::PeerCredentials).unwrap(); + let b_cred = getsockopt(b, super::PeerCredentials).unwrap(); + assert_eq!(a_cred, b_cred); + assert!(a_cred.pid() != 0); + } + + #[test] + fn is_socket_type_unix() { + use super::super::*; + use crate::unistd::close; + + let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap(); + let a_type = getsockopt(a, super::SockType).unwrap(); + assert_eq!(a_type, SockType::Stream); + close(a).unwrap(); + close(b).unwrap(); + } + + #[test] + fn is_socket_type_dgram() { + use super::super::*; + use crate::unistd::close; + + let s = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap(); + let s_type = getsockopt(s, super::SockType).unwrap(); + assert_eq!(s_type, SockType::Datagram); + close(s).unwrap(); + } + + #[cfg(any(target_os = "freebsd", + target_os = "linux", + target_os = "nacl"))] + #[test] + fn can_get_listen_on_tcp_socket() { + use super::super::*; + use crate::unistd::close; + + let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap(); + let s_listening = getsockopt(s, super::AcceptConn).unwrap(); + assert!(!s_listening); + listen(s, 10).unwrap(); + let s_listening2 = getsockopt(s, super::AcceptConn).unwrap(); + assert!(s_listening2); + close(s).unwrap(); + } + +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/stat.rs b/vendor/nix-v0.23.1-patched/src/sys/stat.rs new file mode 100644 index 000000000..c8f10419c --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/stat.rs @@ -0,0 +1,315 @@ +pub use libc::{dev_t, mode_t}; +pub use libc::stat as FileStat; + +use crate::{Result, NixPath, errno::Errno}; +#[cfg(not(target_os = "redox"))] +use crate::fcntl::{AtFlags, at_rawfd}; +use std::mem; +use std::os::unix::io::RawFd; +use crate::sys::time::{TimeSpec, TimeVal}; + +libc_bitflags!( + /// "File type" flags for `mknod` and related functions. + pub struct SFlag: mode_t { + S_IFIFO; + S_IFCHR; + S_IFDIR; + S_IFBLK; + S_IFREG; + S_IFLNK; + S_IFSOCK; + S_IFMT; + } +); + +libc_bitflags! { + /// "File mode / permissions" flags. + pub struct Mode: mode_t { + S_IRWXU; + S_IRUSR; + S_IWUSR; + S_IXUSR; + S_IRWXG; + S_IRGRP; + S_IWGRP; + S_IXGRP; + S_IRWXO; + S_IROTH; + S_IWOTH; + S_IXOTH; + S_ISUID as mode_t; + S_ISGID as mode_t; + S_ISVTX as mode_t; + } +} + +/// Create a special or ordinary file, by pathname. +pub fn mknod(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> { + let res = path.with_nix_path(|cstr| unsafe { + libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev) + })?; + + Errno::result(res).map(drop) +} + +/// Create a special or ordinary file, relative to a given directory. +#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))] +pub fn mknodat( + dirfd: RawFd, + path: &P, + kind: SFlag, + perm: Mode, + dev: dev_t, +) -> Result<()> { + let res = path.with_nix_path(|cstr| unsafe { + libc::mknodat(dirfd, cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev) + })?; + + Errno::result(res).map(drop) +} + +#[cfg(target_os = "linux")] +pub const fn major(dev: dev_t) -> u64 { + ((dev >> 32) & 0xffff_f000) | + ((dev >> 8) & 0x0000_0fff) +} + +#[cfg(target_os = "linux")] +pub const fn minor(dev: dev_t) -> u64 { + ((dev >> 12) & 0xffff_ff00) | + ((dev ) & 0x0000_00ff) +} + +#[cfg(target_os = "linux")] +pub const fn makedev(major: u64, minor: u64) -> dev_t { + ((major & 0xffff_f000) << 32) | + ((major & 0x0000_0fff) << 8) | + ((minor & 0xffff_ff00) << 12) | + (minor & 0x0000_00ff) +} + +pub fn umask(mode: Mode) -> Mode { + let prev = unsafe { libc::umask(mode.bits() as mode_t) }; + Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode") +} + +pub fn stat(path: &P) -> Result { + let mut dst = mem::MaybeUninit::uninit(); + let res = path.with_nix_path(|cstr| { + unsafe { + libc::stat(cstr.as_ptr(), dst.as_mut_ptr()) + } + })?; + + Errno::result(res)?; + + Ok(unsafe{dst.assume_init()}) +} + +pub fn lstat(path: &P) -> Result { + let mut dst = mem::MaybeUninit::uninit(); + let res = path.with_nix_path(|cstr| { + unsafe { + libc::lstat(cstr.as_ptr(), dst.as_mut_ptr()) + } + })?; + + Errno::result(res)?; + + Ok(unsafe{dst.assume_init()}) +} + +pub fn fstat(fd: RawFd) -> Result { + let mut dst = mem::MaybeUninit::uninit(); + let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) }; + + Errno::result(res)?; + + Ok(unsafe{dst.assume_init()}) +} + +#[cfg(not(target_os = "redox"))] +pub fn fstatat(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result { + let mut dst = mem::MaybeUninit::uninit(); + let res = pathname.with_nix_path(|cstr| { + unsafe { libc::fstatat(dirfd, cstr.as_ptr(), dst.as_mut_ptr(), f.bits() as libc::c_int) } + })?; + + Errno::result(res)?; + + Ok(unsafe{dst.assume_init()}) +} + +/// Change the file permission bits of the file specified by a file descriptor. +/// +/// # References +/// +/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html). +pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> { + let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) }; + + Errno::result(res).map(drop) +} + +/// Flags for `fchmodat` function. +#[derive(Clone, Copy, Debug)] +pub enum FchmodatFlags { + FollowSymlink, + NoFollowSymlink, +} + +/// Change the file permission bits. +/// +/// The file to be changed is determined relative to the directory associated +/// with the file descriptor `dirfd` or the current working directory +/// if `dirfd` is `None`. +/// +/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link, +/// then the mode of the symbolic link is changed. +/// +/// `fchmodat(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to +/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented +/// in the `nix` crate. +/// +/// # References +/// +/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html). +#[cfg(not(target_os = "redox"))] +pub fn fchmodat( + dirfd: Option, + path: &P, + mode: Mode, + flag: FchmodatFlags, +) -> Result<()> { + let atflag = + match flag { + FchmodatFlags::FollowSymlink => AtFlags::empty(), + FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, + }; + let res = path.with_nix_path(|cstr| unsafe { + libc::fchmodat( + at_rawfd(dirfd), + cstr.as_ptr(), + mode.bits() as mode_t, + atflag.bits() as libc::c_int, + ) + })?; + + Errno::result(res).map(drop) +} + +/// Change the access and modification times of a file. +/// +/// `utimes(path, times)` is identical to +/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former +/// is a deprecated API so prefer using the latter if the platforms you care +/// about support it. +/// +/// # References +/// +/// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html). +pub fn utimes(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> { + let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()]; + let res = path.with_nix_path(|cstr| unsafe { + libc::utimes(cstr.as_ptr(), ×[0]) + })?; + + Errno::result(res).map(drop) +} + +/// Change the access and modification times of a file without following symlinks. +/// +/// `lutimes(path, times)` is identical to +/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former +/// is a deprecated API so prefer using the latter if the platforms you care +/// about support it. +/// +/// # References +/// +/// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html). +#[cfg(any(target_os = "linux", + target_os = "haiku", + target_os = "ios", + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd"))] +pub fn lutimes(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> { + let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()]; + let res = path.with_nix_path(|cstr| unsafe { + libc::lutimes(cstr.as_ptr(), ×[0]) + })?; + + Errno::result(res).map(drop) +} + +/// Change the access and modification times of the file specified by a file descriptor. +/// +/// # References +/// +/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html). +#[inline] +pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> { + let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()]; + let res = unsafe { libc::futimens(fd, ×[0]) }; + + Errno::result(res).map(drop) +} + +/// Flags for `utimensat` function. +// TODO: replace with fcntl::AtFlags +#[derive(Clone, Copy, Debug)] +pub enum UtimensatFlags { + FollowSymlink, + NoFollowSymlink, +} + +/// Change the access and modification times of a file. +/// +/// The file to be changed is determined relative to the directory associated +/// with the file descriptor `dirfd` or the current working directory +/// if `dirfd` is `None`. +/// +/// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link, +/// then the mode of the symbolic link is changed. +/// +/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to +/// `utimes(path, times)`. The latter is a deprecated API so prefer using the +/// former if the platforms you care about support it. +/// +/// # References +/// +/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html). +#[cfg(not(target_os = "redox"))] +pub fn utimensat( + dirfd: Option, + path: &P, + atime: &TimeSpec, + mtime: &TimeSpec, + flag: UtimensatFlags +) -> Result<()> { + let atflag = + match flag { + UtimensatFlags::FollowSymlink => AtFlags::empty(), + UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, + }; + let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()]; + let res = path.with_nix_path(|cstr| unsafe { + libc::utimensat( + at_rawfd(dirfd), + cstr.as_ptr(), + ×[0], + atflag.bits() as libc::c_int, + ) + })?; + + Errno::result(res).map(drop) +} + +#[cfg(not(target_os = "redox"))] +pub fn mkdirat(fd: RawFd, path: &P, mode: Mode) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t) } + })?; + + Errno::result(res).map(drop) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/statfs.rs b/vendor/nix-v0.23.1-patched/src/sys/statfs.rs new file mode 100644 index 000000000..829be57f6 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/statfs.rs @@ -0,0 +1,622 @@ +//! Get filesystem statistics, non-portably +//! +//! See [`statvfs`](crate::sys::statvfs) for a portable alternative. +use std::fmt::{self, Debug}; +use std::mem; +use std::os::unix::io::AsRawFd; +#[cfg(not(any(target_os = "linux", target_os = "android")))] +use std::ffi::CStr; + +use crate::{NixPath, Result, errno::Errno}; + +/// Identifies a mounted file system +#[cfg(target_os = "android")] +pub type fsid_t = libc::__fsid_t; +/// Identifies a mounted file system +#[cfg(not(target_os = "android"))] +pub type fsid_t = libc::fsid_t; + +/// Describes a mounted file system +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct Statfs(libc::statfs); + +#[cfg(target_os = "freebsd")] +type fs_type_t = u32; +#[cfg(target_os = "android")] +type fs_type_t = libc::c_ulong; +#[cfg(all(target_os = "linux", target_arch = "s390x"))] +type fs_type_t = libc::c_uint; +#[cfg(all(target_os = "linux", target_env = "musl"))] +type fs_type_t = libc::c_ulong; +#[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] +type fs_type_t = libc::__fsword_t; + +/// Describes the file system type as known by the operating system. +#[cfg(any( + target_os = "freebsd", + target_os = "android", + all(target_os = "linux", target_arch = "s390x"), + all(target_os = "linux", target_env = "musl"), + all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))), +))] +#[derive(Eq, Copy, Clone, PartialEq, Debug)] +pub struct FsType(pub fs_type_t); + +// These constants are defined without documentation in the Linux headers, so we +// can't very well document them here. +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const ADFS_SUPER_MAGIC: FsType = FsType(libc::ADFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const AFFS_SUPER_MAGIC: FsType = FsType(libc::AFFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const CODA_SUPER_MAGIC: FsType = FsType(libc::CODA_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const EXT2_SUPER_MAGIC: FsType = FsType(libc::EXT2_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const EXT3_SUPER_MAGIC: FsType = FsType(libc::EXT3_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const EXT4_SUPER_MAGIC: FsType = FsType(libc::EXT4_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const HPFS_SUPER_MAGIC: FsType = FsType(libc::HPFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const ISOFS_SUPER_MAGIC: FsType = FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const JFFS2_SUPER_MAGIC: FsType = FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const MINIX_SUPER_MAGIC: FsType = FsType(libc::MINIX_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const MINIX_SUPER_MAGIC2: FsType = FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const MINIX2_SUPER_MAGIC: FsType = FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const MINIX2_SUPER_MAGIC2: FsType = FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const MSDOS_SUPER_MAGIC: FsType = FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const OPENPROM_SUPER_MAGIC: FsType = FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const OVERLAYFS_SUPER_MAGIC: FsType = FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const PROC_SUPER_MAGIC: FsType = FsType(libc::PROC_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const QNX4_SUPER_MAGIC: FsType = FsType(libc::QNX4_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const REISERFS_SUPER_MAGIC: FsType = FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const USBDEVICE_SUPER_MAGIC: FsType = FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const CGROUP_SUPER_MAGIC: FsType = FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t); +#[cfg(all(target_os = "linux", not(target_env = "musl")))] +#[allow(missing_docs)] +pub const CGROUP2_SUPER_MAGIC: FsType = FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t); + + +impl Statfs { + /// Magic code defining system type + #[cfg(not(any( + target_os = "openbsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos" + )))] + pub fn filesystem_type(&self) -> FsType { + FsType(self.0.f_type) + } + + /// Magic code defining system type + #[cfg(not(any(target_os = "linux", target_os = "android")))] + pub fn filesystem_type_name(&self) -> &str { + let c_str = unsafe { CStr::from_ptr(self.0.f_fstypename.as_ptr()) }; + c_str.to_str().unwrap() + } + + /// Optimal transfer block size + #[cfg(any(target_os = "ios", target_os = "macos"))] + pub fn optimal_transfer_size(&self) -> i32 { + self.0.f_iosize + } + + /// Optimal transfer block size + #[cfg(target_os = "openbsd")] + pub fn optimal_transfer_size(&self) -> u32 { + self.0.f_iosize + } + + /// Optimal transfer block size + #[cfg(all(target_os = "linux", target_arch = "s390x"))] + pub fn optimal_transfer_size(&self) -> u32 { + self.0.f_bsize + } + + /// Optimal transfer block size + #[cfg(any( + target_os = "android", + all(target_os = "linux", target_env = "musl") + ))] + pub fn optimal_transfer_size(&self) -> libc::c_ulong { + self.0.f_bsize + } + + /// Optimal transfer block size + #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] + pub fn optimal_transfer_size(&self) -> libc::__fsword_t { + self.0.f_bsize + } + + /// Optimal transfer block size + #[cfg(target_os = "dragonfly")] + pub fn optimal_transfer_size(&self) -> libc::c_long { + self.0.f_iosize + } + + /// Optimal transfer block size + #[cfg(target_os = "freebsd")] + pub fn optimal_transfer_size(&self) -> u64 { + self.0.f_iosize + } + + /// Size of a block + #[cfg(any(target_os = "ios", target_os = "macos", target_os = "openbsd"))] + pub fn block_size(&self) -> u32 { + self.0.f_bsize + } + + /// Size of a block + // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471 + #[cfg(all(target_os = "linux", target_arch = "s390x"))] + pub fn block_size(&self) -> u32 { + self.0.f_bsize + } + + /// Size of a block + // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471 + #[cfg(all(target_os = "linux", target_env = "musl"))] + pub fn block_size(&self) -> libc::c_ulong { + self.0.f_bsize + } + + /// Size of a block + // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471 + #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] + pub fn block_size(&self) -> libc::__fsword_t { + self.0.f_bsize + } + + /// Size of a block + #[cfg(target_os = "freebsd")] + pub fn block_size(&self) -> u64 { + self.0.f_bsize + } + + /// Size of a block + #[cfg(target_os = "android")] + pub fn block_size(&self) -> libc::c_ulong { + self.0.f_bsize + } + + /// Size of a block + #[cfg(target_os = "dragonfly")] + pub fn block_size(&self) -> libc::c_long { + self.0.f_bsize + } + + /// Maximum length of filenames + #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] + pub fn maximum_name_length(&self) -> u32 { + self.0.f_namemax + } + + /// Maximum length of filenames + #[cfg(all(target_os = "linux", target_arch = "s390x"))] + pub fn maximum_name_length(&self) -> u32 { + self.0.f_namelen + } + + /// Maximum length of filenames + #[cfg(all(target_os = "linux", target_env = "musl"))] + pub fn maximum_name_length(&self) -> libc::c_ulong { + self.0.f_namelen + } + + /// Maximum length of filenames + #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] + pub fn maximum_name_length(&self) -> libc::__fsword_t { + self.0.f_namelen + } + + /// Maximum length of filenames + #[cfg(target_os = "android")] + pub fn maximum_name_length(&self) -> libc::c_ulong { + self.0.f_namelen + } + + /// Total data blocks in filesystem + #[cfg(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + ))] + pub fn blocks(&self) -> u64 { + self.0.f_blocks + } + + /// Total data blocks in filesystem + #[cfg(target_os = "dragonfly")] + pub fn blocks(&self) -> libc::c_long { + self.0.f_blocks + } + + /// Total data blocks in filesystem + #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))] + pub fn blocks(&self) -> u64 { + self.0.f_blocks + } + + /// Total data blocks in filesystem + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))) + )))] + pub fn blocks(&self) -> libc::c_ulong { + self.0.f_blocks + } + + /// Free blocks in filesystem + #[cfg(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + ))] + pub fn blocks_free(&self) -> u64 { + self.0.f_bfree + } + + /// Free blocks in filesystem + #[cfg(target_os = "dragonfly")] + pub fn blocks_free(&self) -> libc::c_long { + self.0.f_bfree + } + + /// Free blocks in filesystem + #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))] + pub fn blocks_free(&self) -> u64 { + self.0.f_bfree + } + + /// Free blocks in filesystem + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))) + )))] + pub fn blocks_free(&self) -> libc::c_ulong { + self.0.f_bfree + } + + /// Free blocks available to unprivileged user + #[cfg(any(target_os = "ios", target_os = "macos", target_os = "android"))] + pub fn blocks_available(&self) -> u64 { + self.0.f_bavail + } + + /// Free blocks available to unprivileged user + #[cfg(target_os = "dragonfly")] + pub fn blocks_available(&self) -> libc::c_long { + self.0.f_bavail + } + + /// Free blocks available to unprivileged user + #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] + pub fn blocks_available(&self) -> i64 { + self.0.f_bavail + } + + /// Free blocks available to unprivileged user + #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))] + pub fn blocks_available(&self) -> u64 { + self.0.f_bavail + } + + /// Free blocks available to unprivileged user + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))) + )))] + pub fn blocks_available(&self) -> libc::c_ulong { + self.0.f_bavail + } + + /// Total file nodes in filesystem + #[cfg(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + ))] + pub fn files(&self) -> u64 { + self.0.f_files + } + + /// Total file nodes in filesystem + #[cfg(target_os = "dragonfly")] + pub fn files(&self) -> libc::c_long { + self.0.f_files + } + + /// Total file nodes in filesystem + #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))] + pub fn files(&self) -> libc::fsfilcnt_t { + self.0.f_files + } + + /// Total file nodes in filesystem + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))) + )))] + pub fn files(&self) -> libc::c_ulong { + self.0.f_files + } + + /// Free file nodes in filesystem + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "macos", + target_os = "openbsd" + ))] + pub fn files_free(&self) -> u64 { + self.0.f_ffree + } + + /// Free file nodes in filesystem + #[cfg(target_os = "dragonfly")] + pub fn files_free(&self) -> libc::c_long { + self.0.f_ffree + } + + /// Free file nodes in filesystem + #[cfg(target_os = "freebsd")] + pub fn files_free(&self) -> i64 { + self.0.f_ffree + } + + /// Free file nodes in filesystem + #[cfg(all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))))] + pub fn files_free(&self) -> libc::fsfilcnt_t { + self.0.f_ffree + } + + /// Free file nodes in filesystem + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + all(target_os = "linux", any(target_env = "musl", all(target_arch = "x86_64", target_pointer_width = "32"))) + )))] + pub fn files_free(&self) -> libc::c_ulong { + self.0.f_ffree + } + + /// Filesystem ID + pub fn filesystem_id(&self) -> fsid_t { + self.0.f_fsid + } +} + +impl Debug for Statfs { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Statfs") + .field("optimal_transfer_size", &self.optimal_transfer_size()) + .field("block_size", &self.block_size()) + .field("blocks", &self.blocks()) + .field("blocks_free", &self.blocks_free()) + .field("blocks_available", &self.blocks_available()) + .field("files", &self.files()) + .field("files_free", &self.files_free()) + .field("filesystem_id", &self.filesystem_id()) + .finish() + } +} + +/// Describes a mounted file system. +/// +/// The result is OS-dependent. For a portabable alternative, see +/// [`statvfs`](crate::sys::statvfs::statvfs). +/// +/// # Arguments +/// +/// `path` - Path to any file within the file system to describe +pub fn statfs(path: &P) -> Result { + unsafe { + let mut stat = mem::MaybeUninit::::uninit(); + let res = path.with_nix_path(|path| libc::statfs(path.as_ptr(), stat.as_mut_ptr()))?; + Errno::result(res).map(|_| Statfs(stat.assume_init())) + } +} + +/// Describes a mounted file system. +/// +/// The result is OS-dependent. For a portabable alternative, see +/// [`fstatvfs`](crate::sys::statvfs::fstatvfs). +/// +/// # Arguments +/// +/// `fd` - File descriptor of any open file within the file system to describe +pub fn fstatfs(fd: &T) -> Result { + unsafe { + let mut stat = mem::MaybeUninit::::uninit(); + Errno::result(libc::fstatfs(fd.as_raw_fd(), stat.as_mut_ptr())) + .map(|_| Statfs(stat.assume_init())) + } +} + +#[cfg(test)] +mod test { + use std::fs::File; + + use crate::sys::statfs::*; + use crate::sys::statvfs::*; + use std::path::Path; + + #[test] + fn statfs_call() { + check_statfs("/tmp"); + check_statfs("/dev"); + check_statfs("/run"); + check_statfs("/"); + } + + #[test] + fn fstatfs_call() { + check_fstatfs("/tmp"); + check_fstatfs("/dev"); + check_fstatfs("/run"); + check_fstatfs("/"); + } + + fn check_fstatfs(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()).unwrap(); + let file = File::open(path).unwrap(); + let fs = fstatfs(&file).unwrap(); + assert_fs_equals(fs, vfs); + } + + fn check_statfs(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()).unwrap(); + let fs = statfs(path.as_bytes()).unwrap(); + assert_fs_equals(fs, vfs); + } + + fn assert_fs_equals(fs: Statfs, vfs: Statvfs) { + assert_eq!(fs.files() as u64, vfs.files() as u64); + assert_eq!(fs.blocks() as u64, vfs.blocks() as u64); + assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64); + } + + // This test is ignored because files_free/blocks_free can change after statvfs call and before + // statfs call. + #[test] + #[ignore] + fn statfs_call_strict() { + check_statfs_strict("/tmp"); + check_statfs_strict("/dev"); + check_statfs_strict("/run"); + check_statfs_strict("/"); + } + + // This test is ignored because files_free/blocks_free can change after statvfs call and before + // fstatfs call. + #[test] + #[ignore] + fn fstatfs_call_strict() { + check_fstatfs_strict("/tmp"); + check_fstatfs_strict("/dev"); + check_fstatfs_strict("/run"); + check_fstatfs_strict("/"); + } + + fn check_fstatfs_strict(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()); + let file = File::open(path).unwrap(); + let fs = fstatfs(&file); + assert_fs_equals_strict(fs.unwrap(), vfs.unwrap()) + } + + fn check_statfs_strict(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()); + let fs = statfs(path.as_bytes()); + assert_fs_equals_strict(fs.unwrap(), vfs.unwrap()) + } + + fn assert_fs_equals_strict(fs: Statfs, vfs: Statvfs) { + assert_eq!(fs.files_free() as u64, vfs.files_free() as u64); + assert_eq!(fs.blocks_free() as u64, vfs.blocks_free() as u64); + assert_eq!(fs.blocks_available() as u64, vfs.blocks_available() as u64); + assert_eq!(fs.files() as u64, vfs.files() as u64); + assert_eq!(fs.blocks() as u64, vfs.blocks() as u64); + assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/statvfs.rs b/vendor/nix-v0.23.1-patched/src/sys/statvfs.rs new file mode 100644 index 000000000..15e7a7d4a --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/statvfs.rs @@ -0,0 +1,161 @@ +//! Get filesystem statistics +//! +//! See [the man pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html) +//! for more details. +use std::mem; +use std::os::unix::io::AsRawFd; + +use libc::{self, c_ulong}; + +use crate::{Result, NixPath, errno::Errno}; + +#[cfg(not(target_os = "redox"))] +libc_bitflags!( + /// File system mount Flags + #[repr(C)] + #[derive(Default)] + pub struct FsFlags: c_ulong { + /// Read Only + ST_RDONLY; + /// Do not allow the set-uid bits to have an effect + ST_NOSUID; + /// Do not interpret character or block-special devices + #[cfg(any(target_os = "android", target_os = "linux"))] + ST_NODEV; + /// Do not allow execution of binaries on the filesystem + #[cfg(any(target_os = "android", target_os = "linux"))] + ST_NOEXEC; + /// All IO should be done synchronously + #[cfg(any(target_os = "android", target_os = "linux"))] + ST_SYNCHRONOUS; + /// Allow mandatory locks on the filesystem + #[cfg(any(target_os = "android", target_os = "linux"))] + ST_MANDLOCK; + /// Write on file/directory/symlink + #[cfg(target_os = "linux")] + ST_WRITE; + /// Append-only file + #[cfg(target_os = "linux")] + ST_APPEND; + /// Immutable file + #[cfg(target_os = "linux")] + ST_IMMUTABLE; + /// Do not update access times on files + #[cfg(any(target_os = "android", target_os = "linux"))] + ST_NOATIME; + /// Do not update access times on files + #[cfg(any(target_os = "android", target_os = "linux"))] + ST_NODIRATIME; + /// Update access time relative to modify/change time + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_env = "musl"))))] + ST_RELATIME; + } +); + +/// Wrapper around the POSIX `statvfs` struct +/// +/// For more information see the [`statvfs(3)` man pages](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_statvfs.h.html). +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Statvfs(libc::statvfs); + +impl Statvfs { + /// get the file system block size + pub fn block_size(&self) -> c_ulong { + self.0.f_bsize + } + + /// Get the fundamental file system block size + pub fn fragment_size(&self) -> c_ulong { + self.0.f_frsize + } + + /// Get the number of blocks. + /// + /// Units are in units of `fragment_size()` + pub fn blocks(&self) -> libc::fsblkcnt_t { + self.0.f_blocks + } + + /// Get the number of free blocks in the file system + pub fn blocks_free(&self) -> libc::fsblkcnt_t { + self.0.f_bfree + } + + /// Get the number of free blocks for unprivileged users + pub fn blocks_available(&self) -> libc::fsblkcnt_t { + self.0.f_bavail + } + + /// Get the total number of file inodes + pub fn files(&self) -> libc::fsfilcnt_t { + self.0.f_files + } + + /// Get the number of free file inodes + pub fn files_free(&self) -> libc::fsfilcnt_t { + self.0.f_ffree + } + + /// Get the number of free file inodes for unprivileged users + pub fn files_available(&self) -> libc::fsfilcnt_t { + self.0.f_favail + } + + /// Get the file system id + pub fn filesystem_id(&self) -> c_ulong { + self.0.f_fsid + } + + /// Get the mount flags + #[cfg(not(target_os = "redox"))] + pub fn flags(&self) -> FsFlags { + FsFlags::from_bits_truncate(self.0.f_flag) + } + + /// Get the maximum filename length + pub fn name_max(&self) -> c_ulong { + self.0.f_namemax + } + +} + +/// Return a `Statvfs` object with information about the `path` +pub fn statvfs(path: &P) -> Result { + unsafe { + Errno::clear(); + let mut stat = mem::MaybeUninit::::uninit(); + let res = path.with_nix_path(|path| + libc::statvfs(path.as_ptr(), stat.as_mut_ptr()) + )?; + + Errno::result(res).map(|_| Statvfs(stat.assume_init())) + } +} + +/// Return a `Statvfs` object with information about `fd` +pub fn fstatvfs(fd: &T) -> Result { + unsafe { + Errno::clear(); + let mut stat = mem::MaybeUninit::::uninit(); + Errno::result(libc::fstatvfs(fd.as_raw_fd(), stat.as_mut_ptr())) + .map(|_| Statvfs(stat.assume_init())) + } +} + +#[cfg(test)] +mod test { + use std::fs::File; + use crate::sys::statvfs::*; + + #[test] + fn statvfs_call() { + statvfs(&b"/"[..]).unwrap(); + } + + #[test] + fn fstatvfs_call() { + let root = File::open("/").unwrap(); + fstatvfs(&root).unwrap(); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/sysinfo.rs b/vendor/nix-v0.23.1-patched/src/sys/sysinfo.rs new file mode 100644 index 000000000..dc943c1ad --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/sysinfo.rs @@ -0,0 +1,79 @@ +use libc::{self, SI_LOAD_SHIFT}; +use std::{cmp, mem}; +use std::time::Duration; + +use crate::Result; +use crate::errno::Errno; + +/// System info structure returned by `sysinfo`. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[repr(transparent)] +pub struct SysInfo(libc::sysinfo); + +// The fields are c_ulong on 32-bit linux, u64 on 64-bit linux; x32's ulong is u32 +#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] +type mem_blocks_t = u64; +#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] +type mem_blocks_t = libc::c_ulong; + +impl SysInfo { + /// Returns the load average tuple. + /// + /// The returned values represent the load average over time intervals of + /// 1, 5, and 15 minutes, respectively. + pub fn load_average(&self) -> (f64, f64, f64) { + ( + self.0.loads[0] as f64 / (1 << SI_LOAD_SHIFT) as f64, + self.0.loads[1] as f64 / (1 << SI_LOAD_SHIFT) as f64, + self.0.loads[2] as f64 / (1 << SI_LOAD_SHIFT) as f64, + ) + } + + /// Returns the time since system boot. + pub fn uptime(&self) -> Duration { + // Truncate negative values to 0 + Duration::from_secs(cmp::max(self.0.uptime, 0) as u64) + } + + /// Current number of processes. + pub fn process_count(&self) -> u16 { + self.0.procs + } + + /// Returns the amount of swap memory in Bytes. + pub fn swap_total(&self) -> u64 { + self.scale_mem(self.0.totalswap) + } + + /// Returns the amount of unused swap memory in Bytes. + pub fn swap_free(&self) -> u64 { + self.scale_mem(self.0.freeswap) + } + + /// Returns the total amount of installed RAM in Bytes. + pub fn ram_total(&self) -> u64 { + self.scale_mem(self.0.totalram) + } + + /// Returns the amount of completely unused RAM in Bytes. + /// + /// "Unused" in this context means that the RAM in neither actively used by + /// programs, nor by the operating system as disk cache or buffer. It is + /// "wasted" RAM since it currently serves no purpose. + pub fn ram_unused(&self) -> u64 { + self.scale_mem(self.0.freeram) + } + + fn scale_mem(&self, units: mem_blocks_t) -> u64 { + units as u64 * self.0.mem_unit as u64 + } +} + +/// Returns system information. +/// +/// [See `sysinfo(2)`](https://man7.org/linux/man-pages/man2/sysinfo.2.html). +pub fn sysinfo() -> Result { + let mut info = mem::MaybeUninit::uninit(); + let res = unsafe { libc::sysinfo(info.as_mut_ptr()) }; + Errno::result(res).map(|_| unsafe{ SysInfo(info.assume_init()) }) +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/termios.rs b/vendor/nix-v0.23.1-patched/src/sys/termios.rs new file mode 100644 index 000000000..01d460803 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/termios.rs @@ -0,0 +1,1016 @@ +//! An interface for controlling asynchronous communication ports +//! +//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The +//! underlying types are all implemented in libc for most platforms and either wrapped in safer +//! types here or exported directly. +//! +//! If you are unfamiliar with the `termios` API, you should first read the +//! [API documentation](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and +//! then come back to understand how `nix` safely wraps it. +//! +//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions. +//! As this interface is not used with high-bandwidth information, this should be fine in most +//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the +//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields. +//! This means that when crossing the FFI interface to the underlying C library, data is first +//! copied into the underlying `termios` struct, then the operation is done, and the data is copied +//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is +//! relatively small across all platforms (on the order of 32-64 bytes). +//! +//! The following examples highlight some of the API use cases such that users coming from using C +//! or reading the standard documentation will understand how to use the safe API exposed here. +//! +//! Example disabling processing of the end-of-file control character: +//! +//! ``` +//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF; +//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios}; +//! # let mut termios: Termios = unsafe { std::mem::zeroed() }; +//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE; +//! ``` +//! +//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides +//! an interface for working with bitfields that is similar to working with the raw unsigned +//! integer types but offers type safety because of the internal checking that values will always +//! be a valid combination of the defined flags. +//! +//! An example showing some of the basic operations for interacting with the control flags: +//! +//! ``` +//! # use self::nix::sys::termios::{ControlFlags, Termios}; +//! # let mut termios: Termios = unsafe { std::mem::zeroed() }; +//! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5; +//! termios.control_flags |= ControlFlags::CS5; +//! ``` +//! +//! # Baud rates +//! +//! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both +//! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs +//! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer +//! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following +//! conventions: +//! +//! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux +//! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux +//! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux +//! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux +//! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux +//! +//! The most common use case of specifying a baud rate using the enum will work the same across +//! platforms: +//! +//! ```rust +//! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t: Termios = unsafe { std::mem::zeroed() }; +//! cfsetispeed(&mut t, BaudRate::B9600); +//! cfsetospeed(&mut t, BaudRate::B9600); +//! cfsetspeed(&mut t, BaudRate::B9600); +//! # } +//! ``` +//! +//! Additionally round-tripping baud rates is consistent across platforms: +//! +//! ```rust +//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t: Termios = unsafe { std::mem::zeroed() }; +//! # cfsetspeed(&mut t, BaudRate::B9600); +//! let speed = cfgetispeed(&t); +//! assert_eq!(speed, cfgetospeed(&t)); +//! cfsetispeed(&mut t, speed); +//! # } +//! ``` +//! +//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`: +//! +#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"), + doc = " ```rust,ignore")] +#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd")), + doc = " ```rust")] +//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t: Termios = unsafe { std::mem::zeroed() }; +//! # cfsetspeed(&mut t, BaudRate::B9600); +//! assert_eq!(cfgetispeed(&t), BaudRate::B9600); +//! assert_eq!(cfgetospeed(&t), BaudRate::B9600); +//! # } +//! ``` +//! +//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s: +//! +#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"), + doc = " ```rust")] +#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd")), + doc = " ```rust,ignore")] +//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t: Termios = unsafe { std::mem::zeroed() }; +//! # cfsetspeed(&mut t, 9600u32); +//! assert_eq!(cfgetispeed(&t), 9600u32); +//! assert_eq!(cfgetospeed(&t), 9600u32); +//! # } +//! ``` +//! +//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs: +//! +#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"), + doc = " ```rust")] +#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd")), + doc = " ```rust,ignore")] +//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t: Termios = unsafe { std::mem::zeroed() }; +//! # cfsetspeed(&mut t, 9600u32); +//! assert_eq!(cfgetispeed(&t), BaudRate::B9600.into()); +//! assert_eq!(u32::from(BaudRate::B9600), 9600u32); +//! # } +//! ``` +//! +//! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support) +//! by specifying baud rates directly using `u32`s: +//! +#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"), + doc = " ```rust")] +#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd")), + doc = " ```rust,ignore")] +//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t: Termios = unsafe { std::mem::zeroed() }; +//! cfsetispeed(&mut t, 9600u32); +//! cfsetospeed(&mut t, 9600u32); +//! cfsetspeed(&mut t, 9600u32); +//! # } +//! ``` +use cfg_if::cfg_if; +use crate::Result; +use crate::errno::Errno; +use libc::{self, c_int, tcflag_t}; +use std::cell::{Ref, RefCell}; +use std::convert::From; +use std::mem; +use std::os::unix::io::RawFd; + +use crate::unistd::Pid; + +/// Stores settings for the termios API +/// +/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the +/// standard fields. The only safe way to obtain an instance of this struct is to extract it from +/// an open port using `tcgetattr()`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Termios { + inner: RefCell, + /// Input mode flags (see `termios.c_iflag` documentation) + pub input_flags: InputFlags, + /// Output mode flags (see `termios.c_oflag` documentation) + pub output_flags: OutputFlags, + /// Control mode flags (see `termios.c_cflag` documentation) + pub control_flags: ControlFlags, + /// Local mode flags (see `termios.c_lflag` documentation) + pub local_flags: LocalFlags, + /// Control characters (see `termios.c_cc` documentation) + pub control_chars: [libc::cc_t; NCCS], +} + +impl Termios { + /// Exposes an immutable reference to the underlying `libc::termios` data structure. + /// + /// This is not part of `nix`'s public API because it requires additional work to maintain type + /// safety. + pub(crate) fn get_libc_termios(&self) -> Ref { + { + let mut termios = self.inner.borrow_mut(); + termios.c_iflag = self.input_flags.bits(); + termios.c_oflag = self.output_flags.bits(); + termios.c_cflag = self.control_flags.bits(); + termios.c_lflag = self.local_flags.bits(); + termios.c_cc = self.control_chars; + } + self.inner.borrow() + } + + /// Exposes the inner `libc::termios` datastore within `Termios`. + /// + /// This is unsafe because if this is used to modify the inner `libc::termios` struct, it will + /// not automatically update the safe wrapper type around it. In this case it should also be + /// paired with a call to `update_wrapper()` so that the wrapper-type and internal + /// representation stay consistent. + pub(crate) unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios { + { + let mut termios = self.inner.borrow_mut(); + termios.c_iflag = self.input_flags.bits(); + termios.c_oflag = self.output_flags.bits(); + termios.c_cflag = self.control_flags.bits(); + termios.c_lflag = self.local_flags.bits(); + termios.c_cc = self.control_chars; + } + self.inner.as_ptr() + } + + /// Updates the wrapper values from the internal `libc::termios` data structure. + pub(crate) fn update_wrapper(&mut self) { + let termios = *self.inner.borrow_mut(); + self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag); + self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag); + self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag); + self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag); + self.control_chars = termios.c_cc; + } +} + +impl From for Termios { + fn from(termios: libc::termios) -> Self { + Termios { + inner: RefCell::new(termios), + input_flags: InputFlags::from_bits_truncate(termios.c_iflag), + output_flags: OutputFlags::from_bits_truncate(termios.c_oflag), + control_flags: ControlFlags::from_bits_truncate(termios.c_cflag), + local_flags: LocalFlags::from_bits_truncate(termios.c_lflag), + control_chars: termios.c_cc, + } + } +} + +impl From for libc::termios { + fn from(termios: Termios) -> Self { + termios.inner.into_inner() + } +} + +libc_enum!{ + /// Baud rates supported by the system. + /// + /// For the BSDs, arbitrary baud rates can be specified by using `u32`s directly instead of this + /// enum. + /// + /// B0 is special and will disable the port. + #[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))] + #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), repr(u32))] + #[non_exhaustive] + pub enum BaudRate { + B0, + B50, + B75, + B110, + B134, + B150, + B200, + B300, + B600, + B1200, + B1800, + B2400, + B4800, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B7200, + B9600, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B14400, + B19200, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B28800, + B38400, + B57600, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B76800, + B115200, + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + B153600, + B230400, + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + B307200, + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "solaris"))] + B460800, + #[cfg(any(target_os = "android", target_os = "linux"))] + B500000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B576000, + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "solaris"))] + B921600, + #[cfg(any(target_os = "android", target_os = "linux"))] + B1000000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B1152000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B1500000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B2000000, + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] + B2500000, + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] + B3000000, + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] + B3500000, + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] + B4000000, + } + impl TryFrom +} + +#[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +impl From for u32 { + fn from(b: BaudRate) -> u32 { + b as u32 + } +} + +// TODO: Add TCSASOFT, which will require treating this as a bitfield. +libc_enum! { + /// Specify when a port configuration change should occur. + /// + /// Used as an argument to `tcsetattr()` + #[repr(i32)] + #[non_exhaustive] + pub enum SetArg { + /// The change will occur immediately + TCSANOW, + /// The change occurs after all output has been written + TCSADRAIN, + /// Same as `TCSADRAIN`, but will also flush the input buffer + TCSAFLUSH, + } +} + +libc_enum! { + /// Specify a combination of the input and output buffers to flush + /// + /// Used as an argument to `tcflush()`. + #[repr(i32)] + #[non_exhaustive] + pub enum FlushArg { + /// Flush data that was received but not read + TCIFLUSH, + /// Flush data written but not transmitted + TCOFLUSH, + /// Flush both received data not read and written data not transmitted + TCIOFLUSH, + } +} + +libc_enum! { + /// Specify how transmission flow should be altered + /// + /// Used as an argument to `tcflow()`. + #[repr(i32)] + #[non_exhaustive] + pub enum FlowArg { + /// Suspend transmission + TCOOFF, + /// Resume transmission + TCOON, + /// Transmit a STOP character, which should disable a connected terminal device + TCIOFF, + /// Transmit a START character, which should re-enable a connected terminal device + TCION, + } +} + +// TODO: Make this usable directly as a slice index. +libc_enum! { + /// Indices into the `termios.c_cc` array for special characters. + #[repr(usize)] + #[non_exhaustive] + pub enum SpecialCharacterIndices { + VDISCARD, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris"))] + VDSUSP, + VEOF, + VEOL, + VEOL2, + VERASE, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "solaris"))] + VERASE2, + VINTR, + VKILL, + VLNEXT, + #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"), + target_os = "illumos", target_os = "solaris")))] + VMIN, + VQUIT, + VREPRINT, + VSTART, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris"))] + VSTATUS, + VSTOP, + VSUSP, + #[cfg(target_os = "linux")] + VSWTC, + #[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))] + VSWTCH, + #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"), + target_os = "illumos", target_os = "solaris")))] + VTIME, + VWERASE, + #[cfg(target_os = "dragonfly")] + VCHECKPT, + } +} + +#[cfg(any(all(target_os = "linux", target_arch = "sparc64"), + target_os = "illumos", target_os = "solaris"))] +impl SpecialCharacterIndices { + pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF; + pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL; +} + +pub use libc::NCCS; +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +pub use libc::_POSIX_VDISABLE; + +libc_bitflags! { + /// Flags for configuring the input mode of a terminal + pub struct InputFlags: tcflag_t { + IGNBRK; + BRKINT; + IGNPAR; + PARMRK; + INPCK; + ISTRIP; + INLCR; + IGNCR; + ICRNL; + IXON; + IXOFF; + #[cfg(not(target_os = "redox"))] + IXANY; + #[cfg(not(target_os = "redox"))] + IMAXBEL; + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] + IUTF8; + } +} + +libc_bitflags! { + /// Flags for configuring the output mode of a terminal + pub struct OutputFlags: tcflag_t { + OPOST; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "linux", + target_os = "openbsd"))] + OLCUC; + ONLCR; + OCRNL as tcflag_t; + ONOCR as tcflag_t; + ONLRET as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + OFILL as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + OFDEL as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + NL0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + NL1 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + CR0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + CR1 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + CR2 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + CR3 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + TAB0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + TAB1 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + TAB2 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + TAB3 as tcflag_t; + #[cfg(any(target_os = "android", target_os = "linux"))] + XTABS; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + BS0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + BS1 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + VT0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + VT1 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + FF0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + FF1 as tcflag_t; + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + OXTABS; + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + ONOEOT as tcflag_t; + + // Bitmasks for use with OutputFlags to select specific settings + // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110 + // is resolved. + + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + NLDLY as tcflag_t; // FIXME: Datatype needs to be corrected in libc for mac + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + CRDLY as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + TABDLY as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + BSDLY as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + VTDLY as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + FFDLY as tcflag_t; + } +} + +libc_bitflags! { + /// Flags for setting the control mode of a terminal + pub struct ControlFlags: tcflag_t { + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + CIGNORE; + CS5; + CS6; + CS7; + CS8; + CSTOPB; + CREAD; + PARENB; + PARODD; + HUPCL; + CLOCAL; + #[cfg(not(target_os = "redox"))] + CRTSCTS; + #[cfg(any(target_os = "android", target_os = "linux"))] + CBAUD; + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))] + CMSPAR; + #[cfg(any(target_os = "android", + all(target_os = "linux", + not(any(target_arch = "powerpc", target_arch = "powerpc64")))))] + CIBAUD; + #[cfg(any(target_os = "android", target_os = "linux"))] + CBAUDEX; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + MDMBUF; + #[cfg(any(target_os = "netbsd", target_os = "openbsd"))] + CHWFLOW; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd"))] + CCTS_OFLOW; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd"))] + CRTS_IFLOW; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd"))] + CDTR_IFLOW; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd"))] + CDSR_OFLOW; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd"))] + CCAR_OFLOW; + + // Bitmasks for use with ControlFlags to select specific settings + // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110 + // is resolved. + + CSIZE; + } +} + +libc_bitflags! { + /// Flags for setting any local modes + pub struct LocalFlags: tcflag_t { + #[cfg(not(target_os = "redox"))] + ECHOKE; + ECHOE; + ECHOK; + ECHO; + ECHONL; + #[cfg(not(target_os = "redox"))] + ECHOPRT; + #[cfg(not(target_os = "redox"))] + ECHOCTL; + ISIG; + ICANON; + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + ALTWERASE; + IEXTEN; + #[cfg(not(target_os = "redox"))] + EXTPROC; + TOSTOP; + #[cfg(not(target_os = "redox"))] + FLUSHO; + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + NOKERNINFO; + #[cfg(not(target_os = "redox"))] + PENDIN; + NOFLSH; + } +} + +cfg_if!{ + if #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] { + /// Get input baud rate (see + /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)). + /// + /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure. + pub fn cfgetispeed(termios: &Termios) -> u32 { + let inner_termios = termios.get_libc_termios(); + unsafe { libc::cfgetispeed(&*inner_termios) as u32 } + } + + /// Get output baud rate (see + /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)). + /// + /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure. + pub fn cfgetospeed(termios: &Termios) -> u32 { + let inner_termios = termios.get_libc_termios(); + unsafe { libc::cfgetospeed(&*inner_termios) as u32 } + } + + /// Set input baud rate (see + /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)). + /// + /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure. + pub fn cfsetispeed>(termios: &mut Termios, baud: T) -> Result<()> { + let inner_termios = unsafe { termios.get_libc_termios_mut() }; + let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) }; + termios.update_wrapper(); + Errno::result(res).map(drop) + } + + /// Set output baud rate (see + /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)). + /// + /// `cfsetospeed()` sets the output baud rate in the given termios structure. + pub fn cfsetospeed>(termios: &mut Termios, baud: T) -> Result<()> { + let inner_termios = unsafe { termios.get_libc_termios_mut() }; + let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) }; + termios.update_wrapper(); + Errno::result(res).map(drop) + } + + /// Set both the input and output baud rates (see + /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)). + /// + /// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that + /// this is part of the 4.4BSD standard and not part of POSIX. + pub fn cfsetspeed>(termios: &mut Termios, baud: T) -> Result<()> { + let inner_termios = unsafe { termios.get_libc_termios_mut() }; + let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) }; + termios.update_wrapper(); + Errno::result(res).map(drop) + } + } else { + use std::convert::TryInto; + + /// Get input baud rate (see + /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)). + /// + /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure. + pub fn cfgetispeed(termios: &Termios) -> BaudRate { + let inner_termios = termios.get_libc_termios(); + unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap() + } + + /// Get output baud rate (see + /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)). + /// + /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure. + pub fn cfgetospeed(termios: &Termios) -> BaudRate { + let inner_termios = termios.get_libc_termios(); + unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap() + } + + /// Set input baud rate (see + /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)). + /// + /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure. + pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> { + let inner_termios = unsafe { termios.get_libc_termios_mut() }; + let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) }; + termios.update_wrapper(); + Errno::result(res).map(drop) + } + + /// Set output baud rate (see + /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)). + /// + /// `cfsetospeed()` sets the output baud rate in the given `Termios` structure. + pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> { + let inner_termios = unsafe { termios.get_libc_termios_mut() }; + let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) }; + termios.update_wrapper(); + Errno::result(res).map(drop) + } + + /// Set both the input and output baud rates (see + /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)). + /// + /// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that + /// this is part of the 4.4BSD standard and not part of POSIX. + pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> { + let inner_termios = unsafe { termios.get_libc_termios_mut() }; + let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) }; + termios.update_wrapper(); + Errno::result(res).map(drop) + } + } +} + +/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see +/// [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)). +/// +/// `cfmakeraw()` configures the termios structure such that input is available character-by- +/// character, echoing is disabled, and all special input and output processing is disabled. Note +/// that this is a non-standard function, but is available on Linux and BSDs. +pub fn cfmakeraw(termios: &mut Termios) { + let inner_termios = unsafe { termios.get_libc_termios_mut() }; + unsafe { + libc::cfmakeraw(inner_termios); + } + termios.update_wrapper(); +} + +/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see +/// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)). +/// +/// Note that this is a non-standard function, available on FreeBSD. +#[cfg(target_os = "freebsd")] +pub fn cfmakesane(termios: &mut Termios) { + let inner_termios = unsafe { termios.get_libc_termios_mut() }; + unsafe { + libc::cfmakesane(inner_termios); + } + termios.update_wrapper(); +} + +/// Return the configuration of a port +/// [tcgetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)). +/// +/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying +/// this structure *will not* reconfigure the port, instead the modifications should be done to +/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`. +pub fn tcgetattr(fd: RawFd) -> Result { + let mut termios = mem::MaybeUninit::uninit(); + + let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) }; + + Errno::result(res)?; + + unsafe { Ok(termios.assume_init().into()) } +} + +/// Set the configuration for a terminal (see +/// [tcsetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)). +/// +/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change +/// takes affect at a time specified by `actions`. Note that this function may return success if +/// *any* of the parameters were successfully set, not only if all were set successfully. +pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> { + let inner_termios = termios.get_libc_termios(); + Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop) +} + +/// Block until all output data is written (see +/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)). +pub fn tcdrain(fd: RawFd) -> Result<()> { + Errno::result(unsafe { libc::tcdrain(fd) }).map(drop) +} + +/// Suspend or resume the transmission or reception of data (see +/// [tcflow(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)). +/// +/// `tcflow()` suspends of resumes the transmission or reception of data for the given port +/// depending on the value of `action`. +pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> { + Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop) +} + +/// Discard data in the output or input queue (see +/// [tcflush(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)). +/// +/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both +/// depending on the value of `action`. +pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> { + Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop) +} + +/// Send a break for a specific duration (see +/// [tcsendbreak(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)). +/// +/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream +/// of zero-valued bits for an implementation-defined duration. +pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> { + Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop) +} + +/// Get the session controlled by the given terminal (see +/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)). +pub fn tcgetsid(fd: RawFd) -> Result { + let res = unsafe { libc::tcgetsid(fd) }; + + Errno::result(res).map(Pid::from_raw) +} + +#[cfg(test)] +mod test { + use super::*; + use std::convert::TryFrom; + + #[test] + fn try_from() { + assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0)); + assert!(BaudRate::try_from(999999999).is_err()); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/time.rs b/vendor/nix-v0.23.1-patched/src/sys/time.rs new file mode 100644 index 000000000..ac4247180 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/time.rs @@ -0,0 +1,609 @@ +use std::{cmp, fmt, ops}; +use std::time::Duration; +use std::convert::From; +use libc::{timespec, timeval}; +#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 +pub use libc::{time_t, suseconds_t}; + +pub trait TimeValLike: Sized { + #[inline] + fn zero() -> Self { + Self::seconds(0) + } + + #[inline] + fn hours(hours: i64) -> Self { + let secs = hours.checked_mul(SECS_PER_HOUR) + .expect("TimeValLike::hours ouf of bounds"); + Self::seconds(secs) + } + + #[inline] + fn minutes(minutes: i64) -> Self { + let secs = minutes.checked_mul(SECS_PER_MINUTE) + .expect("TimeValLike::minutes out of bounds"); + Self::seconds(secs) + } + + fn seconds(seconds: i64) -> Self; + fn milliseconds(milliseconds: i64) -> Self; + fn microseconds(microseconds: i64) -> Self; + fn nanoseconds(nanoseconds: i64) -> Self; + + #[inline] + fn num_hours(&self) -> i64 { + self.num_seconds() / 3600 + } + + #[inline] + fn num_minutes(&self) -> i64 { + self.num_seconds() / 60 + } + + fn num_seconds(&self) -> i64; + fn num_milliseconds(&self) -> i64; + fn num_microseconds(&self) -> i64; + fn num_nanoseconds(&self) -> i64; +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct TimeSpec(timespec); + +const NANOS_PER_SEC: i64 = 1_000_000_000; +const SECS_PER_MINUTE: i64 = 60; +const SECS_PER_HOUR: i64 = 3600; + +#[cfg(target_pointer_width = "64")] +const TS_MAX_SECONDS: i64 = (::std::i64::MAX / NANOS_PER_SEC) - 1; + +#[cfg(target_pointer_width = "32")] +const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64; + +const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS; + +// x32 compatibility +// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437 +#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] +type timespec_tv_nsec_t = i64; +#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] +type timespec_tv_nsec_t = libc::c_long; + +impl From for TimeSpec { + fn from(ts: timespec) -> Self { + Self(ts) + } +} + +impl From for TimeSpec { + fn from(duration: Duration) -> Self { + Self::from_duration(duration) + } +} + +impl From for Duration { + fn from(timespec: TimeSpec) -> Self { + Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32) + } +} + +impl AsRef for TimeSpec { + fn as_ref(&self) -> ×pec { + &self.0 + } +} + +impl AsMut for TimeSpec { + fn as_mut(&mut self) -> &mut timespec { + &mut self.0 + } +} + +impl Ord for TimeSpec { + // The implementation of cmp is simplified by assuming that the struct is + // normalized. That is, tv_nsec must always be within [0, 1_000_000_000) + fn cmp(&self, other: &TimeSpec) -> cmp::Ordering { + if self.tv_sec() == other.tv_sec() { + self.tv_nsec().cmp(&other.tv_nsec()) + } else { + self.tv_sec().cmp(&other.tv_sec()) + } + } +} + +impl PartialOrd for TimeSpec { + fn partial_cmp(&self, other: &TimeSpec) -> Option { + Some(self.cmp(other)) + } +} + +impl TimeValLike for TimeSpec { + #[inline] + fn seconds(seconds: i64) -> TimeSpec { + assert!(seconds >= TS_MIN_SECONDS && seconds <= TS_MAX_SECONDS, + "TimeSpec out of bounds; seconds={}", seconds); + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 + TimeSpec(timespec {tv_sec: seconds as time_t, tv_nsec: 0 }) + } + + #[inline] + fn milliseconds(milliseconds: i64) -> TimeSpec { + let nanoseconds = milliseconds.checked_mul(1_000_000) + .expect("TimeSpec::milliseconds out of bounds"); + + TimeSpec::nanoseconds(nanoseconds) + } + + /// Makes a new `TimeSpec` with given number of microseconds. + #[inline] + fn microseconds(microseconds: i64) -> TimeSpec { + let nanoseconds = microseconds.checked_mul(1_000) + .expect("TimeSpec::milliseconds out of bounds"); + + TimeSpec::nanoseconds(nanoseconds) + } + + /// Makes a new `TimeSpec` with given number of nanoseconds. + #[inline] + fn nanoseconds(nanoseconds: i64) -> TimeSpec { + let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC); + assert!(secs >= TS_MIN_SECONDS && secs <= TS_MAX_SECONDS, + "TimeSpec out of bounds"); + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 + TimeSpec(timespec {tv_sec: secs as time_t, + tv_nsec: nanos as timespec_tv_nsec_t }) + } + + fn num_seconds(&self) -> i64 { + if self.tv_sec() < 0 && self.tv_nsec() > 0 { + (self.tv_sec() + 1) as i64 + } else { + self.tv_sec() as i64 + } + } + + fn num_milliseconds(&self) -> i64 { + self.num_nanoseconds() / 1_000_000 + } + + fn num_microseconds(&self) -> i64 { + self.num_nanoseconds() / 1_000_000_000 + } + + fn num_nanoseconds(&self) -> i64 { + let secs = self.num_seconds() * 1_000_000_000; + let nsec = self.nanos_mod_sec(); + secs + nsec as i64 + } +} + +impl TimeSpec { + fn nanos_mod_sec(&self) -> timespec_tv_nsec_t { + if self.tv_sec() < 0 && self.tv_nsec() > 0 { + self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t + } else { + self.tv_nsec() + } + } + + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 + pub const fn tv_sec(&self) -> time_t { + self.0.tv_sec + } + + pub const fn tv_nsec(&self) -> timespec_tv_nsec_t { + self.0.tv_nsec + } + + pub const fn from_duration(duration: Duration) -> Self { + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 + TimeSpec(timespec { + tv_sec: duration.as_secs() as time_t, + tv_nsec: duration.subsec_nanos() as timespec_tv_nsec_t + }) + } + + pub const fn from_timespec(timespec: timespec) -> Self { + Self(timespec) + } +} + +impl ops::Neg for TimeSpec { + type Output = TimeSpec; + + fn neg(self) -> TimeSpec { + TimeSpec::nanoseconds(-self.num_nanoseconds()) + } +} + +impl ops::Add for TimeSpec { + type Output = TimeSpec; + + fn add(self, rhs: TimeSpec) -> TimeSpec { + TimeSpec::nanoseconds( + self.num_nanoseconds() + rhs.num_nanoseconds()) + } +} + +impl ops::Sub for TimeSpec { + type Output = TimeSpec; + + fn sub(self, rhs: TimeSpec) -> TimeSpec { + TimeSpec::nanoseconds( + self.num_nanoseconds() - rhs.num_nanoseconds()) + } +} + +impl ops::Mul for TimeSpec { + type Output = TimeSpec; + + fn mul(self, rhs: i32) -> TimeSpec { + let usec = self.num_nanoseconds().checked_mul(i64::from(rhs)) + .expect("TimeSpec multiply out of bounds"); + + TimeSpec::nanoseconds(usec) + } +} + +impl ops::Div for TimeSpec { + type Output = TimeSpec; + + fn div(self, rhs: i32) -> TimeSpec { + let usec = self.num_nanoseconds() / i64::from(rhs); + TimeSpec::nanoseconds(usec) + } +} + +impl fmt::Display for TimeSpec { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (abs, sign) = if self.tv_sec() < 0 { + (-*self, "-") + } else { + (*self, "") + }; + + let sec = abs.tv_sec(); + + write!(f, "{}", sign)?; + + if abs.tv_nsec() == 0 { + if abs.tv_sec() == 1 { + write!(f, "{} second", sec)?; + } else { + write!(f, "{} seconds", sec)?; + } + } else if abs.tv_nsec() % 1_000_000 == 0 { + write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)?; + } else if abs.tv_nsec() % 1_000 == 0 { + write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)?; + } else { + write!(f, "{}.{:09} seconds", sec, abs.tv_nsec())?; + } + + Ok(()) + } +} + + + +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct TimeVal(timeval); + +const MICROS_PER_SEC: i64 = 1_000_000; + +#[cfg(target_pointer_width = "64")] +const TV_MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1; + +#[cfg(target_pointer_width = "32")] +const TV_MAX_SECONDS: i64 = ::std::isize::MAX as i64; + +const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS; + +impl AsRef for TimeVal { + fn as_ref(&self) -> &timeval { + &self.0 + } +} + +impl AsMut for TimeVal { + fn as_mut(&mut self) -> &mut timeval { + &mut self.0 + } +} + +impl Ord for TimeVal { + // The implementation of cmp is simplified by assuming that the struct is + // normalized. That is, tv_usec must always be within [0, 1_000_000) + fn cmp(&self, other: &TimeVal) -> cmp::Ordering { + if self.tv_sec() == other.tv_sec() { + self.tv_usec().cmp(&other.tv_usec()) + } else { + self.tv_sec().cmp(&other.tv_sec()) + } + } +} + +impl PartialOrd for TimeVal { + fn partial_cmp(&self, other: &TimeVal) -> Option { + Some(self.cmp(other)) + } +} + +impl TimeValLike for TimeVal { + #[inline] + fn seconds(seconds: i64) -> TimeVal { + assert!(seconds >= TV_MIN_SECONDS && seconds <= TV_MAX_SECONDS, + "TimeVal out of bounds; seconds={}", seconds); + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 + TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 }) + } + + #[inline] + fn milliseconds(milliseconds: i64) -> TimeVal { + let microseconds = milliseconds.checked_mul(1_000) + .expect("TimeVal::milliseconds out of bounds"); + + TimeVal::microseconds(microseconds) + } + + /// Makes a new `TimeVal` with given number of microseconds. + #[inline] + fn microseconds(microseconds: i64) -> TimeVal { + let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); + assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, + "TimeVal out of bounds"); + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 + TimeVal(timeval {tv_sec: secs as time_t, + tv_usec: micros as suseconds_t }) + } + + /// Makes a new `TimeVal` with given number of nanoseconds. Some precision + /// will be lost + #[inline] + fn nanoseconds(nanoseconds: i64) -> TimeVal { + let microseconds = nanoseconds / 1000; + let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); + assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, + "TimeVal out of bounds"); + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 + TimeVal(timeval {tv_sec: secs as time_t, + tv_usec: micros as suseconds_t }) + } + + fn num_seconds(&self) -> i64 { + if self.tv_sec() < 0 && self.tv_usec() > 0 { + (self.tv_sec() + 1) as i64 + } else { + self.tv_sec() as i64 + } + } + + fn num_milliseconds(&self) -> i64 { + self.num_microseconds() / 1_000 + } + + fn num_microseconds(&self) -> i64 { + let secs = self.num_seconds() * 1_000_000; + let usec = self.micros_mod_sec(); + secs + usec as i64 + } + + fn num_nanoseconds(&self) -> i64 { + self.num_microseconds() * 1_000 + } +} + +impl TimeVal { + fn micros_mod_sec(&self) -> suseconds_t { + if self.tv_sec() < 0 && self.tv_usec() > 0 { + self.tv_usec() - MICROS_PER_SEC as suseconds_t + } else { + self.tv_usec() + } + } + + #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 + pub const fn tv_sec(&self) -> time_t { + self.0.tv_sec + } + + pub const fn tv_usec(&self) -> suseconds_t { + self.0.tv_usec + } +} + +impl ops::Neg for TimeVal { + type Output = TimeVal; + + fn neg(self) -> TimeVal { + TimeVal::microseconds(-self.num_microseconds()) + } +} + +impl ops::Add for TimeVal { + type Output = TimeVal; + + fn add(self, rhs: TimeVal) -> TimeVal { + TimeVal::microseconds( + self.num_microseconds() + rhs.num_microseconds()) + } +} + +impl ops::Sub for TimeVal { + type Output = TimeVal; + + fn sub(self, rhs: TimeVal) -> TimeVal { + TimeVal::microseconds( + self.num_microseconds() - rhs.num_microseconds()) + } +} + +impl ops::Mul for TimeVal { + type Output = TimeVal; + + fn mul(self, rhs: i32) -> TimeVal { + let usec = self.num_microseconds().checked_mul(i64::from(rhs)) + .expect("TimeVal multiply out of bounds"); + + TimeVal::microseconds(usec) + } +} + +impl ops::Div for TimeVal { + type Output = TimeVal; + + fn div(self, rhs: i32) -> TimeVal { + let usec = self.num_microseconds() / i64::from(rhs); + TimeVal::microseconds(usec) + } +} + +impl fmt::Display for TimeVal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (abs, sign) = if self.tv_sec() < 0 { + (-*self, "-") + } else { + (*self, "") + }; + + let sec = abs.tv_sec(); + + write!(f, "{}", sign)?; + + if abs.tv_usec() == 0 { + if abs.tv_sec() == 1 { + write!(f, "{} second", sec)?; + } else { + write!(f, "{} seconds", sec)?; + } + } else if abs.tv_usec() % 1000 == 0 { + write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)?; + } else { + write!(f, "{}.{:06} seconds", sec, abs.tv_usec())?; + } + + Ok(()) + } +} + +impl From for TimeVal { + fn from(tv: timeval) -> Self { + TimeVal(tv) + } +} + +#[inline] +fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { + (div_floor_64(this, other), mod_floor_64(this, other)) +} + +#[inline] +fn div_floor_64(this: i64, other: i64) -> i64 { + match div_rem_64(this, other) { + (d, r) if (r > 0 && other < 0) + || (r < 0 && other > 0) => d - 1, + (d, _) => d, + } +} + +#[inline] +fn mod_floor_64(this: i64, other: i64) -> i64 { + match this % other { + r if (r > 0 && other < 0) + || (r < 0 && other > 0) => r + other, + r => r, + } +} + +#[inline] +fn div_rem_64(this: i64, other: i64) -> (i64, i64) { + (this / other, this % other) +} + +#[cfg(test)] +mod test { + use super::{TimeSpec, TimeVal, TimeValLike}; + use std::time::Duration; + + #[test] + pub fn test_timespec() { + assert!(TimeSpec::seconds(1) != TimeSpec::zero()); + assert_eq!(TimeSpec::seconds(1) + TimeSpec::seconds(2), + TimeSpec::seconds(3)); + assert_eq!(TimeSpec::minutes(3) + TimeSpec::seconds(2), + TimeSpec::seconds(182)); + } + + #[test] + pub fn test_timespec_from() { + let duration = Duration::new(123, 123_456_789); + let timespec = TimeSpec::nanoseconds(123_123_456_789); + + assert_eq!(TimeSpec::from(duration), timespec); + assert_eq!(Duration::from(timespec), duration); + } + + #[test] + pub fn test_timespec_neg() { + let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123); + let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123); + + assert_eq!(a, -b); + } + + #[test] + pub fn test_timespec_ord() { + assert!(TimeSpec::seconds(1) == TimeSpec::nanoseconds(1_000_000_000)); + assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001)); + assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999)); + assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999)); + assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001)); + } + + #[test] + pub fn test_timespec_fmt() { + assert_eq!(TimeSpec::zero().to_string(), "0 seconds"); + assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds"); + assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds"); + assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds"); + assert_eq!(TimeSpec::nanoseconds(42).to_string(), "0.000000042 seconds"); + assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds"); + } + + #[test] + pub fn test_timeval() { + assert!(TimeVal::seconds(1) != TimeVal::zero()); + assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2), + TimeVal::seconds(3)); + assert_eq!(TimeVal::minutes(3) + TimeVal::seconds(2), + TimeVal::seconds(182)); + } + + #[test] + pub fn test_timeval_ord() { + assert!(TimeVal::seconds(1) == TimeVal::microseconds(1_000_000)); + assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001)); + assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999)); + assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999)); + assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001)); + } + + #[test] + pub fn test_timeval_neg() { + let a = TimeVal::seconds(1) + TimeVal::microseconds(123); + let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123); + + assert_eq!(a, -b); + } + + #[test] + pub fn test_timeval_fmt() { + assert_eq!(TimeVal::zero().to_string(), "0 seconds"); + assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds"); + assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds"); + assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds"); + assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds"); + assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds"); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/timerfd.rs b/vendor/nix-v0.23.1-patched/src/sys/timerfd.rs new file mode 100644 index 000000000..705a3c4d6 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/timerfd.rs @@ -0,0 +1,281 @@ +//! Timer API via file descriptors. +//! +//! Timer FD is a Linux-only API to create timers and get expiration +//! notifications through file descriptors. +//! +//! For more documentation, please read [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html). +//! +//! # Examples +//! +//! Create a new one-shot timer that expires after 1 second. +//! ``` +//! # use std::os::unix::io::AsRawFd; +//! # use nix::sys::timerfd::{TimerFd, ClockId, TimerFlags, TimerSetTimeFlags, +//! # Expiration}; +//! # use nix::sys::time::{TimeSpec, TimeValLike}; +//! # use nix::unistd::read; +//! # +//! // We create a new monotonic timer. +//! let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()) +//! .unwrap(); +//! +//! // We set a new one-shot timer in 1 seconds. +//! timer.set( +//! Expiration::OneShot(TimeSpec::seconds(1)), +//! TimerSetTimeFlags::empty() +//! ).unwrap(); +//! +//! // We wait for the timer to expire. +//! timer.wait().unwrap(); +//! ``` +use crate::sys::time::TimeSpec; +use crate::unistd::read; +use crate::{errno::Errno, Result}; +use bitflags::bitflags; +use libc::c_int; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; + +/// A timerfd instance. This is also a file descriptor, you can feed it to +/// other interfaces consuming file descriptors, epoll for example. +#[derive(Debug)] +pub struct TimerFd { + fd: RawFd, +} + +impl AsRawFd for TimerFd { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +impl FromRawFd for TimerFd { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + TimerFd { fd } + } +} + +libc_enum! { + /// The type of the clock used to mark the progress of the timer. For more + /// details on each kind of clock, please refer to [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html). + #[repr(i32)] + #[non_exhaustive] + pub enum ClockId { + CLOCK_REALTIME, + CLOCK_MONOTONIC, + CLOCK_BOOTTIME, + CLOCK_REALTIME_ALARM, + CLOCK_BOOTTIME_ALARM, + } +} + +libc_bitflags! { + /// Additional flags to change the behaviour of the file descriptor at the + /// time of creation. + pub struct TimerFlags: c_int { + TFD_NONBLOCK; + TFD_CLOEXEC; + } +} + +bitflags! { + /// Flags that are used for arming the timer. + pub struct TimerSetTimeFlags: libc::c_int { + const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME; + } +} + +#[derive(Debug, Clone, Copy)] +struct TimerSpec(libc::itimerspec); + +impl TimerSpec { + pub const fn none() -> Self { + Self(libc::itimerspec { + it_interval: libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }, + it_value: libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }, + }) + } +} + +impl AsRef for TimerSpec { + fn as_ref(&self) -> &libc::itimerspec { + &self.0 + } +} + +impl From for TimerSpec { + fn from(expiration: Expiration) -> TimerSpec { + match expiration { + Expiration::OneShot(t) => TimerSpec(libc::itimerspec { + it_interval: libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }, + it_value: *t.as_ref(), + }), + Expiration::IntervalDelayed(start, interval) => TimerSpec(libc::itimerspec { + it_interval: *interval.as_ref(), + it_value: *start.as_ref(), + }), + Expiration::Interval(t) => TimerSpec(libc::itimerspec { + it_interval: *t.as_ref(), + it_value: *t.as_ref(), + }), + } + } +} + +impl From for Expiration { + fn from(timerspec: TimerSpec) -> Expiration { + match timerspec { + TimerSpec(libc::itimerspec { + it_interval: + libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }, + it_value: ts, + }) => Expiration::OneShot(ts.into()), + TimerSpec(libc::itimerspec { + it_interval: int_ts, + it_value: val_ts, + }) => { + if (int_ts.tv_sec == val_ts.tv_sec) && (int_ts.tv_nsec == val_ts.tv_nsec) { + Expiration::Interval(int_ts.into()) + } else { + Expiration::IntervalDelayed(val_ts.into(), int_ts.into()) + } + } + } + } +} + +/// An enumeration allowing the definition of the expiration time of an alarm, +/// recurring or not. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Expiration { + OneShot(TimeSpec), + IntervalDelayed(TimeSpec, TimeSpec), + Interval(TimeSpec), +} + +impl TimerFd { + /// Creates a new timer based on the clock defined by `clockid`. The + /// underlying fd can be assigned specific flags with `flags` (CLOEXEC, + /// NONBLOCK). The underlying fd will be closed on drop. + pub fn new(clockid: ClockId, flags: TimerFlags) -> Result { + Errno::result(unsafe { libc::timerfd_create(clockid as i32, flags.bits()) }) + .map(|fd| Self { fd }) + } + + /// Sets a new alarm on the timer. + /// + /// # Types of alarm + /// + /// There are 3 types of alarms you can set: + /// + /// - one shot: the alarm will trigger once after the specified amount of + /// time. + /// Example: I want an alarm to go off in 60s and then disables itself. + /// + /// - interval: the alarm will trigger every specified interval of time. + /// Example: I want an alarm to go off every 60s. The alarm will first + /// go off 60s after I set it and every 60s after that. The alarm will + /// not disable itself. + /// + /// - interval delayed: the alarm will trigger after a certain amount of + /// time and then trigger at a specified interval. + /// Example: I want an alarm to go off every 60s but only start in 1h. + /// The alarm will first trigger 1h after I set it and then every 60s + /// after that. The alarm will not disable itself. + /// + /// # Relative vs absolute alarm + /// + /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass + /// to the `Expiration` you want is relative. If however you want an alarm + /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`. + /// Then the one shot TimeSpec and the delay TimeSpec of the delayed + /// interval are going to be interpreted as absolute. + /// + /// # Disabling alarms + /// + /// Note: Only one alarm can be set for any given timer. Setting a new alarm + /// actually removes the previous one. + /// + /// Note: Setting a one shot alarm with a 0s TimeSpec disables the alarm + /// altogether. + pub fn set(&self, expiration: Expiration, flags: TimerSetTimeFlags) -> Result<()> { + let timerspec: TimerSpec = expiration.into(); + Errno::result(unsafe { + libc::timerfd_settime( + self.fd, + flags.bits(), + timerspec.as_ref(), + std::ptr::null_mut(), + ) + }) + .map(drop) + } + + /// Get the parameters for the alarm currently set, if any. + pub fn get(&self) -> Result> { + let mut timerspec = TimerSpec::none(); + let timerspec_ptr: *mut libc::itimerspec = &mut timerspec.0; + + Errno::result(unsafe { libc::timerfd_gettime(self.fd, timerspec_ptr) }).map(|_| { + if timerspec.0.it_interval.tv_sec == 0 + && timerspec.0.it_interval.tv_nsec == 0 + && timerspec.0.it_value.tv_sec == 0 + && timerspec.0.it_value.tv_nsec == 0 + { + None + } else { + Some(timerspec.into()) + } + }) + } + + /// Remove the alarm if any is set. + pub fn unset(&self) -> Result<()> { + Errno::result(unsafe { + libc::timerfd_settime( + self.fd, + TimerSetTimeFlags::empty().bits(), + TimerSpec::none().as_ref(), + std::ptr::null_mut(), + ) + }) + .map(drop) + } + + /// Wait for the configured alarm to expire. + /// + /// Note: If the alarm is unset, then you will wait forever. + pub fn wait(&self) -> Result<()> { + while let Err(e) = read(self.fd, &mut [0u8; 8]) { + if e != Errno::EINTR { + return Err(e) + } + } + + Ok(()) + } +} + +impl Drop for TimerFd { + fn drop(&mut self) { + if !std::thread::panicking() { + let result = Errno::result(unsafe { + libc::close(self.fd) + }); + if let Err(Errno::EBADF) = result { + panic!("close of TimerFd encountered EBADF"); + } + } + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/uio.rs b/vendor/nix-v0.23.1-patched/src/sys/uio.rs new file mode 100644 index 000000000..878f5cbe2 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/uio.rs @@ -0,0 +1,223 @@ +//! Vectored I/O + +use crate::Result; +use crate::errno::Errno; +use libc::{self, c_int, c_void, size_t, off_t}; +use std::marker::PhantomData; +use std::os::unix::io::RawFd; + +/// Low-level vectored write to a raw file descriptor +/// +/// See also [writev(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/writev.html) +pub fn writev(fd: RawFd, iov: &[IoVec<&[u8]>]) -> Result { + let res = unsafe { libc::writev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) }; + + Errno::result(res).map(|r| r as usize) +} + +/// Low-level vectored read from a raw file descriptor +/// +/// See also [readv(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html) +pub fn readv(fd: RawFd, iov: &mut [IoVec<&mut [u8]>]) -> Result { + let res = unsafe { libc::readv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) }; + + Errno::result(res).map(|r| r as usize) +} + +/// Write to `fd` at `offset` from buffers in `iov`. +/// +/// Buffers in `iov` will be written in order until all buffers have been written +/// or an error occurs. The file offset is not changed. +/// +/// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html) +#[cfg(not(any(target_os = "macos", target_os = "redox")))] +pub fn pwritev(fd: RawFd, iov: &[IoVec<&[u8]>], + offset: off_t) -> Result { + let res = unsafe { + libc::pwritev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int, offset) + }; + + Errno::result(res).map(|r| r as usize) +} + +/// Read from `fd` at `offset` filling buffers in `iov`. +/// +/// Buffers in `iov` will be filled in order until all buffers have been filled, +/// no more bytes are available, or an error occurs. The file offset is not +/// changed. +/// +/// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html) +#[cfg(not(any(target_os = "macos", target_os = "redox")))] +pub fn preadv(fd: RawFd, iov: &[IoVec<&mut [u8]>], + offset: off_t) -> Result { + let res = unsafe { + libc::preadv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int, offset) + }; + + Errno::result(res).map(|r| r as usize) +} + +/// Low-level write to a file, with specified offset. +/// +/// See also [pwrite(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html) +// TODO: move to unistd +pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result { + let res = unsafe { + libc::pwrite(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, + offset) + }; + + Errno::result(res).map(|r| r as usize) +} + +/// Low-level write to a file, with specified offset. +/// +/// See also [pread(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html) +// TODO: move to unistd +pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result{ + let res = unsafe { + libc::pread(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t, + offset) + }; + + Errno::result(res).map(|r| r as usize) +} + +/// A slice of memory in a remote process, starting at address `base` +/// and consisting of `len` bytes. +/// +/// This is the same underlying C structure as [`IoVec`](struct.IoVec.html), +/// except that it refers to memory in some other process, and is +/// therefore not represented in Rust by an actual slice as `IoVec` is. It +/// is used with [`process_vm_readv`](fn.process_vm_readv.html) +/// and [`process_vm_writev`](fn.process_vm_writev.html). +#[cfg(target_os = "linux")] +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct RemoteIoVec { + /// The starting address of this slice (`iov_base`). + pub base: usize, + /// The number of bytes in this slice (`iov_len`). + pub len: usize, +} + +/// Write data directly to another process's virtual memory +/// (see [`process_vm_writev`(2)]). +/// +/// `local_iov` is a list of [`IoVec`]s containing the data to be written, +/// and `remote_iov` is a list of [`RemoteIoVec`]s identifying where the +/// data should be written in the target process. On success, returns the +/// number of bytes written, which will always be a whole +/// number of `remote_iov` chunks. +/// +/// This requires the same permissions as debugging the process using +/// [ptrace]: you must either be a privileged process (with +/// `CAP_SYS_PTRACE`), or you must be running as the same user as the +/// target process and the OS must have unprivileged debugging enabled. +/// +/// This function is only available on Linux. +/// +/// [`process_vm_writev`(2)]: https://man7.org/linux/man-pages/man2/process_vm_writev.2.html +/// [ptrace]: ../ptrace/index.html +/// [`IoVec`]: struct.IoVec.html +/// [`RemoteIoVec`]: struct.RemoteIoVec.html +#[cfg(target_os = "linux")] +pub fn process_vm_writev( + pid: crate::unistd::Pid, + local_iov: &[IoVec<&[u8]>], + remote_iov: &[RemoteIoVec]) -> Result +{ + let res = unsafe { + libc::process_vm_writev(pid.into(), + local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong, + remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0) + }; + + Errno::result(res).map(|r| r as usize) +} + +/// Read data directly from another process's virtual memory +/// (see [`process_vm_readv`(2)]). +/// +/// `local_iov` is a list of [`IoVec`]s containing the buffer to copy +/// data into, and `remote_iov` is a list of [`RemoteIoVec`]s identifying +/// where the source data is in the target process. On success, +/// returns the number of bytes written, which will always be a whole +/// number of `remote_iov` chunks. +/// +/// This requires the same permissions as debugging the process using +/// [`ptrace`]: you must either be a privileged process (with +/// `CAP_SYS_PTRACE`), or you must be running as the same user as the +/// target process and the OS must have unprivileged debugging enabled. +/// +/// This function is only available on Linux. +/// +/// [`process_vm_readv`(2)]: https://man7.org/linux/man-pages/man2/process_vm_readv.2.html +/// [`ptrace`]: ../ptrace/index.html +/// [`IoVec`]: struct.IoVec.html +/// [`RemoteIoVec`]: struct.RemoteIoVec.html +#[cfg(any(target_os = "linux"))] +pub fn process_vm_readv( + pid: crate::unistd::Pid, + local_iov: &[IoVec<&mut [u8]>], + remote_iov: &[RemoteIoVec]) -> Result +{ + let res = unsafe { + libc::process_vm_readv(pid.into(), + local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong, + remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0) + }; + + Errno::result(res).map(|r| r as usize) +} + +/// A vector of buffers. +/// +/// Vectored I/O methods like [`writev`] and [`readv`] use this structure for +/// both reading and writing. Each `IoVec` specifies the base address and +/// length of an area in memory. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct IoVec(pub(crate) libc::iovec, PhantomData); + +impl IoVec { + /// View the `IoVec` as a Rust slice. + #[inline] + pub fn as_slice(&self) -> &[u8] { + use std::slice; + + unsafe { + slice::from_raw_parts( + self.0.iov_base as *const u8, + self.0.iov_len as usize) + } + } +} + +impl<'a> IoVec<&'a [u8]> { + #[cfg(target_os = "freebsd")] + pub(crate) fn from_raw_parts(base: *mut c_void, len: usize) -> Self { + IoVec(libc::iovec { + iov_base: base, + iov_len: len + }, PhantomData) + } + + /// Create an `IoVec` from a Rust slice. + pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> { + IoVec(libc::iovec { + iov_base: buf.as_ptr() as *mut c_void, + iov_len: buf.len() as size_t, + }, PhantomData) + } +} + +impl<'a> IoVec<&'a mut [u8]> { + /// Create an `IoVec` from a mutable Rust slice. + pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> { + IoVec(libc::iovec { + iov_base: buf.as_ptr() as *mut c_void, + iov_len: buf.len() as size_t, + }, PhantomData) + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/utsname.rs b/vendor/nix-v0.23.1-patched/src/sys/utsname.rs new file mode 100644 index 000000000..98edee042 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/utsname.rs @@ -0,0 +1,75 @@ +//! Get system identification +use std::mem; +use libc::{self, c_char}; +use std::ffi::CStr; +use std::str::from_utf8_unchecked; + +/// Describes the running system. Return type of [`uname`]. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(transparent)] +pub struct UtsName(libc::utsname); + +impl UtsName { + /// Name of the operating system implementation + pub fn sysname(&self) -> &str { + to_str(&(&self.0.sysname as *const c_char ) as *const *const c_char) + } + + /// Network name of this machine. + pub fn nodename(&self) -> &str { + to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char) + } + + /// Release level of the operating system. + pub fn release(&self) -> &str { + to_str(&(&self.0.release as *const c_char ) as *const *const c_char) + } + + /// Version level of the operating system. + pub fn version(&self) -> &str { + to_str(&(&self.0.version as *const c_char ) as *const *const c_char) + } + + /// Machine hardware platform. + pub fn machine(&self) -> &str { + to_str(&(&self.0.machine as *const c_char ) as *const *const c_char) + } +} + +/// Get system identification +pub fn uname() -> UtsName { + unsafe { + let mut ret = mem::MaybeUninit::uninit(); + libc::uname(ret.as_mut_ptr()); + UtsName(ret.assume_init()) + } +} + +#[inline] +fn to_str<'a>(s: *const *const c_char) -> &'a str { + unsafe { + let res = CStr::from_ptr(*s).to_bytes(); + from_utf8_unchecked(res) + } +} + +#[cfg(test)] +mod test { + #[cfg(target_os = "linux")] + #[test] + pub fn test_uname_linux() { + assert_eq!(super::uname().sysname(), "Linux"); + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + #[test] + pub fn test_uname_darwin() { + assert_eq!(super::uname().sysname(), "Darwin"); + } + + #[cfg(target_os = "freebsd")] + #[test] + pub fn test_uname_freebsd() { + assert_eq!(super::uname().sysname(), "FreeBSD"); + } +} diff --git a/vendor/nix-v0.23.1-patched/src/sys/wait.rs b/vendor/nix-v0.23.1-patched/src/sys/wait.rs new file mode 100644 index 000000000..ee49e37de --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/sys/wait.rs @@ -0,0 +1,262 @@ +//! Wait for a process to change status +use crate::errno::Errno; +use crate::sys::signal::Signal; +use crate::unistd::Pid; +use crate::Result; +use cfg_if::cfg_if; +use libc::{self, c_int}; +use std::convert::TryFrom; + +libc_bitflags!( + /// Controls the behavior of [`waitpid`]. + pub struct WaitPidFlag: c_int { + /// Do not block when there are no processes wishing to report status. + WNOHANG; + /// Report the status of selected processes which are stopped due to a + /// [`SIGTTIN`](crate::sys::signal::Signal::SIGTTIN), + /// [`SIGTTOU`](crate::sys::signal::Signal::SIGTTOU), + /// [`SIGTSTP`](crate::sys::signal::Signal::SIGTSTP), or + /// [`SIGSTOP`](crate::sys::signal::Signal::SIGSTOP) signal. + WUNTRACED; + /// Report the status of selected processes which have terminated. + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "redox", + target_os = "macos", + target_os = "netbsd"))] + WEXITED; + /// Report the status of selected processes that have continued from a + /// job control stop by receiving a + /// [`SIGCONT`](crate::sys::signal::Signal::SIGCONT) signal. + WCONTINUED; + /// An alias for WUNTRACED. + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "redox", + target_os = "macos", + target_os = "netbsd"))] + WSTOPPED; + /// Don't reap, just poll status. + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "redox", + target_os = "macos", + target_os = "netbsd"))] + WNOWAIT; + /// Don't wait on children of other threads in this group + #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))] + __WNOTHREAD; + /// Wait on all children, regardless of type + #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))] + __WALL; + /// Wait for "clone" children only. + #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))] + __WCLONE; + } +); + +/// Possible return values from `wait()` or `waitpid()`. +/// +/// Each status (other than `StillAlive`) describes a state transition +/// in a child process `Pid`, such as the process exiting or stopping, +/// plus additional data about the transition if any. +/// +/// Note that there are two Linux-specific enum variants, `PtraceEvent` +/// and `PtraceSyscall`. Portable code should avoid exhaustively +/// matching on `WaitStatus`. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum WaitStatus { + /// The process exited normally (as with `exit()` or returning from + /// `main`) with the given exit code. This case matches the C macro + /// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`. + Exited(Pid, i32), + /// The process was killed by the given signal. The third field + /// indicates whether the signal generated a core dump. This case + /// matches the C macro `WIFSIGNALED(status)`; the last two fields + /// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`. + Signaled(Pid, Signal, bool), + /// The process is alive, but was stopped by the given signal. This + /// is only reported if `WaitPidFlag::WUNTRACED` was passed. This + /// case matches the C macro `WIFSTOPPED(status)`; the second field + /// is `WSTOPSIG(status)`. + Stopped(Pid, Signal), + /// The traced process was stopped by a `PTRACE_EVENT_*` event. See + /// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All + /// currently-defined events use `SIGTRAP` as the signal; the third + /// field is the `PTRACE_EVENT_*` value of the event. + /// + /// [`nix::sys::ptrace`]: ../ptrace/index.html + /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html + #[cfg(any(target_os = "linux", target_os = "android"))] + PtraceEvent(Pid, Signal, c_int), + /// The traced process was stopped by execution of a system call, + /// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for + /// more information. + /// + /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html + #[cfg(any(target_os = "linux", target_os = "android"))] + PtraceSyscall(Pid), + /// The process was previously stopped but has resumed execution + /// after receiving a `SIGCONT` signal. This is only reported if + /// `WaitPidFlag::WCONTINUED` was passed. This case matches the C + /// macro `WIFCONTINUED(status)`. + Continued(Pid), + /// There are currently no state changes to report in any awaited + /// child process. This is only returned if `WaitPidFlag::WNOHANG` + /// was used (otherwise `wait()` or `waitpid()` would block until + /// there was something to report). + StillAlive, +} + +impl WaitStatus { + /// Extracts the PID from the WaitStatus unless it equals StillAlive. + pub fn pid(&self) -> Option { + use self::WaitStatus::*; + match *self { + Exited(p, _) | Signaled(p, _, _) | Stopped(p, _) | Continued(p) => Some(p), + StillAlive => None, + #[cfg(any(target_os = "android", target_os = "linux"))] + PtraceEvent(p, _, _) | PtraceSyscall(p) => Some(p), + } + } +} + +fn exited(status: i32) -> bool { + libc::WIFEXITED(status) +} + +fn exit_status(status: i32) -> i32 { + libc::WEXITSTATUS(status) +} + +fn signaled(status: i32) -> bool { + libc::WIFSIGNALED(status) +} + +fn term_signal(status: i32) -> Result { + Signal::try_from(libc::WTERMSIG(status)) +} + +fn dumped_core(status: i32) -> bool { + libc::WCOREDUMP(status) +} + +fn stopped(status: i32) -> bool { + libc::WIFSTOPPED(status) +} + +fn stop_signal(status: i32) -> Result { + Signal::try_from(libc::WSTOPSIG(status)) +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +fn syscall_stop(status: i32) -> bool { + // From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect + // of delivering SIGTRAP | 0x80 as the signal number for syscall + // stops. This allows easily distinguishing syscall stops from + // genuine SIGTRAP signals. + libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80 +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +fn stop_additional(status: i32) -> c_int { + (status >> 16) as c_int +} + +fn continued(status: i32) -> bool { + libc::WIFCONTINUED(status) +} + +impl WaitStatus { + /// Convert a raw `wstatus` as returned by `waitpid`/`wait` into a `WaitStatus` + /// + /// # Errors + /// + /// Returns an `Error` corresponding to `EINVAL` for invalid status values. + /// + /// # Examples + /// + /// Convert a `wstatus` obtained from `libc::waitpid` into a `WaitStatus`: + /// + /// ``` + /// use nix::sys::wait::WaitStatus; + /// use nix::sys::signal::Signal; + /// let pid = nix::unistd::Pid::from_raw(1); + /// let status = WaitStatus::from_raw(pid, 0x0002); + /// assert_eq!(status, Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false))); + /// ``` + pub fn from_raw(pid: Pid, status: i32) -> Result { + Ok(if exited(status) { + WaitStatus::Exited(pid, exit_status(status)) + } else if signaled(status) { + WaitStatus::Signaled(pid, term_signal(status)?, dumped_core(status)) + } else if stopped(status) { + cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + fn decode_stopped(pid: Pid, status: i32) -> Result { + let status_additional = stop_additional(status); + Ok(if syscall_stop(status) { + WaitStatus::PtraceSyscall(pid) + } else if status_additional == 0 { + WaitStatus::Stopped(pid, stop_signal(status)?) + } else { + WaitStatus::PtraceEvent(pid, stop_signal(status)?, + stop_additional(status)) + }) + } + } else { + fn decode_stopped(pid: Pid, status: i32) -> Result { + Ok(WaitStatus::Stopped(pid, stop_signal(status)?)) + } + } + } + return decode_stopped(pid, status); + } else { + assert!(continued(status)); + WaitStatus::Continued(pid) + }) + } +} + +/// Wait for a process to change status +/// +/// See also [waitpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html) +pub fn waitpid>>(pid: P, options: Option) -> Result { + use self::WaitStatus::*; + + let mut status: i32 = 0; + + let option_bits = match options { + Some(bits) => bits.bits(), + None => 0, + }; + + let res = unsafe { + libc::waitpid( + pid.into().unwrap_or_else(|| Pid::from_raw(-1)).into(), + &mut status as *mut c_int, + option_bits, + ) + }; + + match Errno::result(res)? { + 0 => Ok(StillAlive), + res => WaitStatus::from_raw(Pid::from_raw(res), status), + } +} + +/// Wait for any child process to change status or a signal is received. +/// +/// See also [wait(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html) +pub fn wait() -> Result { + waitpid(None, None) +} diff --git a/vendor/nix-v0.23.1-patched/src/time.rs b/vendor/nix-v0.23.1-patched/src/time.rs new file mode 100644 index 000000000..6275b59c7 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/time.rs @@ -0,0 +1,260 @@ +use crate::sys::time::TimeSpec; +#[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux", + target_os = "android", + target_os = "emscripten", +))] +use crate::unistd::Pid; +use crate::{Errno, Result}; +use libc::{self, clockid_t}; +use std::mem::MaybeUninit; + +/// Clock identifier +/// +/// Newtype pattern around `clockid_t` (which is just alias). It pervents bugs caused by +/// accidentally passing wrong value. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct ClockId(clockid_t); + +impl ClockId { + /// Creates `ClockId` from raw `clockid_t` + pub const fn from_raw(clk_id: clockid_t) -> Self { + ClockId(clk_id) + } + + /// Returns `ClockId` of a `pid` CPU-time clock + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux", + target_os = "android", + target_os = "emscripten", + ))] + pub fn pid_cpu_clock_id(pid: Pid) -> Result { + clock_getcpuclockid(pid) + } + + /// Returns resolution of the clock id + #[cfg(not(target_os = "redox"))] + pub fn res(self) -> Result { + clock_getres(self) + } + + /// Returns the current time on the clock id + pub fn now(self) -> Result { + clock_gettime(self) + } + + /// Sets time to `timespec` on the clock id + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + all( + not(any(target_env = "uclibc", target_env = "newlibc")), + any(target_os = "redox", target_os = "hermit",), + ), + )))] + pub fn set_time(self, timespec: TimeSpec) -> Result<()> { + clock_settime(self, timespec) + } + + /// Gets the raw `clockid_t` wrapped by `self` + pub const fn as_raw(self) -> clockid_t { + self.0 + } + + #[cfg(any( + target_os = "fuchsia", + all( + not(any(target_env = "uclibc", target_env = "newlib")), + any(target_os = "linux", target_os = "android", target_os = "emscripten"), + ) + ))] + pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME); + #[cfg(any( + target_os = "fuchsia", + all( + not(any(target_env = "uclibc", target_env = "newlib")), + any(target_os = "linux", target_os = "android", target_os = "emscripten") + ) + ))] + pub const CLOCK_BOOTTIME_ALARM: ClockId = ClockId(libc::CLOCK_BOOTTIME_ALARM); + pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC); + #[cfg(any( + target_os = "fuchsia", + all( + not(any(target_env = "uclibc", target_env = "newlib")), + any(target_os = "linux", target_os = "android", target_os = "emscripten") + ) + ))] + pub const CLOCK_MONOTONIC_COARSE: ClockId = ClockId(libc::CLOCK_MONOTONIC_COARSE); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + pub const CLOCK_MONOTONIC_FAST: ClockId = ClockId(libc::CLOCK_MONOTONIC_FAST); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + pub const CLOCK_MONOTONIC_PRECISE: ClockId = ClockId(libc::CLOCK_MONOTONIC_PRECISE); + #[cfg(any( + target_os = "fuchsia", + all( + not(any(target_env = "uclibc", target_env = "newlib")), + any(target_os = "linux", target_os = "android", target_os = "emscripten") + ) + ))] + pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW); + #[cfg(any( + target_os = "fuchsia", + target_env = "uclibc", + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly", + all( + not(target_env = "newlib"), + any(target_os = "linux", target_os = "android", target_os = "emscripten") + ) + ))] + pub const CLOCK_PROCESS_CPUTIME_ID: ClockId = ClockId(libc::CLOCK_PROCESS_CPUTIME_ID); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF); + pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME); + #[cfg(any( + target_os = "fuchsia", + all( + not(any(target_env = "uclibc", target_env = "newlib")), + any(target_os = "linux", target_os = "android", target_os = "emscripten") + ) + ))] + pub const CLOCK_REALTIME_ALARM: ClockId = ClockId(libc::CLOCK_REALTIME_ALARM); + #[cfg(any( + target_os = "fuchsia", + all( + not(any(target_env = "uclibc", target_env = "newlib")), + any(target_os = "linux", target_os = "android", target_os = "emscripten") + ) + ))] + pub const CLOCK_REALTIME_COARSE: ClockId = ClockId(libc::CLOCK_REALTIME_COARSE); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + pub const CLOCK_REALTIME_PRECISE: ClockId = ClockId(libc::CLOCK_REALTIME_PRECISE); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND); + #[cfg(any( + target_os = "fuchsia", + all( + not(any(target_env = "uclibc", target_env = "newlib")), + any( + target_os = "emscripten", + all(target_os = "linux", target_env = "musl") + ) + ) + ))] + pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE); + #[cfg(any( + target_os = "fuchsia", + all( + not(any(target_env = "uclibc", target_env = "newlib")), + any( + target_os = "emscripten", + all(target_os = "linux", target_env = "musl") + ) + ) + ))] + pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI); + #[cfg(any( + target_env = "uclibc", + target_os = "fuchsia", + target_os = "ios", + target_os = "macos", + target_os = "freebsd", + target_os = "dragonfly", + all( + not(target_env = "newlib"), + any(target_os = "linux", target_os = "android", target_os = "emscripten",), + ), + ))] + pub const CLOCK_THREAD_CPUTIME_ID: ClockId = ClockId(libc::CLOCK_THREAD_CPUTIME_ID); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + pub const CLOCK_UPTIME_PRECISE: ClockId = ClockId(libc::CLOCK_UPTIME_PRECISE); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL); +} + +impl From for clockid_t { + fn from(clock_id: ClockId) -> Self { + clock_id.as_raw() + } +} + +impl From for ClockId { + fn from(clk_id: clockid_t) -> Self { + ClockId::from_raw(clk_id) + } +} + +impl std::fmt::Display for ClockId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } +} + +/// Get the resolution of the specified clock, (see +/// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)). +#[cfg(not(target_os = "redox"))] +pub fn clock_getres(clock_id: ClockId) -> Result { + let mut c_time: MaybeUninit = MaybeUninit::uninit(); + let ret = unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) }; + Errno::result(ret)?; + let res = unsafe { c_time.assume_init() }; + Ok(TimeSpec::from(res)) +} + +/// Get the time of the specified clock, (see +/// [clock_gettime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_gettime.html)). +pub fn clock_gettime(clock_id: ClockId) -> Result { + let mut c_time: MaybeUninit = MaybeUninit::uninit(); + let ret = unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) }; + Errno::result(ret)?; + let res = unsafe { c_time.assume_init() }; + Ok(TimeSpec::from(res)) +} + +/// Set the time of the specified clock, (see +/// [clock_settime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_settime.html)). +#[cfg(not(any( + target_os = "macos", + target_os = "ios", + all( + not(any(target_env = "uclibc", target_env = "newlibc")), + any(target_os = "redox", target_os = "hermit",), + ), +)))] +pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> { + let ret = unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) }; + Errno::result(ret).map(drop) +} + +/// Get the clock id of the specified process id, (see +/// [clock_getcpuclockid(3)](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_getcpuclockid.html)). +#[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux", + target_os = "android", + target_os = "emscripten", +))] +pub fn clock_getcpuclockid(pid: Pid) -> Result { + let mut clk_id: MaybeUninit = MaybeUninit::uninit(); + let ret = unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) }; + if ret == 0 { + let res = unsafe { clk_id.assume_init() }; + Ok(ClockId::from(res)) + } else { + Err(Errno::from_i32(ret)) + } +} diff --git a/vendor/nix-v0.23.1-patched/src/ucontext.rs b/vendor/nix-v0.23.1-patched/src/ucontext.rs new file mode 100644 index 000000000..f2338bd42 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/ucontext.rs @@ -0,0 +1,43 @@ +#[cfg(not(target_env = "musl"))] +use crate::Result; +#[cfg(not(target_env = "musl"))] +use crate::errno::Errno; +#[cfg(not(target_env = "musl"))] +use std::mem; +use crate::sys::signal::SigSet; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct UContext { + context: libc::ucontext_t, +} + +impl UContext { + #[cfg(not(target_env = "musl"))] + pub fn get() -> Result { + let mut context = mem::MaybeUninit::::uninit(); + let res = unsafe { libc::getcontext(context.as_mut_ptr()) }; + Errno::result(res).map(|_| unsafe { + UContext { context: context.assume_init()} + }) + } + + #[cfg(not(target_env = "musl"))] + pub fn set(&self) -> Result<()> { + let res = unsafe { + libc::setcontext(&self.context as *const libc::ucontext_t) + }; + Errno::result(res).map(drop) + } + + pub fn sigmask_mut(&mut self) -> &mut SigSet { + unsafe { + &mut *(&mut self.context.uc_sigmask as *mut libc::sigset_t as *mut SigSet) + } + } + + pub fn sigmask(&self) -> &SigSet { + unsafe { + &*(&self.context.uc_sigmask as *const libc::sigset_t as *const SigSet) + } + } +} diff --git a/vendor/nix-v0.23.1-patched/src/unistd.rs b/vendor/nix-v0.23.1-patched/src/unistd.rs new file mode 100644 index 000000000..a9862d37a --- /dev/null +++ b/vendor/nix-v0.23.1-patched/src/unistd.rs @@ -0,0 +1,2994 @@ +//! Safe wrappers around functions found in libc "unistd.h" header + +#[cfg(not(target_os = "redox"))] +use cfg_if::cfg_if; +use crate::errno::{self, Errno}; +use crate::{Error, Result, NixPath}; +#[cfg(not(target_os = "redox"))] +use crate::fcntl::{AtFlags, at_rawfd}; +use crate::fcntl::{FdFlag, OFlag, fcntl}; +use crate::fcntl::FcntlArg::F_SETFD; +use libc::{self, c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t, + uid_t, gid_t, mode_t, PATH_MAX}; +use std::{fmt, mem, ptr}; +use std::convert::Infallible; +use std::ffi::{CStr, OsString}; +#[cfg(not(target_os = "redox"))] +use std::ffi::{CString, OsStr}; +use std::os::unix::ffi::OsStringExt; +#[cfg(not(target_os = "redox"))] +use std::os::unix::ffi::OsStrExt; +use std::os::unix::io::RawFd; +use std::path::PathBuf; +use crate::sys::stat::Mode; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::pivot_root::*; + +#[cfg(any(target_os = "android", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] +pub use self::setres::*; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::getres::*; + +/// User identifier +/// +/// Newtype pattern around `uid_t` (which is just alias). It prevents bugs caused by accidentally +/// passing wrong value. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Uid(uid_t); + +impl Uid { + /// Creates `Uid` from raw `uid_t`. + pub const fn from_raw(uid: uid_t) -> Self { + Uid(uid) + } + + /// Returns Uid of calling process. This is practically a more Rusty alias for `getuid`. + pub fn current() -> Self { + getuid() + } + + /// Returns effective Uid of calling process. This is practically a more Rusty alias for `geteuid`. + pub fn effective() -> Self { + geteuid() + } + + /// Returns true if the `Uid` represents privileged user - root. (If it equals zero.) + pub const fn is_root(self) -> bool { + self.0 == ROOT.0 + } + + /// Get the raw `uid_t` wrapped by `self`. + pub const fn as_raw(self) -> uid_t { + self.0 + } +} + +impl From for uid_t { + fn from(uid: Uid) -> Self { + uid.0 + } +} + +impl fmt::Display for Uid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +/// Constant for UID = 0 +pub const ROOT: Uid = Uid(0); + +/// Group identifier +/// +/// Newtype pattern around `gid_t` (which is just alias). It prevents bugs caused by accidentally +/// passing wrong value. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Gid(gid_t); + +impl Gid { + /// Creates `Gid` from raw `gid_t`. + pub const fn from_raw(gid: gid_t) -> Self { + Gid(gid) + } + + /// Returns Gid of calling process. This is practically a more Rusty alias for `getgid`. + pub fn current() -> Self { + getgid() + } + + /// Returns effective Gid of calling process. This is practically a more Rusty alias for `getegid`. + pub fn effective() -> Self { + getegid() + } + + /// Get the raw `gid_t` wrapped by `self`. + pub const fn as_raw(self) -> gid_t { + self.0 + } +} + +impl From for gid_t { + fn from(gid: Gid) -> Self { + gid.0 + } +} + +impl fmt::Display for Gid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +/// Process identifier +/// +/// Newtype pattern around `pid_t` (which is just alias). It prevents bugs caused by accidentally +/// passing wrong value. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct Pid(pid_t); + +impl Pid { + /// Creates `Pid` from raw `pid_t`. + pub const fn from_raw(pid: pid_t) -> Self { + Pid(pid) + } + + /// Returns PID of calling process + pub fn this() -> Self { + getpid() + } + + /// Returns PID of parent of calling process + pub fn parent() -> Self { + getppid() + } + + /// Get the raw `pid_t` wrapped by `self`. + pub const fn as_raw(self) -> pid_t { + self.0 + } +} + +impl From for pid_t { + fn from(pid: Pid) -> Self { + pid.0 + } +} + +impl fmt::Display for Pid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + + +/// Represents the successful result of calling `fork` +/// +/// When `fork` is called, the process continues execution in the parent process +/// and in the new child. This return type can be examined to determine whether +/// you are now executing in the parent process or in the child. +#[derive(Clone, Copy, Debug)] +pub enum ForkResult { + Parent { child: Pid }, + Child, +} + +impl ForkResult { + + /// Return `true` if this is the child process of the `fork()` + #[inline] + pub fn is_child(self) -> bool { + matches!(self, ForkResult::Child) + } + + /// Returns `true` if this is the parent process of the `fork()` + #[inline] + pub fn is_parent(self) -> bool { + !self.is_child() + } +} + +/// Create a new child process duplicating the parent process ([see +/// fork(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)). +/// +/// After calling the fork system call (successfully) two processes will +/// be created that are identical with the exception of their pid and the +/// return value of this function. As an example: +/// +/// ``` +/// use nix::{sys::wait::waitpid,unistd::{fork, ForkResult, write}}; +/// +/// match unsafe{fork()} { +/// Ok(ForkResult::Parent { child, .. }) => { +/// println!("Continuing execution in parent process, new child has pid: {}", child); +/// waitpid(child, None).unwrap(); +/// } +/// Ok(ForkResult::Child) => { +/// // Unsafe to use `println!` (or `unwrap`) here. See Safety. +/// write(libc::STDOUT_FILENO, "I'm a new child process\n".as_bytes()).ok(); +/// unsafe { libc::_exit(0) }; +/// } +/// Err(_) => println!("Fork failed"), +/// } +/// ``` +/// +/// This will print something like the following (order indeterministic). The +/// thing to note is that you end up with two processes continuing execution +/// immediately after the fork call but with different match arms. +/// +/// ```text +/// Continuing execution in parent process, new child has pid: 1234 +/// I'm a new child process +/// ``` +/// +/// # Safety +/// +/// In a multithreaded program, only [async-signal-safe] functions like `pause` +/// and `_exit` may be called by the child (the parent isn't restricted). Note +/// that memory allocation may **not** be async-signal-safe and thus must be +/// prevented. +/// +/// Those functions are only a small subset of your operating system's API, so +/// special care must be taken to only invoke code you can control and audit. +/// +/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html +#[inline] +pub unsafe fn fork() -> Result { + use self::ForkResult::*; + let res = libc::fork(); + + Errno::result(res).map(|res| match res { + 0 => Child, + res => Parent { child: Pid(res) }, + }) +} + +/// Get the pid of this process (see +/// [getpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpid.html)). +/// +/// Since you are running code, there is always a pid to return, so there +/// is no error case that needs to be handled. +#[inline] +pub fn getpid() -> Pid { + Pid(unsafe { libc::getpid() }) +} + +/// Get the pid of this processes' parent (see +/// [getpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getppid.html)). +/// +/// There is always a parent pid to return, so there is no error case that needs +/// to be handled. +#[inline] +pub fn getppid() -> Pid { + Pid(unsafe { libc::getppid() }) // no error handling, according to man page: "These functions are always successful." +} + +/// Set a process group ID (see +/// [setpgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html)). +/// +/// Set the process group id (PGID) of a particular process. If a pid of zero +/// is specified, then the pid of the calling process is used. Process groups +/// may be used to group together a set of processes in order for the OS to +/// apply some operations across the group. +/// +/// `setsid()` may be used to create a new process group. +#[inline] +pub fn setpgid(pid: Pid, pgid: Pid) -> Result<()> { + let res = unsafe { libc::setpgid(pid.into(), pgid.into()) }; + Errno::result(res).map(drop) +} +#[inline] +pub fn getpgid(pid: Option) -> Result { + let res = unsafe { libc::getpgid(pid.unwrap_or(Pid(0)).into()) }; + Errno::result(res).map(Pid) +} + +/// Create new session and set process group id (see +/// [setsid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsid.html)). +#[inline] +pub fn setsid() -> Result { + Errno::result(unsafe { libc::setsid() }).map(Pid) +} + +/// Get the process group ID of a session leader +/// [getsid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsid.html). +/// +/// Obtain the process group ID of the process that is the session leader of the process specified +/// by pid. If pid is zero, it specifies the calling process. +#[inline] +#[cfg(not(target_os = "redox"))] +pub fn getsid(pid: Option) -> Result { + let res = unsafe { libc::getsid(pid.unwrap_or(Pid(0)).into()) }; + Errno::result(res).map(Pid) +} + + +/// Get the terminal foreground process group (see +/// [tcgetpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetpgrp.html)). +/// +/// Get the group process id (GPID) of the foreground process group on the +/// terminal associated to file descriptor (FD). +#[inline] +pub fn tcgetpgrp(fd: c_int) -> Result { + let res = unsafe { libc::tcgetpgrp(fd) }; + Errno::result(res).map(Pid) +} +/// Set the terminal foreground process group (see +/// [tcgetpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetpgrp.html)). +/// +/// Get the group process id (PGID) to the foreground process group on the +/// terminal associated to file descriptor (FD). +#[inline] +pub fn tcsetpgrp(fd: c_int, pgrp: Pid) -> Result<()> { + let res = unsafe { libc::tcsetpgrp(fd, pgrp.into()) }; + Errno::result(res).map(drop) +} + + +/// Get the group id of the calling process (see +///[getpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html)). +/// +/// Get the process group id (PGID) of the calling process. +/// According to the man page it is always successful. +#[inline] +pub fn getpgrp() -> Pid { + Pid(unsafe { libc::getpgrp() }) +} + +/// Get the caller's thread ID (see +/// [gettid(2)](https://man7.org/linux/man-pages/man2/gettid.2.html). +/// +/// This function is only available on Linux based systems. In a single +/// threaded process, the main thread will have the same ID as the process. In +/// a multithreaded process, each thread will have a unique thread id but the +/// same process ID. +/// +/// No error handling is required as a thread id should always exist for any +/// process, even if threads are not being used. +#[cfg(any(target_os = "linux", target_os = "android"))] +#[inline] +pub fn gettid() -> Pid { + Pid(unsafe { libc::syscall(libc::SYS_gettid) as pid_t }) +} + +/// Create a copy of the specified file descriptor (see +/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)). +/// +/// The new file descriptor will be have a new index but refer to the same +/// resource as the old file descriptor and the old and new file descriptors may +/// be used interchangeably. The new and old file descriptor share the same +/// underlying resource, offset, and file status flags. The actual index used +/// for the file descriptor will be the lowest fd index that is available. +/// +/// The two file descriptors do not share file descriptor flags (e.g. `OFlag::FD_CLOEXEC`). +#[inline] +pub fn dup(oldfd: RawFd) -> Result { + let res = unsafe { libc::dup(oldfd) }; + + Errno::result(res) +} + +/// Create a copy of the specified file descriptor using the specified fd (see +/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)). +/// +/// This function behaves similar to `dup()` except that it will try to use the +/// specified fd instead of allocating a new one. See the man pages for more +/// detail on the exact behavior of this function. +#[inline] +pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result { + let res = unsafe { libc::dup2(oldfd, newfd) }; + + Errno::result(res) +} + +/// Create a new copy of the specified file descriptor using the specified fd +/// and flags (see [dup(2)](https://man7.org/linux/man-pages/man2/dup.2.html)). +/// +/// This function behaves similar to `dup2()` but allows for flags to be +/// specified. +pub fn dup3(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result { + dup3_polyfill(oldfd, newfd, flags) +} + +#[inline] +fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result { + if oldfd == newfd { + return Err(Errno::EINVAL); + } + + let fd = dup2(oldfd, newfd)?; + + if flags.contains(OFlag::O_CLOEXEC) { + if let Err(e) = fcntl(fd, F_SETFD(FdFlag::FD_CLOEXEC)) { + let _ = close(fd); + return Err(e); + } + } + + Ok(fd) +} + +/// Change the current working directory of the calling process (see +/// [chdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html)). +/// +/// This function may fail in a number of different scenarios. See the man +/// pages for additional details on possible failure cases. +#[inline] +pub fn chdir(path: &P) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::chdir(cstr.as_ptr()) } + })?; + + Errno::result(res).map(drop) +} + +/// Change the current working directory of the process to the one +/// given as an open file descriptor (see +/// [fchdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html)). +/// +/// This function may fail in a number of different scenarios. See the man +/// pages for additional details on possible failure cases. +#[inline] +#[cfg(not(target_os = "fuchsia"))] +pub fn fchdir(dirfd: RawFd) -> Result<()> { + let res = unsafe { libc::fchdir(dirfd) }; + + Errno::result(res).map(drop) +} + +/// Creates new directory `path` with access rights `mode`. (see [mkdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html)) +/// +/// # Errors +/// +/// There are several situations where mkdir might fail: +/// +/// - current user has insufficient rights in the parent directory +/// - the path already exists +/// - the path name is too long (longer than `PATH_MAX`, usually 4096 on linux, 1024 on OS X) +/// +/// # Example +/// +/// ```rust +/// use nix::unistd; +/// use nix::sys::stat; +/// use tempfile::tempdir; +/// +/// let tmp_dir1 = tempdir().unwrap(); +/// let tmp_dir2 = tmp_dir1.path().join("new_dir"); +/// +/// // create new directory and give read, write and execute rights to the owner +/// match unistd::mkdir(&tmp_dir2, stat::Mode::S_IRWXU) { +/// Ok(_) => println!("created {:?}", tmp_dir2), +/// Err(err) => println!("Error creating directory: {}", err), +/// } +/// ``` +#[inline] +pub fn mkdir(path: &P, mode: Mode) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::mkdir(cstr.as_ptr(), mode.bits() as mode_t) } + })?; + + Errno::result(res).map(drop) +} + +/// Creates new fifo special file (named pipe) with path `path` and access rights `mode`. +/// +/// # Errors +/// +/// There are several situations where mkfifo might fail: +/// +/// - current user has insufficient rights in the parent directory +/// - the path already exists +/// - the path name is too long (longer than `PATH_MAX`, usually 4096 on linux, 1024 on OS X) +/// +/// For a full list consult +/// [posix specification](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html) +/// +/// # Example +/// +/// ```rust +/// use nix::unistd; +/// use nix::sys::stat; +/// use tempfile::tempdir; +/// +/// let tmp_dir = tempdir().unwrap(); +/// let fifo_path = tmp_dir.path().join("foo.pipe"); +/// +/// // create new fifo and give read, write and execute rights to the owner +/// match unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) { +/// Ok(_) => println!("created {:?}", fifo_path), +/// Err(err) => println!("Error creating fifo: {}", err), +/// } +/// ``` +#[inline] +#[cfg(not(target_os = "redox"))] // RedoxFS does not support fifo yet +pub fn mkfifo(path: &P, mode: Mode) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::mkfifo(cstr.as_ptr(), mode.bits() as mode_t) } + })?; + + Errno::result(res).map(drop) +} + +/// Creates new fifo special file (named pipe) with path `path` and access rights `mode`. +/// +/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor. +/// +/// If `dirfd` is `None`, then `path` is relative to the current working directory. +/// +/// # References +/// +/// [mkfifoat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifoat.html). +// mkfifoat is not implemented in OSX or android +#[inline] +#[cfg(not(any( + target_os = "macos", target_os = "ios", + target_os = "android", target_os = "redox")))] +pub fn mkfifoat(dirfd: Option, path: &P, mode: Mode) -> Result<()> { + let res = path.with_nix_path(|cstr| unsafe { + libc::mkfifoat(at_rawfd(dirfd), cstr.as_ptr(), mode.bits() as mode_t) + })?; + + Errno::result(res).map(drop) +} + +/// Creates a symbolic link at `path2` which points to `path1`. +/// +/// If `dirfd` has a value, then `path2` is relative to directory associated +/// with the file descriptor. +/// +/// If `dirfd` is `None`, then `path2` is relative to the current working +/// directory. This is identical to `libc::symlink(path1, path2)`. +/// +/// See also [symlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html). +#[cfg(not(target_os = "redox"))] +pub fn symlinkat( + path1: &P1, + dirfd: Option, + path2: &P2) -> Result<()> { + let res = + path1.with_nix_path(|path1| { + path2.with_nix_path(|path2| { + unsafe { + libc::symlinkat( + path1.as_ptr(), + dirfd.unwrap_or(libc::AT_FDCWD), + path2.as_ptr() + ) + } + }) + })??; + Errno::result(res).map(drop) +} + +// Double the buffer capacity up to limit. In case it already has +// reached the limit, return Errno::ERANGE. +fn reserve_double_buffer_size(buf: &mut Vec, limit: usize) -> Result<()> { + use std::cmp::min; + + if buf.capacity() >= limit { + return Err(Errno::ERANGE) + } + + let capacity = min(buf.capacity() * 2, limit); + buf.reserve(capacity); + + Ok(()) +} + +/// Returns the current directory as a `PathBuf` +/// +/// Err is returned if the current user doesn't have the permission to read or search a component +/// of the current path. +/// +/// # Example +/// +/// ```rust +/// use nix::unistd; +/// +/// // assume that we are allowed to get current directory +/// let dir = unistd::getcwd().unwrap(); +/// println!("The current directory is {:?}", dir); +/// ``` +#[inline] +pub fn getcwd() -> Result { + let mut buf = Vec::with_capacity(512); + loop { + unsafe { + let ptr = buf.as_mut_ptr() as *mut c_char; + + // The buffer must be large enough to store the absolute pathname plus + // a terminating null byte, or else null is returned. + // To safely handle this we start with a reasonable size (512 bytes) + // and double the buffer size upon every error + if !libc::getcwd(ptr, buf.capacity()).is_null() { + let len = CStr::from_ptr(buf.as_ptr() as *const c_char).to_bytes().len(); + buf.set_len(len); + buf.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(buf))); + } else { + let error = Errno::last(); + // ERANGE means buffer was too small to store directory name + if error != Errno::ERANGE { + return Err(error); + } + } + + // Trigger the internal buffer resizing logic. + reserve_double_buffer_size(&mut buf, PATH_MAX as usize)?; + } + } +} + +/// Computes the raw UID and GID values to pass to a `*chown` call. +// The cast is not unnecessary on all platforms. +#[allow(clippy::unnecessary_cast)] +fn chown_raw_ids(owner: Option, group: Option) -> (libc::uid_t, libc::gid_t) { + // According to the POSIX specification, -1 is used to indicate that owner and group + // are not to be changed. Since uid_t and gid_t are unsigned types, we have to wrap + // around to get -1. + let uid = owner.map(Into::into) + .unwrap_or_else(|| (0 as uid_t).wrapping_sub(1)); + let gid = group.map(Into::into) + .unwrap_or_else(|| (0 as gid_t).wrapping_sub(1)); + (uid, gid) +} + +/// Change the ownership of the file at `path` to be owned by the specified +/// `owner` (user) and `group` (see +/// [chown(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html)). +/// +/// The owner/group for the provided path name will not be modified if `None` is +/// provided for that argument. Ownership change will be attempted for the path +/// only if `Some` owner/group is provided. +#[inline] +pub fn chown(path: &P, owner: Option, group: Option) -> Result<()> { + let res = path.with_nix_path(|cstr| { + let (uid, gid) = chown_raw_ids(owner, group); + unsafe { libc::chown(cstr.as_ptr(), uid, gid) } + })?; + + Errno::result(res).map(drop) +} + +/// Change the ownership of the file referred to by the open file descriptor `fd` to be owned by +/// the specified `owner` (user) and `group` (see +/// [fchown(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html)). +/// +/// The owner/group for the provided file will not be modified if `None` is +/// provided for that argument. Ownership change will be attempted for the path +/// only if `Some` owner/group is provided. +#[inline] +pub fn fchown(fd: RawFd, owner: Option, group: Option) -> Result<()> { + let (uid, gid) = chown_raw_ids(owner, group); + let res = unsafe { libc::fchown(fd, uid, gid) }; + Errno::result(res).map(drop) +} + +/// Flags for `fchownat` function. +#[derive(Clone, Copy, Debug)] +pub enum FchownatFlags { + FollowSymlink, + NoFollowSymlink, +} + +/// Change the ownership of the file at `path` to be owned by the specified +/// `owner` (user) and `group`. +/// +/// The owner/group for the provided path name will not be modified if `None` is +/// provided for that argument. Ownership change will be attempted for the path +/// only if `Some` owner/group is provided. +/// +/// The file to be changed is determined relative to the directory associated +/// with the file descriptor `dirfd` or the current working directory +/// if `dirfd` is `None`. +/// +/// If `flag` is `FchownatFlags::NoFollowSymlink` and `path` names a symbolic link, +/// then the mode of the symbolic link is changed. +/// +/// `fchownat(None, path, mode, FchownatFlags::NoFollowSymlink)` is identical to +/// a call `libc::lchown(path, mode)`. That's why `lchmod` is unimplemented in +/// the `nix` crate. +/// +/// # References +/// +/// [fchownat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html). +#[cfg(not(target_os = "redox"))] +pub fn fchownat( + dirfd: Option, + path: &P, + owner: Option, + group: Option, + flag: FchownatFlags, +) -> Result<()> { + let atflag = + match flag { + FchownatFlags::FollowSymlink => AtFlags::empty(), + FchownatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, + }; + let res = path.with_nix_path(|cstr| unsafe { + let (uid, gid) = chown_raw_ids(owner, group); + libc::fchownat(at_rawfd(dirfd), cstr.as_ptr(), uid, gid, + atflag.bits() as libc::c_int) + })?; + + Errno::result(res).map(drop) +} + +fn to_exec_array>(args: &[S]) -> Vec<*const c_char> { + use std::iter::once; + args.iter().map(|s| s.as_ref().as_ptr()).chain(once(ptr::null())).collect() +} + +/// Replace the current process image with a new one (see +/// [exec(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)). +/// +/// See the `::nix::unistd::execve` system call for additional details. `execv` +/// performs the same action but does not allow for customization of the +/// environment for the new process. +#[inline] +pub fn execv>(path: &CStr, argv: &[S]) -> Result { + let args_p = to_exec_array(argv); + + unsafe { + libc::execv(path.as_ptr(), args_p.as_ptr()) + }; + + Err(Errno::last()) +} + + +/// Replace the current process image with a new one (see +/// [execve(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)). +/// +/// The execve system call allows for another process to be "called" which will +/// replace the current process image. That is, this process becomes the new +/// command that is run. On success, this function will not return. Instead, +/// the new program will run until it exits. +/// +/// `::nix::unistd::execv` and `::nix::unistd::execve` take as arguments a slice +/// of `::std::ffi::CString`s for `args` and `env` (for `execve`). Each element +/// in the `args` list is an argument to the new process. Each element in the +/// `env` list should be a string in the form "key=value". +#[inline] +pub fn execve, SE: AsRef>(path: &CStr, args: &[SA], env: &[SE]) -> Result { + let args_p = to_exec_array(args); + let env_p = to_exec_array(env); + + unsafe { + libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) + }; + + Err(Errno::last()) +} + +/// Replace the current process image with a new one and replicate shell `PATH` +/// searching behavior (see +/// [exec(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)). +/// +/// See `::nix::unistd::execve` for additional details. `execvp` behaves the +/// same as execv except that it will examine the `PATH` environment variables +/// for file names not specified with a leading slash. For example, `execv` +/// would not work if "bash" was specified for the path argument, but `execvp` +/// would assuming that a bash executable was on the system `PATH`. +#[inline] +pub fn execvp>(filename: &CStr, args: &[S]) -> Result { + let args_p = to_exec_array(args); + + unsafe { + libc::execvp(filename.as_ptr(), args_p.as_ptr()) + }; + + Err(Errno::last()) +} + +/// Replace the current process image with a new one and replicate shell `PATH` +/// searching behavior (see +/// [`execvpe(3)`](https://man7.org/linux/man-pages/man3/exec.3.html)). +/// +/// This functions like a combination of `execvp(2)` and `execve(2)` to pass an +/// environment and have a search path. See these two for additional +/// information. +#[cfg(any(target_os = "haiku", + target_os = "linux", + target_os = "openbsd"))] +pub fn execvpe, SE: AsRef>(filename: &CStr, args: &[SA], env: &[SE]) -> Result { + let args_p = to_exec_array(args); + let env_p = to_exec_array(env); + + unsafe { + libc::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) + }; + + Err(Errno::last()) +} + +/// Replace the current process image with a new one (see +/// [fexecve(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fexecve.html)). +/// +/// The `fexecve` function allows for another process to be "called" which will +/// replace the current process image. That is, this process becomes the new +/// command that is run. On success, this function will not return. Instead, +/// the new program will run until it exits. +/// +/// This function is similar to `execve`, except that the program to be executed +/// is referenced as a file descriptor instead of a path. +// Note for NetBSD and OpenBSD: although rust-lang/libc includes it (under +// unix/bsd/netbsdlike/) fexecve is not currently implemented on NetBSD nor on +// OpenBSD. +#[cfg(any(target_os = "android", + target_os = "linux", + target_os = "freebsd"))] +#[inline] +pub fn fexecve ,SE: AsRef>(fd: RawFd, args: &[SA], env: &[SE]) -> Result { + let args_p = to_exec_array(args); + let env_p = to_exec_array(env); + + unsafe { + libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr()) + }; + + Err(Errno::last()) +} + +/// Execute program relative to a directory file descriptor (see +/// [execveat(2)](https://man7.org/linux/man-pages/man2/execveat.2.html)). +/// +/// The `execveat` function allows for another process to be "called" which will +/// replace the current process image. That is, this process becomes the new +/// command that is run. On success, this function will not return. Instead, +/// the new program will run until it exits. +/// +/// This function is similar to `execve`, except that the program to be executed +/// is referenced as a file descriptor to the base directory plus a path. +#[cfg(any(target_os = "android", target_os = "linux"))] +#[inline] +pub fn execveat,SE: AsRef>(dirfd: RawFd, pathname: &CStr, args: &[SA], + env: &[SE], flags: super::fcntl::AtFlags) -> Result { + let args_p = to_exec_array(args); + let env_p = to_exec_array(env); + + unsafe { + libc::syscall(libc::SYS_execveat, dirfd, pathname.as_ptr(), + args_p.as_ptr(), env_p.as_ptr(), flags); + }; + + Err(Errno::last()) +} + +/// Daemonize this process by detaching from the controlling terminal (see +/// [daemon(3)](https://man7.org/linux/man-pages/man3/daemon.3.html)). +/// +/// When a process is launched it is typically associated with a parent and it, +/// in turn, by its controlling terminal/process. In order for a process to run +/// in the "background" it must daemonize itself by detaching itself. Under +/// posix, this is done by doing the following: +/// +/// 1. Parent process (this one) forks +/// 2. Parent process exits +/// 3. Child process continues to run. +/// +/// `nochdir`: +/// +/// * `nochdir = true`: The current working directory after daemonizing will +/// be the current working directory. +/// * `nochdir = false`: The current working directory after daemonizing will +/// be the root direcory, `/`. +/// +/// `noclose`: +/// +/// * `noclose = true`: The process' current stdin, stdout, and stderr file +/// descriptors will remain identical after daemonizing. +/// * `noclose = false`: The process' stdin, stdout, and stderr will point to +/// `/dev/null` after daemonizing. +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris"))] +pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> { + let res = unsafe { libc::daemon(nochdir as c_int, noclose as c_int) }; + Errno::result(res).map(drop) +} + +/// Set the system host name (see +/// [sethostname(2)](https://man7.org/linux/man-pages/man2/gethostname.2.html)). +/// +/// Given a name, attempt to update the system host name to the given string. +/// On some systems, the host name is limited to as few as 64 bytes. An error +/// will be return if the name is not valid or the current process does not have +/// permissions to update the host name. +#[cfg(not(target_os = "redox"))] +pub fn sethostname>(name: S) -> Result<()> { + // Handle some differences in type of the len arg across platforms. + cfg_if! { + if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "solaris", ))] { + type sethostname_len_t = c_int; + } else { + type sethostname_len_t = size_t; + } + } + let ptr = name.as_ref().as_bytes().as_ptr() as *const c_char; + let len = name.as_ref().len() as sethostname_len_t; + + let res = unsafe { libc::sethostname(ptr, len) }; + Errno::result(res).map(drop) +} + +/// Get the host name and store it in the provided buffer, returning a pointer +/// the `CStr` in that buffer on success (see +/// [gethostname(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html)). +/// +/// This function call attempts to get the host name for the running system and +/// store it in a provided buffer. The buffer will be populated with bytes up +/// to the length of the provided slice including a NUL terminating byte. If +/// the hostname is longer than the length provided, no error will be provided. +/// The posix specification does not specify whether implementations will +/// null-terminate in this case, but the nix implementation will ensure that the +/// buffer is null terminated in this case. +/// +/// ```no_run +/// use nix::unistd; +/// +/// let mut buf = [0u8; 64]; +/// let hostname_cstr = unistd::gethostname(&mut buf).expect("Failed getting hostname"); +/// let hostname = hostname_cstr.to_str().expect("Hostname wasn't valid UTF-8"); +/// println!("Hostname: {}", hostname); +/// ``` +pub fn gethostname(buffer: &mut [u8]) -> Result<&CStr> { + let ptr = buffer.as_mut_ptr() as *mut c_char; + let len = buffer.len() as size_t; + + let res = unsafe { libc::gethostname(ptr, len) }; + Errno::result(res).map(|_| { + buffer[len - 1] = 0; // ensure always null-terminated + unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) } + }) +} + +/// Close a raw file descriptor +/// +/// Be aware that many Rust types implicitly close-on-drop, including +/// `std::fs::File`. Explicitly closing them with this method too can result in +/// a double-close condition, which can cause confusing `EBADF` errors in +/// seemingly unrelated code. Caveat programmer. See also +/// [close(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html). +/// +/// # Examples +/// +/// ```no_run +/// use std::os::unix::io::AsRawFd; +/// use nix::unistd::close; +/// +/// let f = tempfile::tempfile().unwrap(); +/// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop! +/// ``` +/// +/// ```rust +/// use std::os::unix::io::IntoRawFd; +/// use nix::unistd::close; +/// +/// let f = tempfile::tempfile().unwrap(); +/// close(f.into_raw_fd()).unwrap(); // Good. into_raw_fd consumes f +/// ``` +pub fn close(fd: RawFd) -> Result<()> { + let res = unsafe { libc::close(fd) }; + Errno::result(res).map(drop) +} + +/// Read from a raw file descriptor. +/// +/// See also [read(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html) +pub fn read(fd: RawFd, buf: &mut [u8]) -> Result { + let res = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) }; + + Errno::result(res).map(|r| r as usize) +} + +/// Write to a raw file descriptor. +/// +/// See also [write(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html) +pub fn write(fd: RawFd, buf: &[u8]) -> Result { + let res = unsafe { libc::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t) }; + + Errno::result(res).map(|r| r as usize) +} + +/// Directive that tells [`lseek`] and [`lseek64`] what the offset is relative to. +/// +/// [`lseek`]: ./fn.lseek.html +/// [`lseek64`]: ./fn.lseek64.html +#[repr(i32)] +#[derive(Clone, Copy, Debug)] +pub enum Whence { + /// Specify an offset relative to the start of the file. + SeekSet = libc::SEEK_SET, + /// Specify an offset relative to the current file location. + SeekCur = libc::SEEK_CUR, + /// Specify an offset relative to the end of the file. + SeekEnd = libc::SEEK_END, + /// Specify an offset relative to the next location in the file greater than or + /// equal to offset that contains some data. If offset points to + /// some data, then the file offset is set to offset. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "solaris"))] + SeekData = libc::SEEK_DATA, + /// Specify an offset relative to the next hole in the file greater than + /// or equal to offset. If offset points into the middle of a hole, then + /// the file offset should be set to offset. If there is no hole past offset, + /// then the file offset should be adjusted to the end of the file (i.e., there + /// is an implicit hole at the end of any file). + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "solaris"))] + SeekHole = libc::SEEK_HOLE +} + +/// Move the read/write file offset. +/// +/// See also [lseek(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lseek.html) +pub fn lseek(fd: RawFd, offset: off_t, whence: Whence) -> Result { + let res = unsafe { libc::lseek(fd, offset, whence as i32) }; + + Errno::result(res).map(|r| r as off_t) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn lseek64(fd: RawFd, offset: libc::off64_t, whence: Whence) -> Result { + let res = unsafe { libc::lseek64(fd, offset, whence as i32) }; + + Errno::result(res).map(|r| r as libc::off64_t) +} + +/// Create an interprocess channel. +/// +/// See also [pipe(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html) +pub fn pipe() -> std::result::Result<(RawFd, RawFd), Error> { + unsafe { + let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit(); + + let res = libc::pipe(fds.as_mut_ptr() as *mut c_int); + + Error::result(res)?; + + Ok((fds.assume_init()[0], fds.assume_init()[1])) + } +} + +/// Like `pipe`, but allows setting certain file descriptor flags. +/// +/// The following flags are supported, and will be set atomically as the pipe is +/// created: +/// +/// `O_CLOEXEC`: Set the close-on-exec flag for the new file descriptors. +#[cfg_attr(target_os = "linux", doc = "`O_DIRECT`: Create a pipe that performs I/O in \"packet\" mode. ")] +#[cfg_attr(target_os = "netbsd", doc = "`O_NOSIGPIPE`: Return `EPIPE` instead of raising `SIGPIPE`. ")] +/// `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe. +/// +/// See also [pipe(2)](https://man7.org/linux/man-pages/man2/pipe.2.html) +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris"))] +pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> { + let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit(); + + let res = unsafe { + libc::pipe2(fds.as_mut_ptr() as *mut c_int, flags.bits()) + }; + + Errno::result(res)?; + + unsafe { Ok((fds.assume_init()[0], fds.assume_init()[1])) } +} + +/// Truncate a file to a specified length +/// +/// See also +/// [truncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html) +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +pub fn truncate(path: &P, len: off_t) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { + libc::truncate(cstr.as_ptr(), len) + } + })?; + + Errno::result(res).map(drop) +} + +/// Truncate a file to a specified length +/// +/// See also +/// [ftruncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html) +pub fn ftruncate(fd: RawFd, len: off_t) -> Result<()> { + Errno::result(unsafe { libc::ftruncate(fd, len) }).map(drop) +} + +pub fn isatty(fd: RawFd) -> Result { + unsafe { + // ENOTTY means `fd` is a valid file descriptor, but not a TTY, so + // we return `Ok(false)` + if libc::isatty(fd) == 1 { + Ok(true) + } else { + match Errno::last() { + Errno::ENOTTY => Ok(false), + err => Err(err), + } + } + } +} + +/// Flags for `linkat` function. +#[derive(Clone, Copy, Debug)] +pub enum LinkatFlags { + SymlinkFollow, + NoSymlinkFollow, +} + +/// Link one file to another file +/// +/// Creates a new link (directory entry) at `newpath` for the existing file at `oldpath`. In the +/// case of a relative `oldpath`, the path is interpreted relative to the directory associated +/// with file descriptor `olddirfd` instead of the current working directory and similiarly for +/// `newpath` and file descriptor `newdirfd`. In case `flag` is LinkatFlags::SymlinkFollow and +/// `oldpath` names a symoblic link, a new link for the target of the symbolic link is created. +/// If either `olddirfd` or `newdirfd` is `None`, `AT_FDCWD` is used respectively where `oldpath` +/// and/or `newpath` is then interpreted relative to the current working directory of the calling +/// process. If either `oldpath` or `newpath` is absolute, then `dirfd` is ignored. +/// +/// # References +/// See also [linkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html) +#[cfg(not(target_os = "redox"))] // RedoxFS does not support symlinks yet +pub fn linkat( + olddirfd: Option, + oldpath: &P, + newdirfd: Option, + newpath: &P, + flag: LinkatFlags, +) -> Result<()> { + + let atflag = + match flag { + LinkatFlags::SymlinkFollow => AtFlags::AT_SYMLINK_FOLLOW, + LinkatFlags::NoSymlinkFollow => AtFlags::empty(), + }; + + let res = + oldpath.with_nix_path(|oldcstr| { + newpath.with_nix_path(|newcstr| { + unsafe { + libc::linkat( + at_rawfd(olddirfd), + oldcstr.as_ptr(), + at_rawfd(newdirfd), + newcstr.as_ptr(), + atflag.bits() as libc::c_int + ) + } + }) + })??; + Errno::result(res).map(drop) +} + + +/// Remove a directory entry +/// +/// See also [unlink(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html) +pub fn unlink(path: &P) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { + libc::unlink(cstr.as_ptr()) + } + })?; + Errno::result(res).map(drop) +} + +/// Flags for `unlinkat` function. +#[derive(Clone, Copy, Debug)] +pub enum UnlinkatFlags { + RemoveDir, + NoRemoveDir, +} + +/// Remove a directory entry +/// +/// In the case of a relative path, the directory entry to be removed is determined relative to +/// the directory associated with the file descriptor `dirfd` or the current working directory +/// if `dirfd` is `None`. In the case of an absolute `path` `dirfd` is ignored. If `flag` is +/// `UnlinkatFlags::RemoveDir` then removal of the directory entry specified by `dirfd` and `path` +/// is performed. +/// +/// # References +/// See also [unlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html) +#[cfg(not(target_os = "redox"))] +pub fn unlinkat( + dirfd: Option, + path: &P, + flag: UnlinkatFlags, +) -> Result<()> { + let atflag = + match flag { + UnlinkatFlags::RemoveDir => AtFlags::AT_REMOVEDIR, + UnlinkatFlags::NoRemoveDir => AtFlags::empty(), + }; + let res = path.with_nix_path(|cstr| { + unsafe { + libc::unlinkat(at_rawfd(dirfd), cstr.as_ptr(), atflag.bits() as libc::c_int) + } + })?; + Errno::result(res).map(drop) +} + + +#[inline] +#[cfg(not(target_os = "fuchsia"))] +pub fn chroot(path: &P) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::chroot(cstr.as_ptr()) } + })?; + + Errno::result(res).map(drop) +} + +/// Commit filesystem caches to disk +/// +/// See also [sync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html) +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" +))] +pub fn sync() { + unsafe { libc::sync() }; +} + +/// Synchronize changes to a file +/// +/// See also [fsync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html) +#[inline] +pub fn fsync(fd: RawFd) -> Result<()> { + let res = unsafe { libc::fsync(fd) }; + + Errno::result(res).map(drop) +} + +/// Synchronize the data of a file +/// +/// See also +/// [fdatasync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html) +// `fdatasync(2) is in POSIX, but in libc it is only defined in `libc::notbsd`. +// TODO: exclude only Apple systems after https://github.com/rust-lang/libc/pull/211 +#[cfg(any(target_os = "linux", + target_os = "android", + target_os = "emscripten", + target_os = "illumos", + target_os = "solaris"))] +#[inline] +pub fn fdatasync(fd: RawFd) -> Result<()> { + let res = unsafe { libc::fdatasync(fd) }; + + Errno::result(res).map(drop) +} + +/// Get a real user ID +/// +/// See also [getuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html) +// POSIX requires that getuid is always successful, so no need to check return +// value or errno. +#[inline] +pub fn getuid() -> Uid { + Uid(unsafe { libc::getuid() }) +} + +/// Get the effective user ID +/// +/// See also [geteuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/geteuid.html) +// POSIX requires that geteuid is always successful, so no need to check return +// value or errno. +#[inline] +pub fn geteuid() -> Uid { + Uid(unsafe { libc::geteuid() }) +} + +/// Get the real group ID +/// +/// See also [getgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html) +// POSIX requires that getgid is always successful, so no need to check return +// value or errno. +#[inline] +pub fn getgid() -> Gid { + Gid(unsafe { libc::getgid() }) +} + +/// Get the effective group ID +/// +/// See also [getegid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html) +// POSIX requires that getegid is always successful, so no need to check return +// value or errno. +#[inline] +pub fn getegid() -> Gid { + Gid(unsafe { libc::getegid() }) +} + +/// Set the effective user ID +/// +/// See also [seteuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/seteuid.html) +#[inline] +pub fn seteuid(euid: Uid) -> Result<()> { + let res = unsafe { libc::seteuid(euid.into()) }; + + Errno::result(res).map(drop) +} + +/// Set the effective group ID +/// +/// See also [setegid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html) +#[inline] +pub fn setegid(egid: Gid) -> Result<()> { + let res = unsafe { libc::setegid(egid.into()) }; + + Errno::result(res).map(drop) +} + +/// Set the user ID +/// +/// See also [setuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setuid.html) +#[inline] +pub fn setuid(uid: Uid) -> Result<()> { + let res = unsafe { libc::setuid(uid.into()) }; + + Errno::result(res).map(drop) +} + +/// Set the group ID +/// +/// See also [setgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setgid.html) +#[inline] +pub fn setgid(gid: Gid) -> Result<()> { + let res = unsafe { libc::setgid(gid.into()) }; + + Errno::result(res).map(drop) +} + +/// Set the user identity used for filesystem checks per-thread. +/// On both success and failure, this call returns the previous filesystem user +/// ID of the caller. +/// +/// See also [setfsuid(2)](https://man7.org/linux/man-pages/man2/setfsuid.2.html) +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn setfsuid(uid: Uid) -> Uid { + let prev_fsuid = unsafe { libc::setfsuid(uid.into()) }; + Uid::from_raw(prev_fsuid as uid_t) +} + +/// Set the group identity used for filesystem checks per-thread. +/// On both success and failure, this call returns the previous filesystem group +/// ID of the caller. +/// +/// See also [setfsgid(2)](https://man7.org/linux/man-pages/man2/setfsgid.2.html) +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn setfsgid(gid: Gid) -> Gid { + let prev_fsgid = unsafe { libc::setfsgid(gid.into()) }; + Gid::from_raw(prev_fsgid as gid_t) +} + +/// Get the list of supplementary group IDs of the calling process. +/// +/// [Further reading](https://pubs.opengroup.org/onlinepubs/009695399/functions/getgroups.html) +/// +/// **Note:** This function is not available for Apple platforms. On those +/// platforms, checking group membership should be achieved via communication +/// with the `opendirectoryd` service. +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +pub fn getgroups() -> Result> { + // First get the maximum number of groups. The value returned + // shall always be greater than or equal to one and less than or + // equal to the value of {NGROUPS_MAX} + 1. + let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) { + Ok(Some(n)) => (n + 1) as usize, + Ok(None) | Err(_) => ::max_value(), + }; + + // Next, get the number of groups so we can size our Vec + let ngroups = unsafe { libc::getgroups(0, ptr::null_mut()) }; + + // If there are no supplementary groups, return early. + // This prevents a potential buffer over-read if the number of groups + // increases from zero before the next call. It would return the total + // number of groups beyond the capacity of the buffer. + if ngroups == 0 { + return Ok(Vec::new()); + } + + // Now actually get the groups. We try multiple times in case the number of + // groups has changed since the first call to getgroups() and the buffer is + // now too small. + let mut groups = Vec::::with_capacity(Errno::result(ngroups)? as usize); + loop { + // FIXME: On the platforms we currently support, the `Gid` struct has + // the same representation in memory as a bare `gid_t`. This is not + // necessarily the case on all Rust platforms, though. See RFC 1785. + let ngroups = unsafe { + libc::getgroups(groups.capacity() as c_int, groups.as_mut_ptr() as *mut gid_t) + }; + + match Errno::result(ngroups) { + Ok(s) => { + unsafe { groups.set_len(s as usize) }; + return Ok(groups); + }, + Err(Errno::EINVAL) => { + // EINVAL indicates that the buffer size was too + // small, resize it up to ngroups_max as limit. + reserve_double_buffer_size(&mut groups, ngroups_max) + .or(Err(Errno::EINVAL))?; + }, + Err(e) => return Err(e) + } + } +} + +/// Set the list of supplementary group IDs for the calling process. +/// +/// [Further reading](https://man7.org/linux/man-pages/man2/getgroups.2.html) +/// +/// **Note:** This function is not available for Apple platforms. On those +/// platforms, group membership management should be achieved via communication +/// with the `opendirectoryd` service. +/// +/// # Examples +/// +/// `setgroups` can be used when dropping privileges from the root user to a +/// specific user and group. For example, given the user `www-data` with UID +/// `33` and the group `backup` with the GID `34`, one could switch the user as +/// follows: +/// +/// ```rust,no_run +/// # use std::error::Error; +/// # use nix::unistd::*; +/// # +/// # fn try_main() -> Result<(), Box> { +/// let uid = Uid::from_raw(33); +/// let gid = Gid::from_raw(34); +/// setgroups(&[gid])?; +/// setgid(gid)?; +/// setuid(uid)?; +/// # +/// # Ok(()) +/// # } +/// # +/// # try_main().unwrap(); +/// ``` +#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))] +pub fn setgroups(groups: &[Gid]) -> Result<()> { + cfg_if! { + if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "illumos", + target_os = "openbsd"))] { + type setgroups_ngroups_t = c_int; + } else { + type setgroups_ngroups_t = size_t; + } + } + // FIXME: On the platforms we currently support, the `Gid` struct has the + // same representation in memory as a bare `gid_t`. This is not necessarily + // the case on all Rust platforms, though. See RFC 1785. + let res = unsafe { + libc::setgroups(groups.len() as setgroups_ngroups_t, groups.as_ptr() as *const gid_t) + }; + + Errno::result(res).map(drop) +} + +/// Calculate the supplementary group access list. +/// +/// Gets the group IDs of all groups that `user` is a member of. The additional +/// group `group` is also added to the list. +/// +/// [Further reading](https://man7.org/linux/man-pages/man3/getgrouplist.3.html) +/// +/// **Note:** This function is not available for Apple platforms. On those +/// platforms, checking group membership should be achieved via communication +/// with the `opendirectoryd` service. +/// +/// # Errors +/// +/// Although the `getgrouplist()` call does not return any specific +/// errors on any known platforms, this implementation will return a system +/// error of `EINVAL` if the number of groups to be fetched exceeds the +/// `NGROUPS_MAX` sysconf value. This mimics the behaviour of `getgroups()` +/// and `setgroups()`. Additionally, while some implementations will return a +/// partial list of groups when `NGROUPS_MAX` is exceeded, this implementation +/// will only ever return the complete list or else an error. +#[cfg(not(any(target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "redox")))] +pub fn getgrouplist(user: &CStr, group: Gid) -> Result> { + let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) { + Ok(Some(n)) => n as c_int, + Ok(None) | Err(_) => ::max_value(), + }; + use std::cmp::min; + let mut groups = Vec::::with_capacity(min(ngroups_max, 8) as usize); + cfg_if! { + if #[cfg(any(target_os = "ios", target_os = "macos"))] { + type getgrouplist_group_t = c_int; + } else { + type getgrouplist_group_t = gid_t; + } + } + let gid: gid_t = group.into(); + loop { + let mut ngroups = groups.capacity() as i32; + let ret = unsafe { + libc::getgrouplist(user.as_ptr(), + gid as getgrouplist_group_t, + groups.as_mut_ptr() as *mut getgrouplist_group_t, + &mut ngroups) + }; + + // BSD systems only return 0 or -1, Linux returns ngroups on success. + if ret >= 0 { + unsafe { groups.set_len(ngroups as usize) }; + return Ok(groups); + } else if ret == -1 { + // Returns -1 if ngroups is too small, but does not set errno. + // BSD systems will still fill the groups buffer with as many + // groups as possible, but Linux manpages do not mention this + // behavior. + reserve_double_buffer_size(&mut groups, ngroups_max as usize) + .map_err(|_| Errno::EINVAL)?; + } + } +} + +/// Initialize the supplementary group access list. +/// +/// Sets the supplementary group IDs for the calling process using all groups +/// that `user` is a member of. The additional group `group` is also added to +/// the list. +/// +/// [Further reading](https://man7.org/linux/man-pages/man3/initgroups.3.html) +/// +/// **Note:** This function is not available for Apple platforms. On those +/// platforms, group membership management should be achieved via communication +/// with the `opendirectoryd` service. +/// +/// # Examples +/// +/// `initgroups` can be used when dropping privileges from the root user to +/// another user. For example, given the user `www-data`, we could look up the +/// UID and GID for the user in the system's password database (usually found +/// in `/etc/passwd`). If the `www-data` user's UID and GID were `33` and `33`, +/// respectively, one could switch the user as follows: +/// +/// ```rust,no_run +/// # use std::error::Error; +/// # use std::ffi::CString; +/// # use nix::unistd::*; +/// # +/// # fn try_main() -> Result<(), Box> { +/// let user = CString::new("www-data").unwrap(); +/// let uid = Uid::from_raw(33); +/// let gid = Gid::from_raw(33); +/// initgroups(&user, gid)?; +/// setgid(gid)?; +/// setuid(uid)?; +/// # +/// # Ok(()) +/// # } +/// # +/// # try_main().unwrap(); +/// ``` +#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))] +pub fn initgroups(user: &CStr, group: Gid) -> Result<()> { + cfg_if! { + if #[cfg(any(target_os = "ios", target_os = "macos"))] { + type initgroups_group_t = c_int; + } else { + type initgroups_group_t = gid_t; + } + } + let gid: gid_t = group.into(); + let res = unsafe { libc::initgroups(user.as_ptr(), gid as initgroups_group_t) }; + + Errno::result(res).map(drop) +} + +/// Suspend the thread until a signal is received. +/// +/// See also [pause(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html). +#[inline] +#[cfg(not(target_os = "redox"))] +pub fn pause() { + unsafe { libc::pause() }; +} + +pub mod alarm { + //! Alarm signal scheduling. + //! + //! Scheduling an alarm will trigger a `SIGALRM` signal when the time has + //! elapsed, which has to be caught, because the default action for the + //! signal is to terminate the program. This signal also can't be ignored + //! because the system calls like `pause` will not be interrupted, see the + //! second example below. + //! + //! # Examples + //! + //! Canceling an alarm: + //! + //! ``` + //! use nix::unistd::alarm; + //! + //! // Set an alarm for 60 seconds from now. + //! alarm::set(60); + //! + //! // Cancel the above set alarm, which returns the number of seconds left + //! // of the previously set alarm. + //! assert_eq!(alarm::cancel(), Some(60)); + //! ``` + //! + //! Scheduling an alarm and waiting for the signal: + //! +#![cfg_attr(target_os = "redox", doc = " ```rust,ignore")] +#![cfg_attr(not(target_os = "redox"), doc = " ```rust")] + //! use std::time::{Duration, Instant}; + //! + //! use nix::unistd::{alarm, pause}; + //! use nix::sys::signal::*; + //! + //! // We need to setup an empty signal handler to catch the alarm signal, + //! // otherwise the program will be terminated once the signal is delivered. + //! extern fn signal_handler(_: nix::libc::c_int) { } + //! let sa = SigAction::new( + //! SigHandler::Handler(signal_handler), + //! SaFlags::SA_RESTART, + //! SigSet::empty() + //! ); + //! unsafe { + //! sigaction(Signal::SIGALRM, &sa); + //! } + //! + //! let start = Instant::now(); + //! + //! // Set an alarm for 1 second from now. + //! alarm::set(1); + //! + //! // Pause the process until the alarm signal is received. + //! let mut sigset = SigSet::empty(); + //! sigset.add(Signal::SIGALRM); + //! sigset.wait(); + //! + //! assert!(start.elapsed() >= Duration::from_secs(1)); + //! ``` + //! + //! # References + //! + //! See also [alarm(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/alarm.html). + + /// Schedule an alarm signal. + /// + /// This will cause the system to generate a `SIGALRM` signal for the + /// process after the specified number of seconds have elapsed. + /// + /// Returns the leftover time of a previously set alarm if there was one. + pub fn set(secs: libc::c_uint) -> Option { + assert!(secs != 0, "passing 0 to `alarm::set` is not allowed, to cancel an alarm use `alarm::cancel`"); + alarm(secs) + } + + /// Cancel an previously set alarm signal. + /// + /// Returns the leftover time of a previously set alarm if there was one. + pub fn cancel() -> Option { + alarm(0) + } + + fn alarm(secs: libc::c_uint) -> Option { + match unsafe { libc::alarm(secs) } { + 0 => None, + secs => Some(secs), + } + } +} + +/// Suspend execution for an interval of time +/// +/// See also [sleep(2)](https://pubs.opengroup.org/onlinepubs/009695399/functions/sleep.html#tag_03_705_05) +// Per POSIX, does not fail +#[inline] +pub fn sleep(seconds: c_uint) -> c_uint { + unsafe { libc::sleep(seconds) } +} + +#[cfg(not(target_os = "redox"))] +pub mod acct { + use crate::{Result, NixPath}; + use crate::errno::Errno; + use std::ptr; + + /// Enable process accounting + /// + /// See also [acct(2)](https://linux.die.net/man/2/acct) + pub fn enable(filename: &P) -> Result<()> { + let res = filename.with_nix_path(|cstr| { + unsafe { libc::acct(cstr.as_ptr()) } + })?; + + Errno::result(res).map(drop) + } + + /// Disable process accounting + pub fn disable() -> Result<()> { + let res = unsafe { libc::acct(ptr::null()) }; + + Errno::result(res).map(drop) + } +} + +/// Creates a regular file which persists even after process termination +/// +/// * `template`: a path whose 6 rightmost characters must be X, e.g. `/tmp/tmpfile_XXXXXX` +/// * returns: tuple of file descriptor and filename +/// +/// Err is returned either if no temporary filename could be created or the template doesn't +/// end with XXXXXX +/// +/// See also [mkstemp(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html) +/// +/// # Example +/// +/// ```rust +/// use nix::unistd; +/// +/// let _ = match unistd::mkstemp("/tmp/tempfile_XXXXXX") { +/// Ok((fd, path)) => { +/// unistd::unlink(path.as_path()).unwrap(); // flag file to be deleted at app termination +/// fd +/// } +/// Err(e) => panic!("mkstemp failed: {}", e) +/// }; +/// // do something with fd +/// ``` +#[inline] +pub fn mkstemp(template: &P) -> Result<(RawFd, PathBuf)> { + let mut path = template.with_nix_path(|path| {path.to_bytes_with_nul().to_owned()})?; + let p = path.as_mut_ptr() as *mut _; + let fd = unsafe { libc::mkstemp(p) }; + let last = path.pop(); // drop the trailing nul + debug_assert!(last == Some(b'\0')); + let pathname = OsString::from_vec(path); + Errno::result(fd)?; + Ok((fd, PathBuf::from(pathname))) +} + +/// Variable names for `pathconf` +/// +/// Nix uses the same naming convention for these variables as the +/// [getconf(1)](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility. +/// That is, `PathconfVar` variables have the same name as the abstract +/// variables shown in the `pathconf(2)` man page. Usually, it's the same as +/// the C variable name without the leading `_PC_`. +/// +/// POSIX 1003.1-2008 standardizes all of these variables, but some OSes choose +/// not to implement variables that cannot change at runtime. +/// +/// # References +/// +/// - [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html) +/// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html) +/// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html) +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(i32)] +#[non_exhaustive] +pub enum PathconfVar { + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux", + target_os = "netbsd", target_os = "openbsd", target_os = "redox"))] + /// Minimum number of bits needed to represent, as a signed integer value, + /// the maximum size of a regular file allowed in the specified directory. + FILESIZEBITS = libc::_PC_FILESIZEBITS, + /// Maximum number of links to a single file. + LINK_MAX = libc::_PC_LINK_MAX, + /// Maximum number of bytes in a terminal canonical input line. + MAX_CANON = libc::_PC_MAX_CANON, + /// Minimum number of bytes for which space is available in a terminal input + /// queue; therefore, the maximum number of bytes a conforming application + /// may require to be typed as input before reading them. + MAX_INPUT = libc::_PC_MAX_INPUT, + /// Maximum number of bytes in a filename (not including the terminating + /// null of a filename string). + NAME_MAX = libc::_PC_NAME_MAX, + /// Maximum number of bytes the implementation will store as a pathname in a + /// user-supplied buffer of unspecified size, including the terminating null + /// character. Minimum number the implementation will accept as the maximum + /// number of bytes in a pathname. + PATH_MAX = libc::_PC_PATH_MAX, + /// Maximum number of bytes that is guaranteed to be atomic when writing to + /// a pipe. + PIPE_BUF = libc::_PC_PIPE_BUF, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "illumos", + target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "redox", target_os = "solaris"))] + /// Symbolic links can be created. + POSIX2_SYMLINKS = libc::_PC_2_SYMLINKS, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd", target_os = "redox"))] + /// Minimum number of bytes of storage actually allocated for any portion of + /// a file. + POSIX_ALLOC_SIZE_MIN = libc::_PC_ALLOC_SIZE_MIN, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] + /// Recommended increment for file transfer sizes between the + /// `POSIX_REC_MIN_XFER_SIZE` and `POSIX_REC_MAX_XFER_SIZE` values. + POSIX_REC_INCR_XFER_SIZE = libc::_PC_REC_INCR_XFER_SIZE, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd", target_os = "redox"))] + /// Maximum recommended file transfer size. + POSIX_REC_MAX_XFER_SIZE = libc::_PC_REC_MAX_XFER_SIZE, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd", target_os = "redox"))] + /// Minimum recommended file transfer size. + POSIX_REC_MIN_XFER_SIZE = libc::_PC_REC_MIN_XFER_SIZE, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd", target_os = "redox"))] + /// Recommended file transfer buffer alignment. + POSIX_REC_XFER_ALIGN = libc::_PC_REC_XFER_ALIGN, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "illumos", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", target_os = "redox", target_os = "solaris"))] + /// Maximum number of bytes in a symbolic link. + SYMLINK_MAX = libc::_PC_SYMLINK_MAX, + /// The use of `chown` and `fchown` is restricted to a process with + /// appropriate privileges, and to changing the group ID of a file only to + /// the effective group ID of the process or to one of its supplementary + /// group IDs. + _POSIX_CHOWN_RESTRICTED = libc::_PC_CHOWN_RESTRICTED, + /// Pathname components longer than {NAME_MAX} generate an error. + _POSIX_NO_TRUNC = libc::_PC_NO_TRUNC, + /// This symbol shall be defined to be the value of a character that shall + /// disable terminal special character handling. + _POSIX_VDISABLE = libc::_PC_VDISABLE, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "illumos", target_os = "linux", target_os = "openbsd", + target_os = "redox", target_os = "solaris"))] + /// Asynchronous input or output operations may be performed for the + /// associated file. + _POSIX_ASYNC_IO = libc::_PC_ASYNC_IO, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "illumos", target_os = "linux", target_os = "openbsd", + target_os = "redox", target_os = "solaris"))] + /// Prioritized input or output operations may be performed for the + /// associated file. + _POSIX_PRIO_IO = libc::_PC_PRIO_IO, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "illumos", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", target_os = "redox", target_os = "solaris"))] + /// Synchronized input or output operations may be performed for the + /// associated file. + _POSIX_SYNC_IO = libc::_PC_SYNC_IO, + #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))] + /// The resolution in nanoseconds for all file timestamps. + _POSIX_TIMESTAMP_RESOLUTION = libc::_PC_TIMESTAMP_RESOLUTION +} + +/// Like `pathconf`, but works with file descriptors instead of paths (see +/// [fpathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html)) +/// +/// # Parameters +/// +/// - `fd`: The file descriptor whose variable should be interrogated +/// - `var`: The pathconf variable to lookup +/// +/// # Returns +/// +/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its +/// implementation level (for option variables). Implementation levels are +/// usually a decimal-coded date, such as 200112 for POSIX 2001.12 +/// - `Ok(None)`: the variable has no limit (for limit variables) or is +/// unsupported (for option variables) +/// - `Err(x)`: an error occurred +pub fn fpathconf(fd: RawFd, var: PathconfVar) -> Result> { + let raw = unsafe { + Errno::clear(); + libc::fpathconf(fd, var as c_int) + }; + if raw == -1 { + if errno::errno() == 0 { + Ok(None) + } else { + Err(Errno::last()) + } + } else { + Ok(Some(raw)) + } +} + +/// Get path-dependent configurable system variables (see +/// [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html)) +/// +/// Returns the value of a path-dependent configurable system variable. Most +/// supported variables also have associated compile-time constants, but POSIX +/// allows their values to change at runtime. There are generally two types of +/// `pathconf` variables: options and limits. See [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html) for more details. +/// +/// # Parameters +/// +/// - `path`: Lookup the value of `var` for this file or directory +/// - `var`: The `pathconf` variable to lookup +/// +/// # Returns +/// +/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its +/// implementation level (for option variables). Implementation levels are +/// usually a decimal-coded date, such as 200112 for POSIX 2001.12 +/// - `Ok(None)`: the variable has no limit (for limit variables) or is +/// unsupported (for option variables) +/// - `Err(x)`: an error occurred +pub fn pathconf(path: &P, var: PathconfVar) -> Result> { + let raw = path.with_nix_path(|cstr| { + unsafe { + Errno::clear(); + libc::pathconf(cstr.as_ptr(), var as c_int) + } + })?; + if raw == -1 { + if errno::errno() == 0 { + Ok(None) + } else { + Err(Errno::last()) + } + } else { + Ok(Some(raw)) + } +} + +/// Variable names for `sysconf` +/// +/// Nix uses the same naming convention for these variables as the +/// [getconf(1)](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility. +/// That is, `SysconfVar` variables have the same name as the abstract variables +/// shown in the `sysconf(3)` man page. Usually, it's the same as the C +/// variable name without the leading `_SC_`. +/// +/// All of these symbols are standardized by POSIX 1003.1-2008, but haven't been +/// implemented by all platforms. +/// +/// # References +/// +/// - [sysconf(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html) +/// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html) +/// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html) +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(i32)] +#[non_exhaustive] +pub enum SysconfVar { + /// Maximum number of I/O operations in a single list I/O call supported by + /// the implementation. + #[cfg(not(target_os = "redox"))] + AIO_LISTIO_MAX = libc::_SC_AIO_LISTIO_MAX, + /// Maximum number of outstanding asynchronous I/O operations supported by + /// the implementation. + #[cfg(not(target_os = "redox"))] + AIO_MAX = libc::_SC_AIO_MAX, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The maximum amount by which a process can decrease its asynchronous I/O + /// priority level from its own scheduling priority. + AIO_PRIO_DELTA_MAX = libc::_SC_AIO_PRIO_DELTA_MAX, + /// Maximum length of argument to the exec functions including environment data. + ARG_MAX = libc::_SC_ARG_MAX, + /// Maximum number of functions that may be registered with `atexit`. + #[cfg(not(target_os = "redox"))] + ATEXIT_MAX = libc::_SC_ATEXIT_MAX, + /// Maximum obase values allowed by the bc utility. + #[cfg(not(target_os = "redox"))] + BC_BASE_MAX = libc::_SC_BC_BASE_MAX, + /// Maximum number of elements permitted in an array by the bc utility. + #[cfg(not(target_os = "redox"))] + BC_DIM_MAX = libc::_SC_BC_DIM_MAX, + /// Maximum scale value allowed by the bc utility. + #[cfg(not(target_os = "redox"))] + BC_SCALE_MAX = libc::_SC_BC_SCALE_MAX, + /// Maximum length of a string constant accepted by the bc utility. + #[cfg(not(target_os = "redox"))] + BC_STRING_MAX = libc::_SC_BC_STRING_MAX, + /// Maximum number of simultaneous processes per real user ID. + CHILD_MAX = libc::_SC_CHILD_MAX, + // The number of clock ticks per second. + CLK_TCK = libc::_SC_CLK_TCK, + /// Maximum number of weights that can be assigned to an entry of the + /// LC_COLLATE order keyword in the locale definition file + #[cfg(not(target_os = "redox"))] + COLL_WEIGHTS_MAX = libc::_SC_COLL_WEIGHTS_MAX, + /// Maximum number of timer expiration overruns. + #[cfg(not(target_os = "redox"))] + DELAYTIMER_MAX = libc::_SC_DELAYTIMER_MAX, + /// Maximum number of expressions that can be nested within parentheses by + /// the expr utility. + #[cfg(not(target_os = "redox"))] + EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + /// Maximum length of a host name (not including the terminating null) as + /// returned from the `gethostname` function + HOST_NAME_MAX = libc::_SC_HOST_NAME_MAX, + /// Maximum number of iovec structures that one process has available for + /// use with `readv` or `writev`. + #[cfg(not(target_os = "redox"))] + IOV_MAX = libc::_SC_IOV_MAX, + /// Unless otherwise noted, the maximum length, in bytes, of a utility's + /// input line (either standard input or another file), when the utility is + /// described as processing text files. The length includes room for the + /// trailing . + #[cfg(not(target_os = "redox"))] + LINE_MAX = libc::_SC_LINE_MAX, + /// Maximum length of a login name. + LOGIN_NAME_MAX = libc::_SC_LOGIN_NAME_MAX, + /// Maximum number of simultaneous supplementary group IDs per process. + NGROUPS_MAX = libc::_SC_NGROUPS_MAX, + /// Initial size of `getgrgid_r` and `getgrnam_r` data buffers + #[cfg(not(target_os = "redox"))] + GETGR_R_SIZE_MAX = libc::_SC_GETGR_R_SIZE_MAX, + /// Initial size of `getpwuid_r` and `getpwnam_r` data buffers + #[cfg(not(target_os = "redox"))] + GETPW_R_SIZE_MAX = libc::_SC_GETPW_R_SIZE_MAX, + /// The maximum number of open message queue descriptors a process may hold. + #[cfg(not(target_os = "redox"))] + MQ_OPEN_MAX = libc::_SC_MQ_OPEN_MAX, + /// The maximum number of message priorities supported by the implementation. + #[cfg(not(target_os = "redox"))] + MQ_PRIO_MAX = libc::_SC_MQ_PRIO_MAX, + /// A value one greater than the maximum value that the system may assign to + /// a newly-created file descriptor. + OPEN_MAX = libc::_SC_OPEN_MAX, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Advisory Information option. + _POSIX_ADVISORY_INFO = libc::_SC_ADVISORY_INFO, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + /// The implementation supports barriers. + _POSIX_BARRIERS = libc::_SC_BARRIERS, + /// The implementation supports asynchronous input and output. + #[cfg(not(target_os = "redox"))] + _POSIX_ASYNCHRONOUS_IO = libc::_SC_ASYNCHRONOUS_IO, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + /// The implementation supports clock selection. + _POSIX_CLOCK_SELECTION = libc::_SC_CLOCK_SELECTION, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + /// The implementation supports the Process CPU-Time Clocks option. + _POSIX_CPUTIME = libc::_SC_CPUTIME, + /// The implementation supports the File Synchronization option. + #[cfg(not(target_os = "redox"))] + _POSIX_FSYNC = libc::_SC_FSYNC, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd", target_os = "solaris"))] + /// The implementation supports the IPv6 option. + _POSIX_IPV6 = libc::_SC_IPV6, + /// The implementation supports job control. + #[cfg(not(target_os = "redox"))] + _POSIX_JOB_CONTROL = libc::_SC_JOB_CONTROL, + /// The implementation supports memory mapped Files. + #[cfg(not(target_os = "redox"))] + _POSIX_MAPPED_FILES = libc::_SC_MAPPED_FILES, + /// The implementation supports the Process Memory Locking option. + #[cfg(not(target_os = "redox"))] + _POSIX_MEMLOCK = libc::_SC_MEMLOCK, + /// The implementation supports the Range Memory Locking option. + #[cfg(not(target_os = "redox"))] + _POSIX_MEMLOCK_RANGE = libc::_SC_MEMLOCK_RANGE, + /// The implementation supports memory protection. + #[cfg(not(target_os = "redox"))] + _POSIX_MEMORY_PROTECTION = libc::_SC_MEMORY_PROTECTION, + /// The implementation supports the Message Passing option. + #[cfg(not(target_os = "redox"))] + _POSIX_MESSAGE_PASSING = libc::_SC_MESSAGE_PASSING, + /// The implementation supports the Monotonic Clock option. + #[cfg(not(target_os = "redox"))] + _POSIX_MONOTONIC_CLOCK = libc::_SC_MONOTONIC_CLOCK, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "illumos", target_os = "ios", target_os="linux", + target_os = "macos", target_os="openbsd", target_os = "solaris"))] + /// The implementation supports the Prioritized Input and Output option. + _POSIX_PRIORITIZED_IO = libc::_SC_PRIORITIZED_IO, + /// The implementation supports the Process Scheduling option. + #[cfg(not(target_os = "redox"))] + _POSIX_PRIORITY_SCHEDULING = libc::_SC_PRIORITY_SCHEDULING, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd", target_os = "solaris"))] + /// The implementation supports the Raw Sockets option. + _POSIX_RAW_SOCKETS = libc::_SC_RAW_SOCKETS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + /// The implementation supports read-write locks. + _POSIX_READER_WRITER_LOCKS = libc::_SC_READER_WRITER_LOCKS, + #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os = "openbsd"))] + /// The implementation supports realtime signals. + _POSIX_REALTIME_SIGNALS = libc::_SC_REALTIME_SIGNALS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="netbsd", target_os="openbsd", target_os = "solaris"))] + /// The implementation supports the Regular Expression Handling option. + _POSIX_REGEXP = libc::_SC_REGEXP, + /// Each process has a saved set-user-ID and a saved set-group-ID. + #[cfg(not(target_os = "redox"))] + _POSIX_SAVED_IDS = libc::_SC_SAVED_IDS, + /// The implementation supports semaphores. + #[cfg(not(target_os = "redox"))] + _POSIX_SEMAPHORES = libc::_SC_SEMAPHORES, + /// The implementation supports the Shared Memory Objects option. + #[cfg(not(target_os = "redox"))] + _POSIX_SHARED_MEMORY_OBJECTS = libc::_SC_SHARED_MEMORY_OBJECTS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the POSIX shell. + _POSIX_SHELL = libc::_SC_SHELL, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Spawn option. + _POSIX_SPAWN = libc::_SC_SPAWN, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports spin locks. + _POSIX_SPIN_LOCKS = libc::_SC_SPIN_LOCKS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Process Sporadic Server option. + _POSIX_SPORADIC_SERVER = libc::_SC_SPORADIC_SERVER, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _POSIX_SS_REPL_MAX = libc::_SC_SS_REPL_MAX, + /// The implementation supports the Synchronized Input and Output option. + #[cfg(not(target_os = "redox"))] + _POSIX_SYNCHRONIZED_IO = libc::_SC_SYNCHRONIZED_IO, + /// The implementation supports the Thread Stack Address Attribute option. + #[cfg(not(target_os = "redox"))] + _POSIX_THREAD_ATTR_STACKADDR = libc::_SC_THREAD_ATTR_STACKADDR, + /// The implementation supports the Thread Stack Size Attribute option. + #[cfg(not(target_os = "redox"))] + _POSIX_THREAD_ATTR_STACKSIZE = libc::_SC_THREAD_ATTR_STACKSIZE, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="netbsd", target_os="openbsd"))] + /// The implementation supports the Thread CPU-Time Clocks option. + _POSIX_THREAD_CPUTIME = libc::_SC_THREAD_CPUTIME, + /// The implementation supports the Non-Robust Mutex Priority Inheritance + /// option. + #[cfg(not(target_os = "redox"))] + _POSIX_THREAD_PRIO_INHERIT = libc::_SC_THREAD_PRIO_INHERIT, + /// The implementation supports the Non-Robust Mutex Priority Protection option. + #[cfg(not(target_os = "redox"))] + _POSIX_THREAD_PRIO_PROTECT = libc::_SC_THREAD_PRIO_PROTECT, + /// The implementation supports the Thread Execution Scheduling option. + #[cfg(not(target_os = "redox"))] + _POSIX_THREAD_PRIORITY_SCHEDULING = libc::_SC_THREAD_PRIORITY_SCHEDULING, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Thread Process-Shared Synchronization + /// option. + _POSIX_THREAD_PROCESS_SHARED = libc::_SC_THREAD_PROCESS_SHARED, + #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))] + /// The implementation supports the Robust Mutex Priority Inheritance option. + _POSIX_THREAD_ROBUST_PRIO_INHERIT = libc::_SC_THREAD_ROBUST_PRIO_INHERIT, + #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))] + /// The implementation supports the Robust Mutex Priority Protection option. + _POSIX_THREAD_ROBUST_PRIO_PROTECT = libc::_SC_THREAD_ROBUST_PRIO_PROTECT, + /// The implementation supports thread-safe functions. + #[cfg(not(target_os = "redox"))] + _POSIX_THREAD_SAFE_FUNCTIONS = libc::_SC_THREAD_SAFE_FUNCTIONS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Thread Sporadic Server option. + _POSIX_THREAD_SPORADIC_SERVER = libc::_SC_THREAD_SPORADIC_SERVER, + /// The implementation supports threads. + #[cfg(not(target_os = "redox"))] + _POSIX_THREADS = libc::_SC_THREADS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports timeouts. + _POSIX_TIMEOUTS = libc::_SC_TIMEOUTS, + /// The implementation supports timers. + #[cfg(not(target_os = "redox"))] + _POSIX_TIMERS = libc::_SC_TIMERS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Trace option. + _POSIX_TRACE = libc::_SC_TRACE, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Trace Event Filter option. + _POSIX_TRACE_EVENT_FILTER = libc::_SC_TRACE_EVENT_FILTER, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _POSIX_TRACE_EVENT_NAME_MAX = libc::_SC_TRACE_EVENT_NAME_MAX, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Trace Inherit option. + _POSIX_TRACE_INHERIT = libc::_SC_TRACE_INHERIT, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Trace Log option. + _POSIX_TRACE_LOG = libc::_SC_TRACE_LOG, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _POSIX_TRACE_NAME_MAX = libc::_SC_TRACE_NAME_MAX, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _POSIX_TRACE_SYS_MAX = libc::_SC_TRACE_SYS_MAX, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _POSIX_TRACE_USER_EVENT_MAX = libc::_SC_TRACE_USER_EVENT_MAX, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Typed Memory Objects option. + _POSIX_TYPED_MEMORY_OBJECTS = libc::_SC_TYPED_MEMORY_OBJECTS, + /// Integer value indicating version of this standard (C-language binding) + /// to which the implementation conforms. For implementations conforming to + /// POSIX.1-2008, the value shall be 200809L. + _POSIX_VERSION = libc::_SC_VERSION, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation provides a C-language compilation environment with + /// 32-bit `int`, `long`, `pointer`, and `off_t` types. + _POSIX_V6_ILP32_OFF32 = libc::_SC_V6_ILP32_OFF32, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation provides a C-language compilation environment with + /// 32-bit `int`, `long`, and pointer types and an `off_t` type using at + /// least 64 bits. + _POSIX_V6_ILP32_OFFBIG = libc::_SC_V6_ILP32_OFFBIG, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation provides a C-language compilation environment with + /// 32-bit `int` and 64-bit `long`, `pointer`, and `off_t` types. + _POSIX_V6_LP64_OFF64 = libc::_SC_V6_LP64_OFF64, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation provides a C-language compilation environment with an + /// `int` type using at least 32 bits and `long`, pointer, and `off_t` types + /// using at least 64 bits. + _POSIX_V6_LPBIG_OFFBIG = libc::_SC_V6_LPBIG_OFFBIG, + /// The implementation supports the C-Language Binding option. + #[cfg(not(target_os = "redox"))] + _POSIX2_C_BIND = libc::_SC_2_C_BIND, + /// The implementation supports the C-Language Development Utilities option. + #[cfg(not(target_os = "redox"))] + _POSIX2_C_DEV = libc::_SC_2_C_DEV, + /// The implementation supports the Terminal Characteristics option. + #[cfg(not(target_os = "redox"))] + _POSIX2_CHAR_TERM = libc::_SC_2_CHAR_TERM, + /// The implementation supports the FORTRAN Development Utilities option. + #[cfg(not(target_os = "redox"))] + _POSIX2_FORT_DEV = libc::_SC_2_FORT_DEV, + /// The implementation supports the FORTRAN Runtime Utilities option. + #[cfg(not(target_os = "redox"))] + _POSIX2_FORT_RUN = libc::_SC_2_FORT_RUN, + /// The implementation supports the creation of locales by the localedef + /// utility. + #[cfg(not(target_os = "redox"))] + _POSIX2_LOCALEDEF = libc::_SC_2_LOCALEDEF, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Batch Environment Services and Utilities + /// option. + _POSIX2_PBS = libc::_SC_2_PBS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Batch Accounting option. + _POSIX2_PBS_ACCOUNTING = libc::_SC_2_PBS_ACCOUNTING, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Batch Checkpoint/Restart option. + _POSIX2_PBS_CHECKPOINT = libc::_SC_2_PBS_CHECKPOINT, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Locate Batch Job Request option. + _POSIX2_PBS_LOCATE = libc::_SC_2_PBS_LOCATE, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Batch Job Message Request option. + _POSIX2_PBS_MESSAGE = libc::_SC_2_PBS_MESSAGE, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Track Batch Job Request option. + _POSIX2_PBS_TRACK = libc::_SC_2_PBS_TRACK, + /// The implementation supports the Software Development Utilities option. + #[cfg(not(target_os = "redox"))] + _POSIX2_SW_DEV = libc::_SC_2_SW_DEV, + /// The implementation supports the User Portability Utilities option. + #[cfg(not(target_os = "redox"))] + _POSIX2_UPE = libc::_SC_2_UPE, + /// Integer value indicating version of the Shell and Utilities volume of + /// POSIX.1 to which the implementation conforms. + #[cfg(not(target_os = "redox"))] + _POSIX2_VERSION = libc::_SC_2_VERSION, + /// The size of a system page in bytes. + /// + /// POSIX also defines an alias named `PAGESIZE`, but Rust does not allow two + /// enum constants to have the same value, so nix omits `PAGESIZE`. + PAGE_SIZE = libc::_SC_PAGE_SIZE, + #[cfg(not(target_os = "redox"))] + PTHREAD_DESTRUCTOR_ITERATIONS = libc::_SC_THREAD_DESTRUCTOR_ITERATIONS, + #[cfg(not(target_os = "redox"))] + PTHREAD_KEYS_MAX = libc::_SC_THREAD_KEYS_MAX, + #[cfg(not(target_os = "redox"))] + PTHREAD_STACK_MIN = libc::_SC_THREAD_STACK_MIN, + #[cfg(not(target_os = "redox"))] + PTHREAD_THREADS_MAX = libc::_SC_THREAD_THREADS_MAX, + RE_DUP_MAX = libc::_SC_RE_DUP_MAX, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + RTSIG_MAX = libc::_SC_RTSIG_MAX, + #[cfg(not(target_os = "redox"))] + SEM_NSEMS_MAX = libc::_SC_SEM_NSEMS_MAX, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + SEM_VALUE_MAX = libc::_SC_SEM_VALUE_MAX, + #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os = "openbsd"))] + SIGQUEUE_MAX = libc::_SC_SIGQUEUE_MAX, + STREAM_MAX = libc::_SC_STREAM_MAX, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + SYMLOOP_MAX = libc::_SC_SYMLOOP_MAX, + #[cfg(not(target_os = "redox"))] + TIMER_MAX = libc::_SC_TIMER_MAX, + TTY_NAME_MAX = libc::_SC_TTY_NAME_MAX, + TZNAME_MAX = libc::_SC_TZNAME_MAX, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the X/Open Encryption Option Group. + _XOPEN_CRYPT = libc::_SC_XOPEN_CRYPT, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the Issue 4, Version 2 Enhanced + /// Internationalization Option Group. + _XOPEN_ENH_I18N = libc::_SC_XOPEN_ENH_I18N, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _XOPEN_LEGACY = libc::_SC_XOPEN_LEGACY, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the X/Open Realtime Option Group. + _XOPEN_REALTIME = libc::_SC_XOPEN_REALTIME, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the X/Open Realtime Threads Option Group. + _XOPEN_REALTIME_THREADS = libc::_SC_XOPEN_REALTIME_THREADS, + /// The implementation supports the Issue 4, Version 2 Shared Memory Option + /// Group. + #[cfg(not(target_os = "redox"))] + _XOPEN_SHM = libc::_SC_XOPEN_SHM, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the XSI STREAMS Option Group. + _XOPEN_STREAMS = libc::_SC_XOPEN_STREAMS, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the XSI option + _XOPEN_UNIX = libc::_SC_XOPEN_UNIX, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// Integer value indicating version of the X/Open Portability Guide to + /// which the implementation conforms. + _XOPEN_VERSION = libc::_SC_XOPEN_VERSION, +} + +/// Get configurable system variables (see +/// [sysconf(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html)) +/// +/// Returns the value of a configurable system variable. Most supported +/// variables also have associated compile-time constants, but POSIX +/// allows their values to change at runtime. There are generally two types of +/// sysconf variables: options and limits. See sysconf(3) for more details. +/// +/// # Returns +/// +/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its +/// implementation level (for option variables). Implementation levels are +/// usually a decimal-coded date, such as 200112 for POSIX 2001.12 +/// - `Ok(None)`: the variable has no limit (for limit variables) or is +/// unsupported (for option variables) +/// - `Err(x)`: an error occurred +pub fn sysconf(var: SysconfVar) -> Result> { + let raw = unsafe { + Errno::clear(); + libc::sysconf(var as c_int) + }; + if raw == -1 { + if errno::errno() == 0 { + Ok(None) + } else { + Err(Errno::last()) + } + } else { + Ok(Some(raw)) + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod pivot_root { + use crate::{Result, NixPath}; + use crate::errno::Errno; + + pub fn pivot_root( + new_root: &P1, put_old: &P2) -> Result<()> { + let res = new_root.with_nix_path(|new_root| { + put_old.with_nix_path(|put_old| { + unsafe { + libc::syscall(libc::SYS_pivot_root, new_root.as_ptr(), put_old.as_ptr()) + } + }) + })??; + + Errno::result(res).map(drop) + } +} + +#[cfg(any(target_os = "android", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] +mod setres { + use crate::Result; + use crate::errno::Errno; + use super::{Uid, Gid}; + + /// Sets the real, effective, and saved uid. + /// ([see setresuid(2)](https://man7.org/linux/man-pages/man2/setresuid.2.html)) + /// + /// * `ruid`: real user id + /// * `euid`: effective user id + /// * `suid`: saved user id + /// * returns: Ok or libc error code. + /// + /// Err is returned if the user doesn't have permission to set this UID. + #[inline] + pub fn setresuid(ruid: Uid, euid: Uid, suid: Uid) -> Result<()> { + let res = unsafe { libc::setresuid(ruid.into(), euid.into(), suid.into()) }; + + Errno::result(res).map(drop) + } + + /// Sets the real, effective, and saved gid. + /// ([see setresuid(2)](https://man7.org/linux/man-pages/man2/setresuid.2.html)) + /// + /// * `rgid`: real group id + /// * `egid`: effective group id + /// * `sgid`: saved group id + /// * returns: Ok or libc error code. + /// + /// Err is returned if the user doesn't have permission to set this GID. + #[inline] + pub fn setresgid(rgid: Gid, egid: Gid, sgid: Gid) -> Result<()> { + let res = unsafe { libc::setresgid(rgid.into(), egid.into(), sgid.into()) }; + + Errno::result(res).map(drop) + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod getres { + use crate::Result; + use crate::errno::Errno; + use super::{Uid, Gid}; + + /// Real, effective and saved user IDs. + #[derive(Debug, Copy, Clone, Eq, PartialEq)] + pub struct ResUid { + pub real: Uid, + pub effective: Uid, + pub saved: Uid + } + + /// Real, effective and saved group IDs. + #[derive(Debug, Copy, Clone, Eq, PartialEq)] + pub struct ResGid { + pub real: Gid, + pub effective: Gid, + pub saved: Gid + } + + /// Gets the real, effective, and saved user IDs. + /// + /// ([see getresuid(2)](http://man7.org/linux/man-pages/man2/getresuid.2.html)) + /// + /// #Returns + /// + /// - `Ok((Uid, Uid, Uid))`: tuple of real, effective and saved uids on success. + /// - `Err(x)`: libc error code on failure. + /// + #[inline] + pub fn getresuid() -> Result { + let mut ruid = libc::uid_t::max_value(); + let mut euid = libc::uid_t::max_value(); + let mut suid = libc::uid_t::max_value(); + let res = unsafe { libc::getresuid(&mut ruid, &mut euid, &mut suid) }; + + Errno::result(res).map(|_| ResUid{ real: Uid(ruid), effective: Uid(euid), saved: Uid(suid) }) + } + + /// Gets the real, effective, and saved group IDs. + /// + /// ([see getresgid(2)](http://man7.org/linux/man-pages/man2/getresgid.2.html)) + /// + /// #Returns + /// + /// - `Ok((Gid, Gid, Gid))`: tuple of real, effective and saved gids on success. + /// - `Err(x)`: libc error code on failure. + /// + #[inline] + pub fn getresgid() -> Result { + let mut rgid = libc::gid_t::max_value(); + let mut egid = libc::gid_t::max_value(); + let mut sgid = libc::gid_t::max_value(); + let res = unsafe { libc::getresgid(&mut rgid, &mut egid, &mut sgid) }; + + Errno::result(res).map(|_| ResGid { real: Gid(rgid), effective: Gid(egid), saved: Gid(sgid) } ) + } +} + +libc_bitflags!{ + /// Options for access() + pub struct AccessFlags : c_int { + /// Test for existence of file. + F_OK; + /// Test for read permission. + R_OK; + /// Test for write permission. + W_OK; + /// Test for execute (search) permission. + X_OK; + } +} + +/// Checks the file named by `path` for accessibility according to the flags given by `amode` +/// See [access(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html) +pub fn access(path: &P, amode: AccessFlags) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { + libc::access(cstr.as_ptr(), amode.bits) + } + })?; + Errno::result(res).map(drop) +} + +/// Representation of a User, based on `libc::passwd` +/// +/// The reason some fields in this struct are `String` and others are `CString` is because some +/// fields are based on the user's locale, which could be non-UTF8, while other fields are +/// guaranteed to conform to [`NAME_REGEX`](https://serverfault.com/a/73101/407341), which only +/// contains ASCII. +#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd +#[derive(Debug, Clone, PartialEq)] +pub struct User { + /// Username + pub name: String, + /// User password (probably encrypted) + pub passwd: CString, + /// User ID + pub uid: Uid, + /// Group ID + pub gid: Gid, + /// User information + #[cfg(not(all(target_os = "android", target_pointer_width = "32")))] + pub gecos: CString, + /// Home directory + pub dir: PathBuf, + /// Path to shell + pub shell: PathBuf, + /// Login class + #[cfg(not(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris")))] + pub class: CString, + /// Last password change + #[cfg(not(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris")))] + pub change: libc::time_t, + /// Expiration time of account + #[cfg(not(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris")))] + pub expire: libc::time_t +} + +#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd +impl From<&libc::passwd> for User { + fn from(pw: &libc::passwd) -> User { + unsafe { + User { + name: CStr::from_ptr((*pw).pw_name).to_string_lossy().into_owned(), + passwd: CString::new(CStr::from_ptr((*pw).pw_passwd).to_bytes()).unwrap(), + #[cfg(not(all(target_os = "android", target_pointer_width = "32")))] + gecos: CString::new(CStr::from_ptr((*pw).pw_gecos).to_bytes()).unwrap(), + dir: PathBuf::from(OsStr::from_bytes(CStr::from_ptr((*pw).pw_dir).to_bytes())), + shell: PathBuf::from(OsStr::from_bytes(CStr::from_ptr((*pw).pw_shell).to_bytes())), + uid: Uid::from_raw((*pw).pw_uid), + gid: Gid::from_raw((*pw).pw_gid), + #[cfg(not(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris")))] + class: CString::new(CStr::from_ptr((*pw).pw_class).to_bytes()).unwrap(), + #[cfg(not(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris")))] + change: (*pw).pw_change, + #[cfg(not(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris")))] + expire: (*pw).pw_expire + } + } + } +} + +#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd +impl From for libc::passwd { + fn from(u: User) -> Self { + let name = match CString::new(u.name) { + Ok(n) => n.into_raw(), + Err(_) => CString::new("").unwrap().into_raw(), + }; + let dir = match u.dir.into_os_string().into_string() { + Ok(s) => CString::new(s.as_str()).unwrap().into_raw(), + Err(_) => CString::new("").unwrap().into_raw(), + }; + let shell = match u.shell.into_os_string().into_string() { + Ok(s) => CString::new(s.as_str()).unwrap().into_raw(), + Err(_) => CString::new("").unwrap().into_raw(), + }; + Self { + pw_name: name, + pw_passwd: u.passwd.into_raw(), + #[cfg(not(all(target_os = "android", target_pointer_width = "32")))] + pw_gecos: u.gecos.into_raw(), + pw_dir: dir, + pw_shell: shell, + pw_uid: u.uid.0, + pw_gid: u.gid.0, + #[cfg(not(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris")))] + pw_class: u.class.into_raw(), + #[cfg(not(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris")))] + pw_change: u.change, + #[cfg(not(any(target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris")))] + pw_expire: u.expire, + #[cfg(target_os = "illumos")] + pw_age: CString::new("").unwrap().into_raw(), + #[cfg(target_os = "illumos")] + pw_comment: CString::new("").unwrap().into_raw(), + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + pw_fields: 0, + } + } +} + +#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd +impl User { + fn from_anything(f: F) -> Result> + where + F: Fn(*mut libc::passwd, + *mut libc::c_char, + libc::size_t, + *mut *mut libc::passwd) -> libc::c_int + { + let buflimit = 1048576; + let bufsize = match sysconf(SysconfVar::GETPW_R_SIZE_MAX) { + Ok(Some(n)) => n as usize, + Ok(None) | Err(_) => 16384, + }; + + let mut cbuf = Vec::with_capacity(bufsize); + let mut pwd = mem::MaybeUninit::::uninit(); + let mut res = ptr::null_mut(); + + loop { + let error = f(pwd.as_mut_ptr(), cbuf.as_mut_ptr(), cbuf.capacity(), &mut res); + if error == 0 { + if res.is_null() { + return Ok(None); + } else { + let pwd = unsafe { pwd.assume_init() }; + return Ok(Some(User::from(&pwd))); + } + } else if Errno::last() == Errno::ERANGE { + // Trigger the internal buffer resizing logic. + reserve_double_buffer_size(&mut cbuf, buflimit)?; + } else { + return Err(Errno::last()); + } + } + } + + /// Get a user by UID. + /// + /// Internally, this function calls + /// [getpwuid_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html) + /// + /// # Examples + /// + /// ``` + /// use nix::unistd::{Uid, User}; + /// // Returns an Result>, thus the double unwrap. + /// let res = User::from_uid(Uid::from_raw(0)).unwrap().unwrap(); + /// assert!(res.name == "root"); + /// ``` + pub fn from_uid(uid: Uid) -> Result> { + User::from_anything(|pwd, cbuf, cap, res| { + unsafe { libc::getpwuid_r(uid.0, pwd, cbuf, cap, res) } + }) + } + + /// Get a user by name. + /// + /// Internally, this function calls + /// [getpwnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html) + /// + /// # Examples + /// + /// ``` + /// use nix::unistd::User; + /// // Returns an Result>, thus the double unwrap. + /// let res = User::from_name("root").unwrap().unwrap(); + /// assert!(res.name == "root"); + /// ``` + pub fn from_name(name: &str) -> Result> { + let name = CString::new(name).unwrap(); + User::from_anything(|pwd, cbuf, cap, res| { + unsafe { libc::getpwnam_r(name.as_ptr(), pwd, cbuf, cap, res) } + }) + } +} + +/// Representation of a Group, based on `libc::group` +#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd +#[derive(Debug, Clone, PartialEq)] +pub struct Group { + /// Group name + pub name: String, + /// Group password + pub passwd: CString, + /// Group ID + pub gid: Gid, + /// List of Group members + pub mem: Vec +} + +#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd +impl From<&libc::group> for Group { + fn from(gr: &libc::group) -> Group { + unsafe { + Group { + name: CStr::from_ptr((*gr).gr_name).to_string_lossy().into_owned(), + passwd: CString::new(CStr::from_ptr((*gr).gr_passwd).to_bytes()).unwrap(), + gid: Gid::from_raw((*gr).gr_gid), + mem: Group::members((*gr).gr_mem) + } + } + } +} + +#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd +impl Group { + unsafe fn members(mem: *mut *mut c_char) -> Vec { + let mut ret = Vec::new(); + + for i in 0.. { + let u = mem.offset(i); + if (*u).is_null() { + break; + } else { + let s = CStr::from_ptr(*u).to_string_lossy().into_owned(); + ret.push(s); + } + } + + ret + } + + fn from_anything(f: F) -> Result> + where + F: Fn(*mut libc::group, + *mut libc::c_char, + libc::size_t, + *mut *mut libc::group) -> libc::c_int + { + let buflimit = 1048576; + let bufsize = match sysconf(SysconfVar::GETGR_R_SIZE_MAX) { + Ok(Some(n)) => n as usize, + Ok(None) | Err(_) => 16384, + }; + + let mut cbuf = Vec::with_capacity(bufsize); + let mut grp = mem::MaybeUninit::::uninit(); + let mut res = ptr::null_mut(); + + loop { + let error = f(grp.as_mut_ptr(), cbuf.as_mut_ptr(), cbuf.capacity(), &mut res); + if error == 0 { + if res.is_null() { + return Ok(None); + } else { + let grp = unsafe { grp.assume_init() }; + return Ok(Some(Group::from(&grp))); + } + } else if Errno::last() == Errno::ERANGE { + // Trigger the internal buffer resizing logic. + reserve_double_buffer_size(&mut cbuf, buflimit)?; + } else { + return Err(Errno::last()); + } + } + } + + /// Get a group by GID. + /// + /// Internally, this function calls + /// [getgrgid_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html) + /// + /// # Examples + /// + // Disable this test on all OS except Linux as root group may not exist. + #[cfg_attr(not(target_os = "linux"), doc = " ```no_run")] + #[cfg_attr(target_os = "linux", doc = " ```")] + /// use nix::unistd::{Gid, Group}; + /// // Returns an Result>, thus the double unwrap. + /// let res = Group::from_gid(Gid::from_raw(0)).unwrap().unwrap(); + /// assert!(res.name == "root"); + /// ``` + pub fn from_gid(gid: Gid) -> Result> { + Group::from_anything(|grp, cbuf, cap, res| { + unsafe { libc::getgrgid_r(gid.0, grp, cbuf, cap, res) } + }) + } + + /// Get a group by name. + /// + /// Internally, this function calls + /// [getgrnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html) + /// + /// # Examples + /// + // Disable this test on all OS except Linux as root group may not exist. + #[cfg_attr(not(target_os = "linux"), doc = " ```no_run")] + #[cfg_attr(target_os = "linux", doc = " ```")] + /// use nix::unistd::Group; + /// // Returns an Result>, thus the double unwrap. + /// let res = Group::from_name("root").unwrap().unwrap(); + /// assert!(res.name == "root"); + /// ``` + pub fn from_name(name: &str) -> Result> { + let name = CString::new(name).unwrap(); + Group::from_anything(|grp, cbuf, cap, res| { + unsafe { libc::getgrnam_r(name.as_ptr(), grp, cbuf, cap, res) } + }) + } +} + +/// Get the name of the terminal device that is open on file descriptor fd +/// (see [`ttyname(3)`](https://man7.org/linux/man-pages/man3/ttyname.3.html)). +#[cfg(not(target_os = "fuchsia"))] +pub fn ttyname(fd: RawFd) -> Result { + const PATH_MAX: usize = libc::PATH_MAX as usize; + let mut buf = vec![0_u8; PATH_MAX]; + let c_buf = buf.as_mut_ptr() as *mut libc::c_char; + + let ret = unsafe { libc::ttyname_r(fd, c_buf, buf.len()) }; + if ret != 0 { + return Err(Errno::from_i32(ret)); + } + + let nul = buf.iter().position(|c| *c == b'\0').unwrap(); + buf.truncate(nul); + Ok(OsString::from_vec(buf).into()) +} + +/// Get the effective user ID and group ID associated with a Unix domain socket. +/// +/// See also [getpeereid(3)](https://www.freebsd.org/cgi/man.cgi?query=getpeereid) +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "dragonfly", +))] +pub fn getpeereid(fd: RawFd) -> Result<(Uid, Gid)> { + let mut uid = 1; + let mut gid = 1; + + let ret = unsafe { libc::getpeereid(fd, &mut uid, &mut gid) }; + + Errno::result(ret).map(|_| (Uid(uid), Gid(gid))) +} diff --git a/vendor/nix-v0.23.1-patched/test/common/mod.rs b/vendor/nix-v0.23.1-patched/test/common/mod.rs new file mode 100644 index 000000000..84a0b4fa3 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/common/mod.rs @@ -0,0 +1,141 @@ +use cfg_if::cfg_if; + +#[macro_export] macro_rules! skip { + ($($reason: expr),+) => { + use ::std::io::{self, Write}; + + let stderr = io::stderr(); + let mut handle = stderr.lock(); + writeln!(handle, $($reason),+).unwrap(); + return; + } +} + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + #[macro_export] macro_rules! require_capability { + ($name:expr, $capname:ident) => { + use ::caps::{Capability, CapSet, has_cap}; + + if !has_cap(None, CapSet::Effective, Capability::$capname) + .unwrap() + { + skip!("{} requires capability {}. Skipping test.", $name, Capability::$capname); + } + } + } + } else if #[cfg(not(target_os = "redox"))] { + #[macro_export] macro_rules! require_capability { + ($name:expr, $capname:ident) => {} + } + } +} + +/// Skip the test if we don't have the ability to mount file systems. +#[cfg(target_os = "freebsd")] +#[macro_export] macro_rules! require_mount { + ($name:expr) => { + use ::sysctl::CtlValue; + use nix::unistd::Uid; + + if !Uid::current().is_root() && CtlValue::Int(0) == ::sysctl::value("vfs.usermount").unwrap() + { + skip!("{} requires the ability to mount file systems. Skipping test.", $name); + } + } +} + +#[cfg(any(target_os = "linux", target_os= "android"))] +#[macro_export] macro_rules! skip_if_cirrus { + ($reason:expr) => { + if std::env::var_os("CIRRUS_CI").is_some() { + skip!("{}", $reason); + } + } +} + +#[cfg(target_os = "freebsd")] +#[macro_export] macro_rules! skip_if_jailed { + ($name:expr) => { + use ::sysctl::CtlValue; + + if let CtlValue::Int(1) = ::sysctl::value("security.jail.jailed") + .unwrap() + { + skip!("{} cannot run in a jail. Skipping test.", $name); + } + } +} + +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +#[macro_export] macro_rules! skip_if_not_root { + ($name:expr) => { + use nix::unistd::Uid; + + if !Uid::current().is_root() { + skip!("{} requires root privileges. Skipping test.", $name); + } + }; +} + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + #[macro_export] macro_rules! skip_if_seccomp { + ($name:expr) => { + if let Ok(s) = std::fs::read_to_string("/proc/self/status") { + for l in s.lines() { + let mut fields = l.split_whitespace(); + if fields.next() == Some("Seccomp:") && + fields.next() != Some("0") + { + skip!("{} cannot be run in Seccomp mode. Skipping test.", + stringify!($name)); + } + } + } + } + } + } else if #[cfg(not(target_os = "redox"))] { + #[macro_export] macro_rules! skip_if_seccomp { + ($name:expr) => {} + } + } +} + +cfg_if! { + if #[cfg(target_os = "linux")] { + #[macro_export] macro_rules! require_kernel_version { + ($name:expr, $version_requirement:expr) => { + use semver::{Version, VersionReq}; + + let version_requirement = VersionReq::parse($version_requirement) + .expect("Bad match_version provided"); + + let uname = nix::sys::utsname::uname(); + println!("{}", uname.sysname()); + println!("{}", uname.nodename()); + println!("{}", uname.release()); + println!("{}", uname.version()); + println!("{}", uname.machine()); + + // Fix stuff that the semver parser can't handle + let fixed_release = &uname.release().to_string() + // Fedora 33 reports version as 4.18.el8_2.x86_64 or + // 5.18.200-fc33.x86_64. Remove the underscore. + .replace("_", "-") + // Cirrus-CI reports version as 4.19.112+ . Remove the + + .replace("+", ""); + let mut version = Version::parse(fixed_release).unwrap(); + + //Keep only numeric parts + version.pre = semver::Prerelease::EMPTY; + version.build = semver::BuildMetadata::EMPTY; + + if !version_requirement.matches(&version) { + skip!("Skip {} because kernel version `{}` doesn't match the requirement `{}`", + stringify!($name), version, version_requirement); + } + } + } + } +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/mod.rs b/vendor/nix-v0.23.1-patched/test/sys/mod.rs new file mode 100644 index 000000000..e73d9b1dc --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/mod.rs @@ -0,0 +1,47 @@ +mod test_signal; + +// NOTE: DragonFly lacks a kernel-level implementation of Posix AIO as of +// this writing. There is an user-level implementation, but whether aio +// works or not heavily depends on which pthread implementation is chosen +// by the user at link time. For this reason we do not want to run aio test +// cases on DragonFly. +#[cfg(any(target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd"))] +mod test_aio; +#[cfg(not(target_os = "redox"))] +mod test_mman; +#[cfg(target_os = "linux")] +mod test_signalfd; +#[cfg(not(target_os = "redox"))] +mod test_socket; +#[cfg(not(target_os = "redox"))] +mod test_sockopt; +#[cfg(not(target_os = "redox"))] +mod test_select; +#[cfg(any(target_os = "android", target_os = "linux"))] +mod test_sysinfo; +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +mod test_termios; +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +mod test_ioctl; +mod test_wait; +mod test_uio; + +#[cfg(target_os = "linux")] +mod test_epoll; +#[cfg(target_os = "linux")] +mod test_inotify; +mod test_pthread; +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +mod test_ptrace; +#[cfg(any(target_os = "android", target_os = "linux"))] +mod test_timerfd; diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_aio.rs b/vendor/nix-v0.23.1-patched/test/sys/test_aio.rs new file mode 100644 index 000000000..b4eb31295 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_aio.rs @@ -0,0 +1,620 @@ +use libc::{c_int, c_void}; +use nix::Result; +use nix::errno::*; +use nix::sys::aio::*; +use nix::sys::signal::{SaFlags, SigAction, sigaction, SigevNotify, SigHandler, Signal, SigSet}; +use nix::sys::time::{TimeSpec, TimeValLike}; +use std::io::{Write, Read, Seek, SeekFrom}; +use std::ops::Deref; +use std::os::unix::io::AsRawFd; +use std::pin::Pin; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::{thread, time}; +use tempfile::tempfile; + +// Helper that polls an AioCb for completion or error +fn poll_aio(aiocb: &mut Pin>) -> Result<()> { + loop { + let err = aiocb.error(); + if err != Err(Errno::EINPROGRESS) { return err; }; + thread::sleep(time::Duration::from_millis(10)); + } +} + +// Helper that polls a component of an LioCb for completion or error +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +fn poll_lio(liocb: &mut LioCb, i: usize) -> Result<()> { + loop { + let err = liocb.error(i); + if err != Err(Errno::EINPROGRESS) { return err; }; + thread::sleep(time::Duration::from_millis(10)); + } +} + +#[test] +fn test_accessors() { + let mut rbuf = vec![0; 4]; + let aiocb = AioCb::from_mut_slice( 1001, + 2, //offset + &mut rbuf, + 42, //priority + SigevNotify::SigevSignal { + signal: Signal::SIGUSR2, + si_value: 99 + }, + LioOpcode::LIO_NOP); + assert_eq!(1001, aiocb.fd()); + assert_eq!(Some(LioOpcode::LIO_NOP), aiocb.lio_opcode()); + assert_eq!(4, aiocb.nbytes()); + assert_eq!(2, aiocb.offset()); + assert_eq!(42, aiocb.priority()); + let sev = aiocb.sigevent().sigevent(); + assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo); + assert_eq!(99, sev.sigev_value.sival_ptr as i64); +} + +// Tests AioCb.cancel. We aren't trying to test the OS's implementation, only +// our bindings. So it's sufficient to check that AioCb.cancel returned any +// AioCancelStat value. +#[test] +#[cfg_attr(target_env = "musl", ignore)] +fn test_cancel() { + let wbuf: &[u8] = b"CDEF"; + + let f = tempfile().unwrap(); + let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + 0, //offset + wbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); + let err = aiocb.error(); + assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS)); + + let cancelstat = aiocb.cancel(); + assert!(cancelstat.is_ok()); + + // Wait for aiocb to complete, but don't care whether it succeeded + let _ = poll_aio(&mut aiocb); + let _ = aiocb.aio_return(); +} + +// Tests using aio_cancel_all for all outstanding IOs. +#[test] +#[cfg_attr(target_env = "musl", ignore)] +fn test_aio_cancel_all() { + let wbuf: &[u8] = b"CDEF"; + + let f = tempfile().unwrap(); + let mut aiocb = AioCb::from_slice(f.as_raw_fd(), + 0, //offset + wbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); + let err = aiocb.error(); + assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS)); + + let cancelstat = aio_cancel_all(f.as_raw_fd()); + assert!(cancelstat.is_ok()); + + // Wait for aiocb to complete, but don't care whether it succeeded + let _ = poll_aio(&mut aiocb); + let _ = aiocb.aio_return(); +} + +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_fsync() { + const INITIAL: &[u8] = b"abcdef123456"; + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + let mut aiocb = AioCb::from_fd( f.as_raw_fd(), + 0, //priority + SigevNotify::SigevNone); + let err = aiocb.fsync(AioFsyncMode::O_SYNC); + assert!(err.is_ok()); + poll_aio(&mut aiocb).unwrap(); + aiocb.aio_return().unwrap(); +} + +/// `AioCb::fsync` should not modify the `AioCb` object if `libc::aio_fsync` returns +/// an error +// Skip on Linux, because Linux's AIO implementation can't detect errors +// synchronously +#[test] +#[cfg(any(target_os = "freebsd", target_os = "macos"))] +fn test_fsync_error() { + use std::mem; + + const INITIAL: &[u8] = b"abcdef123456"; + // Create an invalid AioFsyncMode + let mode = unsafe { mem::transmute(666) }; + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + let mut aiocb = AioCb::from_fd( f.as_raw_fd(), + 0, //priority + SigevNotify::SigevNone); + let err = aiocb.fsync(mode); + assert!(err.is_err()); +} + +#[test] +// On Cirrus on Linux, this test fails due to a glibc bug. +// https://github.com/nix-rust/nix/issues/1099 +#[cfg_attr(target_os = "linux", ignore)] +// On Cirrus, aio_suspend is failing with EINVAL +// https://github.com/nix-rust/nix/issues/1361 +#[cfg_attr(target_os = "macos", ignore)] +fn test_aio_suspend() { + const INITIAL: &[u8] = b"abcdef123456"; + const WBUF: &[u8] = b"CDEFG"; + let timeout = TimeSpec::seconds(10); + let mut rbuf = vec![0; 4]; + let rlen = rbuf.len(); + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + + let mut wcb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_WRITE); + + let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(), + 8, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_READ); + wcb.write().unwrap(); + rcb.read().unwrap(); + loop { + { + let cbbuf = [wcb.as_ref(), rcb.as_ref()]; + let r = aio_suspend(&cbbuf[..], Some(timeout)); + match r { + Err(Errno::EINTR) => continue, + Err(e) => panic!("aio_suspend returned {:?}", e), + Ok(_) => () + }; + } + if rcb.error() != Err(Errno::EINPROGRESS) && + wcb.error() != Err(Errno::EINPROGRESS) { + break + } + } + + assert_eq!(wcb.aio_return().unwrap() as usize, WBUF.len()); + assert_eq!(rcb.aio_return().unwrap() as usize, rlen); +} + +// Test a simple aio operation with no completion notification. We must poll +// for completion +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_read() { + const INITIAL: &[u8] = b"abcdef123456"; + let mut rbuf = vec![0; 4]; + const EXPECT: &[u8] = b"cdef"; + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + { + let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(), + 2, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.read().unwrap(); + + let err = poll_aio(&mut aiocb); + assert_eq!(err, Ok(())); + assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len()); + } + + assert_eq!(EXPECT, rbuf.deref().deref()); +} + +/// `AioCb::read` should not modify the `AioCb` object if `libc::aio_read` +/// returns an error +// Skip on Linux, because Linux's AIO implementation can't detect errors +// synchronously +#[test] +#[cfg(any(target_os = "freebsd", target_os = "macos"))] +fn test_read_error() { + const INITIAL: &[u8] = b"abcdef123456"; + let mut rbuf = vec![0; 4]; + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(), + -1, //an invalid offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + assert!(aiocb.read().is_err()); +} + +// Tests from_mut_slice +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_read_into_mut_slice() { + const INITIAL: &[u8] = b"abcdef123456"; + let mut rbuf = vec![0; 4]; + const EXPECT: &[u8] = b"cdef"; + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + { + let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(), + 2, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.read().unwrap(); + + let err = poll_aio(&mut aiocb); + assert_eq!(err, Ok(())); + assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len()); + } + + assert_eq!(rbuf, EXPECT); +} + +// Tests from_ptr +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_read_into_pointer() { + const INITIAL: &[u8] = b"abcdef123456"; + let mut rbuf = vec![0; 4]; + const EXPECT: &[u8] = b"cdef"; + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + { + // Safety: ok because rbuf lives until after poll_aio + let mut aiocb = unsafe { + AioCb::from_mut_ptr( f.as_raw_fd(), + 2, //offset + rbuf.as_mut_ptr() as *mut c_void, + rbuf.len(), + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP) + }; + aiocb.read().unwrap(); + + let err = poll_aio(&mut aiocb); + assert_eq!(err, Ok(())); + assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len()); + } + + assert_eq!(rbuf, EXPECT); +} + +// Test reading into an immutable buffer. It should fail +// FIXME: This test fails to panic on Linux/musl +#[test] +#[should_panic(expected = "Can't read into an immutable buffer")] +#[cfg_attr(target_env = "musl", ignore)] +fn test_read_immutable_buffer() { + let rbuf: &[u8] = b"CDEF"; + let f = tempfile().unwrap(); + let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.read().unwrap(); +} + + +// Test a simple aio operation with no completion notification. We must poll +// for completion. Unlike test_aio_read, this test uses AioCb::from_slice +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_write() { + const INITIAL: &[u8] = b"abcdef123456"; + let wbuf = "CDEF".to_string().into_bytes(); + let mut rbuf = Vec::new(); + const EXPECT: &[u8] = b"abCDEF123456"; + + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + &wbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); + + let err = poll_aio(&mut aiocb); + assert_eq!(err, Ok(())); + assert_eq!(aiocb.aio_return().unwrap() as usize, wbuf.len()); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf).unwrap(); + assert_eq!(len, EXPECT.len()); + assert_eq!(rbuf, EXPECT); +} + +// Tests `AioCb::from_ptr` +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_write_from_pointer() { + const INITIAL: &[u8] = b"abcdef123456"; + let wbuf = "CDEF".to_string().into_bytes(); + let mut rbuf = Vec::new(); + const EXPECT: &[u8] = b"abCDEF123456"; + + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + // Safety: ok because aiocb outlives poll_aio + let mut aiocb = unsafe { + AioCb::from_ptr( f.as_raw_fd(), + 2, //offset + wbuf.as_ptr() as *const c_void, + wbuf.len(), + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP) + }; + aiocb.write().unwrap(); + + let err = poll_aio(&mut aiocb); + assert_eq!(err, Ok(())); + assert_eq!(aiocb.aio_return().unwrap() as usize, wbuf.len()); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf).unwrap(); + assert_eq!(len, EXPECT.len()); + assert_eq!(rbuf, EXPECT); +} + +/// `AioCb::write` should not modify the `AioCb` object if `libc::aio_write` +/// returns an error +// Skip on Linux, because Linux's AIO implementation can't detect errors +// synchronously +#[test] +#[cfg(any(target_os = "freebsd", target_os = "macos"))] +fn test_write_error() { + let wbuf = "CDEF".to_string().into_bytes(); + let mut aiocb = AioCb::from_slice( 666, // An invalid file descriptor + 0, //offset + &wbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + assert!(aiocb.write().is_err()); +} + +lazy_static! { + pub static ref SIGNALED: AtomicBool = AtomicBool::new(false); +} + +extern fn sigfunc(_: c_int) { + SIGNALED.store(true, Ordering::Relaxed); +} + +// Test an aio operation with completion delivered by a signal +// FIXME: This test is ignored on mips because of failures in qemu in CI +#[test] +#[cfg_attr(any(all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips", target_arch = "mips64"), ignore)] +fn test_write_sigev_signal() { + let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + let sa = SigAction::new(SigHandler::Handler(sigfunc), + SaFlags::SA_RESETHAND, + SigSet::empty()); + SIGNALED.store(false, Ordering::Relaxed); + unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap(); + + const INITIAL: &[u8] = b"abcdef123456"; + const WBUF: &[u8] = b"CDEF"; + let mut rbuf = Vec::new(); + const EXPECT: &[u8] = b"abCDEF123456"; + + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevSignal { + signal: Signal::SIGUSR2, + si_value: 0 //TODO: validate in sigfunc + }, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); + while !SIGNALED.load(Ordering::Relaxed) { + thread::sleep(time::Duration::from_millis(10)); + } + + assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf).unwrap(); + assert_eq!(len, EXPECT.len()); + assert_eq!(rbuf, EXPECT); +} + +// Test LioCb::listio with LIO_WAIT, so all AIO ops should be complete by the +// time listio returns. +#[test] +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_liocb_listio_wait() { + const INITIAL: &[u8] = b"abcdef123456"; + const WBUF: &[u8] = b"CDEF"; + let mut rbuf = vec![0; 4]; + let rlen = rbuf.len(); + let mut rbuf2 = Vec::new(); + const EXPECT: &[u8] = b"abCDEF123456"; + let mut f = tempfile().unwrap(); + + f.write_all(INITIAL).unwrap(); + + { + let mut liocb = LioCbBuilder::with_capacity(2) + .emplace_slice( + f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_WRITE + ).emplace_mut_slice( + f.as_raw_fd(), + 8, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_READ + ).finish(); + let err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone); + err.expect("lio_listio"); + + assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len()); + assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen); + } + assert_eq!(rbuf.deref().deref(), b"3456"); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf2).unwrap(); + assert_eq!(len, EXPECT.len()); + assert_eq!(rbuf2, EXPECT); +} + +// Test LioCb::listio with LIO_NOWAIT and no SigEvent, so we must use some other +// mechanism to check for the individual AioCb's completion. +#[test] +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_liocb_listio_nowait() { + const INITIAL: &[u8] = b"abcdef123456"; + const WBUF: &[u8] = b"CDEF"; + let mut rbuf = vec![0; 4]; + let rlen = rbuf.len(); + let mut rbuf2 = Vec::new(); + const EXPECT: &[u8] = b"abCDEF123456"; + let mut f = tempfile().unwrap(); + + f.write_all(INITIAL).unwrap(); + + { + let mut liocb = LioCbBuilder::with_capacity(2) + .emplace_slice( + f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_WRITE + ).emplace_mut_slice( + f.as_raw_fd(), + 8, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_READ + ).finish(); + let err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone); + err.expect("lio_listio"); + + poll_lio(&mut liocb, 0).unwrap(); + poll_lio(&mut liocb, 1).unwrap(); + assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len()); + assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen); + } + assert_eq!(rbuf.deref().deref(), b"3456"); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf2).unwrap(); + assert_eq!(len, EXPECT.len()); + assert_eq!(rbuf2, EXPECT); +} + +// Test LioCb::listio with LIO_NOWAIT and a SigEvent to indicate when all +// AioCb's are complete. +// FIXME: This test is ignored on mips/mips64 because of failures in qemu in CI. +#[test] +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[cfg_attr(any(target_arch = "mips", target_arch = "mips64", target_env = "musl"), ignore)] +fn test_liocb_listio_signal() { + let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + const INITIAL: &[u8] = b"abcdef123456"; + const WBUF: &[u8] = b"CDEF"; + let mut rbuf = vec![0; 4]; + let rlen = rbuf.len(); + let mut rbuf2 = Vec::new(); + const EXPECT: &[u8] = b"abCDEF123456"; + let mut f = tempfile().unwrap(); + let sa = SigAction::new(SigHandler::Handler(sigfunc), + SaFlags::SA_RESETHAND, + SigSet::empty()); + let sigev_notify = SigevNotify::SigevSignal { signal: Signal::SIGUSR2, + si_value: 0 }; + + f.write_all(INITIAL).unwrap(); + + { + let mut liocb = LioCbBuilder::with_capacity(2) + .emplace_slice( + f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_WRITE + ).emplace_mut_slice( + f.as_raw_fd(), + 8, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_READ + ).finish(); + SIGNALED.store(false, Ordering::Relaxed); + unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap(); + let err = liocb.listio(LioMode::LIO_NOWAIT, sigev_notify); + err.expect("lio_listio"); + while !SIGNALED.load(Ordering::Relaxed) { + thread::sleep(time::Duration::from_millis(10)); + } + + assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len()); + assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen); + } + assert_eq!(rbuf.deref().deref(), b"3456"); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf2).unwrap(); + assert_eq!(len, EXPECT.len()); + assert_eq!(rbuf2, EXPECT); +} + +// Try to use LioCb::listio to read into an immutable buffer. It should fail +// FIXME: This test fails to panic on Linux/musl +#[test] +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[should_panic(expected = "Can't read into an immutable buffer")] +#[cfg_attr(target_env = "musl", ignore)] +fn test_liocb_listio_read_immutable() { + let rbuf: &[u8] = b"abcd"; + let f = tempfile().unwrap(); + + + let mut liocb = LioCbBuilder::with_capacity(1) + .emplace_slice( + f.as_raw_fd(), + 2, //offset + rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_READ + ).finish(); + let _ = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_aio_drop.rs b/vendor/nix-v0.23.1-patched/test/sys/test_aio_drop.rs new file mode 100644 index 000000000..71a2183bc --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_aio_drop.rs @@ -0,0 +1,29 @@ +// Test dropping an AioCb that hasn't yet finished. +// This must happen in its own process, because on OSX this test seems to hose +// the AIO subsystem and causes subsequent tests to fail +#[test] +#[should_panic(expected = "Dropped an in-progress AioCb")] +#[cfg(all(not(target_env = "musl"), + any(target_os = "linux", + target_os = "ios", + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd")))] +fn test_drop() { + use nix::sys::aio::*; + use nix::sys::signal::*; + use std::os::unix::io::AsRawFd; + use tempfile::tempfile; + + const WBUF: &[u8] = b"CDEF"; + + let f = tempfile().unwrap(); + f.set_len(6).unwrap(); + let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_epoll.rs b/vendor/nix-v0.23.1-patched/test/sys/test_epoll.rs new file mode 100644 index 000000000..8d44cd08f --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_epoll.rs @@ -0,0 +1,23 @@ +use nix::sys::epoll::{EpollCreateFlags, EpollFlags, EpollOp, EpollEvent}; +use nix::sys::epoll::{epoll_create1, epoll_ctl}; +use nix::errno::Errno; + +#[test] +pub fn test_epoll_errno() { + let efd = epoll_create1(EpollCreateFlags::empty()).unwrap(); + let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Errno::ENOENT); + + let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Errno::EINVAL); +} + +#[test] +pub fn test_epoll_ctl() { + let efd = epoll_create1(EpollCreateFlags::empty()).unwrap(); + let mut event = EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLERR, 1); + epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, &mut event).unwrap(); + epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None).unwrap(); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_inotify.rs b/vendor/nix-v0.23.1-patched/test/sys/test_inotify.rs new file mode 100644 index 000000000..137816a35 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_inotify.rs @@ -0,0 +1,63 @@ +use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify}; +use nix::errno::Errno; +use std::ffi::OsString; +use std::fs::{rename, File}; + +#[test] +pub fn test_inotify() { + let instance = Inotify::init(InitFlags::IN_NONBLOCK) + .unwrap(); + let tempdir = tempfile::tempdir().unwrap(); + + instance.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS).unwrap(); + + let events = instance.read_events(); + assert_eq!(events.unwrap_err(), Errno::EAGAIN); + + File::create(tempdir.path().join("test")).unwrap(); + + let events = instance.read_events().unwrap(); + assert_eq!(events[0].name, Some(OsString::from("test"))); +} + +#[test] +pub fn test_inotify_multi_events() { + let instance = Inotify::init(InitFlags::IN_NONBLOCK) + .unwrap(); + let tempdir = tempfile::tempdir().unwrap(); + + instance.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS).unwrap(); + + let events = instance.read_events(); + assert_eq!(events.unwrap_err(), Errno::EAGAIN); + + File::create(tempdir.path().join("test")).unwrap(); + rename(tempdir.path().join("test"), tempdir.path().join("test2")).unwrap(); + + // Now there should be 5 events in queue: + // - IN_CREATE on test + // - IN_OPEN on test + // - IN_CLOSE_WRITE on test + // - IN_MOVED_FROM on test with a cookie + // - IN_MOVED_TO on test2 with the same cookie + + let events = instance.read_events().unwrap(); + assert_eq!(events.len(), 5); + + assert_eq!(events[0].mask, AddWatchFlags::IN_CREATE); + assert_eq!(events[0].name, Some(OsString::from("test"))); + + assert_eq!(events[1].mask, AddWatchFlags::IN_OPEN); + assert_eq!(events[1].name, Some(OsString::from("test"))); + + assert_eq!(events[2].mask, AddWatchFlags::IN_CLOSE_WRITE); + assert_eq!(events[2].name, Some(OsString::from("test"))); + + assert_eq!(events[3].mask, AddWatchFlags::IN_MOVED_FROM); + assert_eq!(events[3].name, Some(OsString::from("test"))); + + assert_eq!(events[4].mask, AddWatchFlags::IN_MOVED_TO); + assert_eq!(events[4].name, Some(OsString::from("test2"))); + + assert_eq!(events[3].cookie, events[4].cookie); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_ioctl.rs b/vendor/nix-v0.23.1-patched/test/sys/test_ioctl.rs new file mode 100644 index 000000000..236d24268 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_ioctl.rs @@ -0,0 +1,337 @@ +#![allow(dead_code)] + +// Simple tests to ensure macro generated fns compile +ioctl_none_bad!(do_bad, 0x1234); +ioctl_read_bad!(do_bad_read, 0x1234, u16); +ioctl_write_int_bad!(do_bad_write_int, 0x1234); +ioctl_write_ptr_bad!(do_bad_write_ptr, 0x1234, u8); +ioctl_readwrite_bad!(do_bad_readwrite, 0x1234, u32); +ioctl_none!(do_none, 0, 0); +ioctl_read!(read_test, 0, 0, u32); +ioctl_write_int!(write_ptr_int, 0, 0); +ioctl_write_ptr!(write_ptr_u8, 0, 0, u8); +ioctl_write_ptr!(write_ptr_u32, 0, 0, u32); +ioctl_write_ptr!(write_ptr_u64, 0, 0, u64); +ioctl_readwrite!(readwrite_test, 0, 0, u64); +ioctl_read_buf!(readbuf_test, 0, 0, u32); +const SPI_IOC_MAGIC: u8 = b'k'; +const SPI_IOC_MESSAGE: u8 = 0; +ioctl_write_buf!(writebuf_test_consts, SPI_IOC_MAGIC, SPI_IOC_MESSAGE, u8); +ioctl_write_buf!(writebuf_test_u8, 0, 0, u8); +ioctl_write_buf!(writebuf_test_u32, 0, 0, u32); +ioctl_write_buf!(writebuf_test_u64, 0, 0, u64); +ioctl_readwrite_buf!(readwritebuf_test, 0, 0, u32); + +// See C code for source of values for op calculations (does NOT work for mips/powerpc): +// https://gist.github.com/posborne/83ea6880770a1aef332e +// +// TODO: Need a way to compute these constants at test time. Using precomputed +// values is fragile and needs to be maintained. + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod linux { + #[test] + fn test_op_none() { + if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ + assert_eq!(request_code_none!(b'q', 10) as u32, 0x2000_710A); + assert_eq!(request_code_none!(b'a', 255) as u32, 0x2000_61FF); + } else { + assert_eq!(request_code_none!(b'q', 10) as u32, 0x0000_710A); + assert_eq!(request_code_none!(b'a', 255) as u32, 0x0000_61FF); + } + } + + #[test] + fn test_op_write() { + if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ + assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x8001_7A0A); + assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x8200_7A0A); + } else { + assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x4001_7A0A); + assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x4200_7A0A); + } + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_op_write_64() { + if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){ + assert_eq!(request_code_write!(b'z', 10, 1u64 << 32) as u32, + 0x8000_7A0A); + } else { + assert_eq!(request_code_write!(b'z', 10, 1u64 << 32) as u32, + 0x4000_7A0A); + } + + } + + #[test] + fn test_op_read() { + if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ + assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x4001_7A0A); + assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x4200_7A0A); + } else { + assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x8001_7A0A); + assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x8200_7A0A); + } + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_op_read_64() { + if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){ + assert_eq!(request_code_read!(b'z', 10, 1u64 << 32) as u32, + 0x4000_7A0A); + } else { + assert_eq!(request_code_read!(b'z', 10, 1u64 << 32) as u32, + 0x8000_7A0A); + } + } + + #[test] + fn test_op_read_write() { + assert_eq!(request_code_readwrite!(b'z', 10, 1) as u32, 0xC001_7A0A); + assert_eq!(request_code_readwrite!(b'z', 10, 512) as u32, 0xC200_7A0A); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_op_read_write_64() { + assert_eq!(request_code_readwrite!(b'z', 10, 1u64 << 32) as u32, + 0xC000_7A0A); + } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +mod bsd { + #[test] + fn test_op_none() { + assert_eq!(request_code_none!(b'q', 10), 0x2000_710A); + assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF); + } + + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + #[test] + fn test_op_write_int() { + assert_eq!(request_code_write_int!(b'v', 4), 0x2004_7604); + assert_eq!(request_code_write_int!(b'p', 2), 0x2004_7002); + } + + #[test] + fn test_op_write() { + assert_eq!(request_code_write!(b'z', 10, 1), 0x8001_7A0A); + assert_eq!(request_code_write!(b'z', 10, 512), 0x8200_7A0A); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_op_write_64() { + assert_eq!(request_code_write!(b'z', 10, 1u64 << 32), 0x8000_7A0A); + } + + #[test] + fn test_op_read() { + assert_eq!(request_code_read!(b'z', 10, 1), 0x4001_7A0A); + assert_eq!(request_code_read!(b'z', 10, 512), 0x4200_7A0A); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_op_read_64() { + assert_eq!(request_code_read!(b'z', 10, 1u64 << 32), 0x4000_7A0A); + } + + #[test] + fn test_op_read_write() { + assert_eq!(request_code_readwrite!(b'z', 10, 1), 0xC001_7A0A); + assert_eq!(request_code_readwrite!(b'z', 10, 512), 0xC200_7A0A); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_op_read_write_64() { + assert_eq!(request_code_readwrite!(b'z', 10, 1u64 << 32), 0xC000_7A0A); + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod linux_ioctls { + use std::mem; + use std::os::unix::io::AsRawFd; + + use tempfile::tempfile; + use libc::{TCGETS, TCSBRK, TCSETS, TIOCNXCL, termios}; + + use nix::errno::Errno; + + ioctl_none_bad!(tiocnxcl, TIOCNXCL); + #[test] + fn test_ioctl_none_bad() { + let file = tempfile().unwrap(); + let res = unsafe { tiocnxcl(file.as_raw_fd()) }; + assert_eq!(res, Err(Errno::ENOTTY)); + } + + ioctl_read_bad!(tcgets, TCGETS, termios); + #[test] + fn test_ioctl_read_bad() { + let file = tempfile().unwrap(); + let mut termios = unsafe { mem::zeroed() }; + let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) }; + assert_eq!(res, Err(Errno::ENOTTY)); + } + + ioctl_write_int_bad!(tcsbrk, TCSBRK); + #[test] + fn test_ioctl_write_int_bad() { + let file = tempfile().unwrap(); + let res = unsafe { tcsbrk(file.as_raw_fd(), 0) }; + assert_eq!(res, Err(Errno::ENOTTY)); + } + + ioctl_write_ptr_bad!(tcsets, TCSETS, termios); + #[test] + fn test_ioctl_write_ptr_bad() { + let file = tempfile().unwrap(); + let termios: termios = unsafe { mem::zeroed() }; + let res = unsafe { tcsets(file.as_raw_fd(), &termios) }; + assert_eq!(res, Err(Errno::ENOTTY)); + } + + // FIXME: Find a suitable example for `ioctl_readwrite_bad` + + // From linux/videodev2.h + ioctl_none!(log_status, b'V', 70); + #[test] + fn test_ioctl_none() { + let file = tempfile().unwrap(); + let res = unsafe { log_status(file.as_raw_fd()) }; + assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS)); + } + + #[repr(C)] + pub struct v4l2_audio { + index: u32, + name: [u8; 32], + capability: u32, + mode: u32, + reserved: [u32; 2], + } + + // From linux/videodev2.h + ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio); + #[test] + fn test_ioctl_write_ptr() { + let file = tempfile().unwrap(); + let data: v4l2_audio = unsafe { mem::zeroed() }; + let res = unsafe { s_audio(file.as_raw_fd(), &data) }; + assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS)); + } + + // From linux/net/bluetooth/hci_sock.h + const HCI_IOC_MAGIC: u8 = b'H'; + const HCI_IOC_HCIDEVUP: u8 = 201; + ioctl_write_int!(hcidevup, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); + #[test] + fn test_ioctl_write_int() { + let file = tempfile().unwrap(); + let res = unsafe { hcidevup(file.as_raw_fd(), 0) }; + assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS)); + } + + // From linux/videodev2.h + ioctl_read!(g_audio, b'V', 33, v4l2_audio); + #[test] + fn test_ioctl_read() { + let file = tempfile().unwrap(); + let mut data: v4l2_audio = unsafe { mem::zeroed() }; + let res = unsafe { g_audio(file.as_raw_fd(), &mut data) }; + assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS)); + } + + // From linux/videodev2.h + ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio); + #[test] + fn test_ioctl_readwrite() { + let file = tempfile().unwrap(); + let mut data: v4l2_audio = unsafe { mem::zeroed() }; + let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) }; + assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS)); + } + + // FIXME: Find a suitable example for `ioctl_read_buf`. + + #[repr(C)] + pub struct spi_ioc_transfer { + tx_buf: u64, + rx_buf: u64, + len: u32, + speed_hz: u32, + delay_usecs: u16, + bits_per_word: u8, + cs_change: u8, + tx_nbits: u8, + rx_nbits: u8, + pad: u16, + } + + // From linux/spi/spidev.h + ioctl_write_buf!(spi_ioc_message, super::SPI_IOC_MAGIC, super::SPI_IOC_MESSAGE, spi_ioc_transfer); + #[test] + fn test_ioctl_write_buf() { + let file = tempfile().unwrap(); + let data: [spi_ioc_transfer; 4] = unsafe { mem::zeroed() }; + let res = unsafe { spi_ioc_message(file.as_raw_fd(), &data[..]) }; + assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS)); + } + + // FIXME: Find a suitable example for `ioctl_readwrite_buf`. +} + +#[cfg(target_os = "freebsd")] +mod freebsd_ioctls { + use std::mem; + use std::os::unix::io::AsRawFd; + + use tempfile::tempfile; + use libc::termios; + + use nix::errno::Errno; + + // From sys/sys/ttycom.h + const TTY_IOC_MAGIC: u8 = b't'; + const TTY_IOC_TYPE_NXCL: u8 = 14; + const TTY_IOC_TYPE_GETA: u8 = 19; + const TTY_IOC_TYPE_SETA: u8 = 20; + + ioctl_none!(tiocnxcl, TTY_IOC_MAGIC, TTY_IOC_TYPE_NXCL); + #[test] + fn test_ioctl_none() { + let file = tempfile().unwrap(); + let res = unsafe { tiocnxcl(file.as_raw_fd()) }; + assert_eq!(res, Err(Errno::ENOTTY)); + } + + ioctl_read!(tiocgeta, TTY_IOC_MAGIC, TTY_IOC_TYPE_GETA, termios); + #[test] + fn test_ioctl_read() { + let file = tempfile().unwrap(); + let mut termios = unsafe { mem::zeroed() }; + let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) }; + assert_eq!(res, Err(Errno::ENOTTY)); + } + + ioctl_write_ptr!(tiocseta, TTY_IOC_MAGIC, TTY_IOC_TYPE_SETA, termios); + #[test] + fn test_ioctl_write_ptr() { + let file = tempfile().unwrap(); + let termios: termios = unsafe { mem::zeroed() }; + let res = unsafe { tiocseta(file.as_raw_fd(), &termios) }; + assert_eq!(res, Err(Errno::ENOTTY)); + } +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_lio_listio_resubmit.rs b/vendor/nix-v0.23.1-patched/test/sys/test_lio_listio_resubmit.rs new file mode 100644 index 000000000..c9077891c --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_lio_listio_resubmit.rs @@ -0,0 +1,106 @@ +// vim: tw=80 + +// Annoyingly, Cargo is unable to conditionally build an entire test binary. So +// we must disable the test here rather than in Cargo.toml +#![cfg(target_os = "freebsd")] + +use nix::errno::*; +use nix::libc::off_t; +use nix::sys::aio::*; +use nix::sys::signal::SigevNotify; +use nix::unistd::{SysconfVar, sysconf}; +use std::os::unix::io::AsRawFd; +use std::{thread, time}; +use sysctl::CtlValue; +use tempfile::tempfile; + +const BYTES_PER_OP: usize = 512; + +/// Attempt to collect final status for all of `liocb`'s operations, freeing +/// system resources +fn finish_liocb(liocb: &mut LioCb) { + for j in 0..liocb.len() { + loop { + let e = liocb.error(j); + match e { + Ok(()) => break, + Err(Errno::EINPROGRESS) => + thread::sleep(time::Duration::from_millis(10)), + Err(x) => panic!("aio_error({:?})", x) + } + } + assert_eq!(liocb.aio_return(j).unwrap(), BYTES_PER_OP as isize); + } +} + +// Deliberately exceed system resource limits, causing lio_listio to return EIO. +// This test must run in its own process since it deliberately uses all AIO +// resources. ATM it is only enabled on FreeBSD, because I don't know how to +// check system AIO limits on other operating systems. +#[test] +fn test_lio_listio_resubmit() { + let mut resubmit_count = 0; + + // Lookup system resource limits + let alm = sysconf(SysconfVar::AIO_LISTIO_MAX) + .expect("sysconf").unwrap() as usize; + let maqpp = if let CtlValue::Int(x) = sysctl::value( + "vfs.aio.max_aio_queue_per_proc").unwrap(){ + x as usize + } else { + panic!("unknown sysctl"); + }; + + // Find lio_listio sizes that satisfy the AIO_LISTIO_MAX constraint and also + // result in a final lio_listio call that can only partially be queued + let target_ops = maqpp + alm / 2; + let num_listios = (target_ops + alm - 3) / (alm - 2); + let ops_per_listio = (target_ops + num_listios - 1) / num_listios; + assert!((num_listios - 1) * ops_per_listio < maqpp, + "the last lio_listio won't make any progress; fix the algorithm"); + println!("Using {:?} LioCbs of {:?} operations apiece", num_listios, + ops_per_listio); + + let f = tempfile().unwrap(); + let buffer_set = (0..num_listios).map(|_| { + (0..ops_per_listio).map(|_| { + vec![0u8; BYTES_PER_OP] + }).collect::>() + }).collect::>(); + + let mut liocbs = (0..num_listios).map(|i| { + let mut builder = LioCbBuilder::with_capacity(ops_per_listio); + for j in 0..ops_per_listio { + let offset = (BYTES_PER_OP * (i * ops_per_listio + j)) as off_t; + builder = builder.emplace_slice(f.as_raw_fd(), + offset, + &buffer_set[i][j][..], + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_WRITE); + } + let mut liocb = builder.finish(); + let mut err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone); + while err == Err(Errno::EIO) || + err == Err(Errno::EAGAIN) || + err == Err(Errno::EINTR) { + // + thread::sleep(time::Duration::from_millis(10)); + resubmit_count += 1; + err = liocb.listio_resubmit(LioMode::LIO_NOWAIT, + SigevNotify::SigevNone); + } + liocb + }).collect::>(); + + // Ensure that every AioCb completed + for liocb in liocbs.iter_mut() { + finish_liocb(liocb); + } + + if resubmit_count > 0 { + println!("Resubmitted {:?} times, test passed", resubmit_count); + } else { + println!("Never resubmitted. Test ambiguous"); + } +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_mman.rs b/vendor/nix-v0.23.1-patched/test/sys/test_mman.rs new file mode 100644 index 000000000..a7ceedcbd --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_mman.rs @@ -0,0 +1,92 @@ +use nix::sys::mman::{mmap, MapFlags, ProtFlags}; + +#[test] +fn test_mmap_anonymous() { + unsafe { + let ptr = mmap(std::ptr::null_mut(), 1, + ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, + MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS, -1, 0) + .unwrap() as *mut u8; + assert_eq !(*ptr, 0x00u8); + *ptr = 0xffu8; + assert_eq !(*ptr, 0xffu8); + } +} + +#[test] +#[cfg(any(target_os = "linux", target_os = "netbsd"))] +fn test_mremap_grow() { + use nix::sys::mman::{mremap, MRemapFlags}; + use nix::libc::{c_void, size_t}; + + const ONE_K : size_t = 1024; + let slice : &mut[u8] = unsafe { + let mem = mmap(std::ptr::null_mut(), ONE_K, + ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, + MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, -1, 0) + .unwrap(); + std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K) + }; + assert_eq !(slice[ONE_K - 1], 0x00); + slice[ONE_K - 1] = 0xFF; + assert_eq !(slice[ONE_K - 1], 0xFF); + + let slice : &mut[u8] = unsafe { + #[cfg(target_os = "linux")] + let mem = mremap(slice.as_mut_ptr() as * mut c_void, ONE_K, 10 * ONE_K, + MRemapFlags::MREMAP_MAYMOVE, None) + .unwrap(); + #[cfg(target_os = "netbsd")] + let mem = mremap(slice.as_mut_ptr() as * mut c_void, ONE_K, 10 * ONE_K, + MRemapFlags::MAP_REMAPDUP, None) + .unwrap(); + std::slice::from_raw_parts_mut(mem as * mut u8, 10 * ONE_K) + }; + + // The first KB should still have the old data in it. + assert_eq !(slice[ONE_K - 1], 0xFF); + + // The additional range should be zero-init'd and accessible. + assert_eq !(slice[10 * ONE_K - 1], 0x00); + slice[10 * ONE_K - 1] = 0xFF; + assert_eq !(slice[10 * ONE_K - 1], 0xFF); +} + +#[test] +#[cfg(any(target_os = "linux", target_os = "netbsd"))] +// Segfaults for unknown reasons under QEMU for 32-bit targets +#[cfg_attr(all(target_pointer_width = "32", qemu), ignore)] +fn test_mremap_shrink() { + use nix::sys::mman::{mremap, MRemapFlags}; + use nix::libc::{c_void, size_t}; + + const ONE_K : size_t = 1024; + let slice : &mut[u8] = unsafe { + let mem = mmap(std::ptr::null_mut(), 10 * ONE_K, + ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, + MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, -1, 0) + .unwrap(); + std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K) + }; + assert_eq !(slice[ONE_K - 1], 0x00); + slice[ONE_K - 1] = 0xFF; + assert_eq !(slice[ONE_K - 1], 0xFF); + + let slice : &mut[u8] = unsafe { + #[cfg(target_os = "linux")] + let mem = mremap(slice.as_mut_ptr() as * mut c_void, 10 * ONE_K, ONE_K, + MRemapFlags::empty(), None) + .unwrap(); + // Since we didn't supply MREMAP_MAYMOVE, the address should be the + // same. + #[cfg(target_os = "netbsd")] + let mem = mremap(slice.as_mut_ptr() as * mut c_void, 10 * ONE_K, ONE_K, + MRemapFlags::MAP_FIXED, None) + .unwrap(); + assert_eq !(mem, slice.as_mut_ptr() as * mut c_void); + std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K) + }; + + // The first KB should still be accessible and have the old data in it. + assert_eq !(slice[ONE_K - 1], 0xFF); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_pthread.rs b/vendor/nix-v0.23.1-patched/test/sys/test_pthread.rs new file mode 100644 index 000000000..fa9b510e8 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_pthread.rs @@ -0,0 +1,22 @@ +use nix::sys::pthread::*; + +#[cfg(any(target_env = "musl", target_os = "redox"))] +#[test] +fn test_pthread_self() { + let tid = pthread_self(); + assert!(tid != ::std::ptr::null_mut()); +} + +#[cfg(not(any(target_env = "musl", target_os = "redox")))] +#[test] +fn test_pthread_self() { + let tid = pthread_self(); + assert!(tid != 0); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_pthread_kill_none() { + pthread_kill(pthread_self(), None) + .expect("Should be able to send signal to my thread."); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_ptrace.rs b/vendor/nix-v0.23.1-patched/test/sys/test_ptrace.rs new file mode 100644 index 000000000..1c72e7c2e --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_ptrace.rs @@ -0,0 +1,219 @@ +use nix::errno::Errno; +use nix::unistd::getpid; +use nix::sys::ptrace; +#[cfg(any(target_os = "android", target_os = "linux"))] +use nix::sys::ptrace::Options; + +#[cfg(any(target_os = "android", target_os = "linux"))] +use std::mem; + +use crate::*; + +#[test] +fn test_ptrace() { + // Just make sure ptrace can be called at all, for now. + // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS + require_capability!("test_ptrace", CAP_SYS_PTRACE); + let err = ptrace::attach(getpid()).unwrap_err(); + assert!(err == Errno::EPERM || err == Errno::EINVAL || + err == Errno::ENOSYS); +} + +// Just make sure ptrace_setoptions can be called at all, for now. +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptrace_setoptions() { + require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE); + let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD).unwrap_err(); + assert!(err != Errno::EOPNOTSUPP); +} + +// Just make sure ptrace_getevent can be called at all, for now. +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptrace_getevent() { + require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE); + let err = ptrace::getevent(getpid()).unwrap_err(); + assert!(err != Errno::EOPNOTSUPP); +} + +// Just make sure ptrace_getsiginfo can be called at all, for now. +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptrace_getsiginfo() { + require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE); + if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) { + panic!("ptrace_getsiginfo returns Errno::EOPNOTSUPP!"); + } +} + +// Just make sure ptrace_setsiginfo can be called at all, for now. +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptrace_setsiginfo() { + require_capability!("test_ptrace_setsiginfo", CAP_SYS_PTRACE); + let siginfo = unsafe { mem::zeroed() }; + if let Err(Errno::EOPNOTSUPP) = ptrace::setsiginfo(getpid(), &siginfo) { + panic!("ptrace_setsiginfo returns Errno::EOPNOTSUPP!"); + } +} + + +#[test] +fn test_ptrace_cont() { + use nix::sys::ptrace; + use nix::sys::signal::{raise, Signal}; + use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus}; + use nix::unistd::fork; + use nix::unistd::ForkResult::*; + + require_capability!("test_ptrace_cont", CAP_SYS_PTRACE); + + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // FIXME: qemu-user doesn't implement ptrace on all architectures + // and retunrs ENOSYS in this case. + // We (ab)use this behavior to detect the affected platforms + // and skip the test then. + // On valid platforms the ptrace call should return Errno::EPERM, this + // is already tested by `test_ptrace`. + let err = ptrace::attach(getpid()).unwrap_err(); + if err == Errno::ENOSYS { + return; + } + + match unsafe{fork()}.expect("Error: Fork Failed") { + Child => { + ptrace::traceme().unwrap(); + // As recommended by ptrace(2), raise SIGTRAP to pause the child + // until the parent is ready to continue + loop { + raise(Signal::SIGTRAP).unwrap(); + } + + }, + Parent { child } => { + assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))); + ptrace::cont(child, None).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))); + ptrace::cont(child, Some(Signal::SIGKILL)).unwrap(); + match waitpid(child, None) { + Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => { + // FIXME It's been observed on some systems (apple) the + // tracee may not be killed but remain as a zombie process + // affecting other wait based tests. Add an extra kill just + // to make sure there are no zombies. + let _ = waitpid(child, Some(WaitPidFlag::WNOHANG)); + while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() { + let _ = waitpid(child, Some(WaitPidFlag::WNOHANG)); + } + } + _ => panic!("The process should have been killed"), + } + }, + } +} + +#[cfg(target_os = "linux")] +#[test] +fn test_ptrace_interrupt() { + use nix::sys::ptrace; + use nix::sys::signal::Signal; + use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus}; + use nix::unistd::fork; + use nix::unistd::ForkResult::*; + use std::thread::sleep; + use std::time::Duration; + + require_capability!("test_ptrace_interrupt", CAP_SYS_PTRACE); + + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + match unsafe{fork()}.expect("Error: Fork Failed") { + Child => { + loop { + sleep(Duration::from_millis(1000)); + } + + }, + Parent { child } => { + ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap(); + ptrace::interrupt(child).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128))); + ptrace::syscall(child, None).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + ptrace::detach(child, Some(Signal::SIGKILL)).unwrap(); + match waitpid(child, None) { + Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => { + let _ = waitpid(child, Some(WaitPidFlag::WNOHANG)); + while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() { + let _ = waitpid(child, Some(WaitPidFlag::WNOHANG)); + } + } + _ => panic!("The process should have been killed"), + } + }, + } +} + +// ptrace::{setoptions, getregs} are only available in these platforms +#[cfg(all(target_os = "linux", + any(target_arch = "x86_64", + target_arch = "x86"), + target_env = "gnu"))] +#[test] +fn test_ptrace_syscall() { + use nix::sys::signal::kill; + use nix::sys::ptrace; + use nix::sys::signal::Signal; + use nix::sys::wait::{waitpid, WaitStatus}; + use nix::unistd::fork; + use nix::unistd::getpid; + use nix::unistd::ForkResult::*; + + require_capability!("test_ptrace_syscall", CAP_SYS_PTRACE); + + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + match unsafe{fork()}.expect("Error: Fork Failed") { + Child => { + ptrace::traceme().unwrap(); + // first sigstop until parent is ready to continue + let pid = getpid(); + kill(pid, Signal::SIGSTOP).unwrap(); + kill(pid, Signal::SIGTERM).unwrap(); + unsafe { ::libc::_exit(0); } + }, + + Parent { child } => { + assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGSTOP))); + + // set this option to recognize syscall-stops + ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap(); + + #[cfg(target_arch = "x86_64")] + let get_syscall_id = || ptrace::getregs(child).unwrap().orig_rax as libc::c_long; + + #[cfg(target_arch = "x86")] + let get_syscall_id = || ptrace::getregs(child).unwrap().orig_eax as libc::c_long; + + // kill entry + ptrace::syscall(child, None).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + assert_eq!(get_syscall_id(), ::libc::SYS_kill); + + // kill exit + ptrace::syscall(child, None).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + assert_eq!(get_syscall_id(), ::libc::SYS_kill); + + // receive signal + ptrace::syscall(child, None).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTERM))); + + // inject signal + ptrace::syscall(child, Signal::SIGTERM).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false))); + }, + } +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_select.rs b/vendor/nix-v0.23.1-patched/test/sys/test_select.rs new file mode 100644 index 000000000..db0794561 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_select.rs @@ -0,0 +1,82 @@ +use nix::sys::select::*; +use nix::unistd::{pipe, write}; +use nix::sys::signal::SigSet; +use nix::sys::time::{TimeSpec, TimeValLike}; + +#[test] +pub fn test_pselect() { + let _mtx = crate::SIGNAL_MTX + .lock() + .expect("Mutex got poisoned by another test"); + + let (r1, w1) = pipe().unwrap(); + write(w1, b"hi!").unwrap(); + let (r2, _w2) = pipe().unwrap(); + + let mut fd_set = FdSet::new(); + fd_set.insert(r1); + fd_set.insert(r2); + + let timeout = TimeSpec::seconds(10); + let sigmask = SigSet::empty(); + assert_eq!( + 1, + pselect(None, &mut fd_set, None, None, &timeout, &sigmask).unwrap() + ); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); +} + +#[test] +pub fn test_pselect_nfds2() { + let (r1, w1) = pipe().unwrap(); + write(w1, b"hi!").unwrap(); + let (r2, _w2) = pipe().unwrap(); + + let mut fd_set = FdSet::new(); + fd_set.insert(r1); + fd_set.insert(r2); + + let timeout = TimeSpec::seconds(10); + assert_eq!( + 1, + pselect( + ::std::cmp::max(r1, r2) + 1, + &mut fd_set, + None, + None, + &timeout, + None + ).unwrap() + ); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); +} + +macro_rules! generate_fdset_bad_fd_tests { + ($fd:expr, $($method:ident),* $(,)?) => { + $( + #[test] + #[should_panic] + fn $method() { + FdSet::new().$method($fd); + } + )* + } +} + +mod test_fdset_negative_fd { + use super::*; + generate_fdset_bad_fd_tests!(-1, insert, remove, contains); +} + +mod test_fdset_too_large_fd { + use super::*; + use std::convert::TryInto; + generate_fdset_bad_fd_tests!( + FD_SETSIZE.try_into().unwrap(), + insert, + remove, + contains, + ); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_signal.rs b/vendor/nix-v0.23.1-patched/test/sys/test_signal.rs new file mode 100644 index 000000000..1b89af573 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_signal.rs @@ -0,0 +1,121 @@ +#[cfg(not(target_os = "redox"))] +use nix::errno::Errno; +use nix::sys::signal::*; +use nix::unistd::*; +use std::convert::TryFrom; +use std::sync::atomic::{AtomicBool, Ordering}; + +#[test] +fn test_kill_none() { + kill(getpid(), None).expect("Should be able to send signal to myself."); +} + +#[test] +#[cfg(not(target_os = "fuchsia"))] +fn test_killpg_none() { + killpg(getpgrp(), None) + .expect("Should be able to send signal to my process group."); +} + +#[test] +fn test_old_sigaction_flags() { + let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + extern "C" fn handler(_: ::libc::c_int) {} + let act = SigAction::new( + SigHandler::Handler(handler), + SaFlags::empty(), + SigSet::empty(), + ); + let oact = unsafe { sigaction(SIGINT, &act) }.unwrap(); + let _flags = oact.flags(); + let oact = unsafe { sigaction(SIGINT, &act) }.unwrap(); + let _flags = oact.flags(); +} + +#[test] +fn test_sigprocmask_noop() { + sigprocmask(SigmaskHow::SIG_BLOCK, None, None) + .expect("this should be an effective noop"); +} + +#[test] +fn test_sigprocmask() { + let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + // This needs to be a signal that rust doesn't use in the test harness. + const SIGNAL: Signal = Signal::SIGCHLD; + + let mut old_signal_set = SigSet::empty(); + sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set)) + .expect("expect to be able to retrieve old signals"); + + // Make sure the old set doesn't contain the signal, otherwise the following + // test don't make sense. + assert!(!old_signal_set.contains(SIGNAL), + "the {:?} signal is already blocked, please change to a \ + different one", SIGNAL); + + // Now block the signal. + let mut signal_set = SigSet::empty(); + signal_set.add(SIGNAL); + sigprocmask(SigmaskHow::SIG_BLOCK, Some(&signal_set), None) + .expect("expect to be able to block signals"); + + // And test it again, to make sure the change was effective. + old_signal_set.clear(); + sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set)) + .expect("expect to be able to retrieve old signals"); + assert!(old_signal_set.contains(SIGNAL), + "expected the {:?} to be blocked", SIGNAL); + + // Reset the signal. + sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None) + .expect("expect to be able to block signals"); +} + +lazy_static! { + static ref SIGNALED: AtomicBool = AtomicBool::new(false); +} + +extern fn test_sigaction_handler(signal: libc::c_int) { + let signal = Signal::try_from(signal).unwrap(); + SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed); +} + +#[cfg(not(target_os = "redox"))] +extern fn test_sigaction_action(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void) {} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_signal_sigaction() { + let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + let action_handler = SigHandler::SigAction(test_sigaction_action); + assert_eq!(unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(), Errno::ENOTSUP); +} + +#[test] +fn test_signal() { + let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap(); + raise(Signal::SIGINT).unwrap(); + assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), SigHandler::SigIgn); + + let handler = SigHandler::Handler(test_sigaction_handler); + assert_eq!(unsafe { signal(Signal::SIGINT, handler) }.unwrap(), SigHandler::SigDfl); + raise(Signal::SIGINT).unwrap(); + assert!(SIGNALED.load(Ordering::Relaxed)); + + #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] + assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), handler); + + // System V based OSes (e.g. illumos and Solaris) always resets the + // disposition to SIG_DFL prior to calling the signal handler + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), SigHandler::SigDfl); + + // Restore default signal handler + unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_signalfd.rs b/vendor/nix-v0.23.1-patched/test/sys/test_signalfd.rs new file mode 100644 index 000000000..af04c2228 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_signalfd.rs @@ -0,0 +1,27 @@ +use std::convert::TryFrom; + +#[test] +fn test_signalfd() { + use nix::sys::signalfd::SignalFd; + use nix::sys::signal::{self, raise, Signal, SigSet}; + + // Grab the mutex for altering signals so we don't interfere with other tests. + let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + // Block the SIGUSR1 signal from automatic processing for this thread + let mut mask = SigSet::empty(); + mask.add(signal::SIGUSR1); + mask.thread_block().unwrap(); + + let mut fd = SignalFd::new(&mask).unwrap(); + + // Send a SIGUSR1 signal to the current process. Note that this uses `raise` instead of `kill` + // because `kill` with `getpid` isn't correct during multi-threaded execution like during a + // cargo test session. Instead use `raise` which does the correct thing by default. + raise(signal::SIGUSR1).expect("Error: raise(SIGUSR1) failed"); + + // And now catch that same signal. + let res = fd.read_signal().unwrap().unwrap(); + let signo = Signal::try_from(res.ssi_signo as i32).unwrap(); + assert_eq!(signo, signal::SIGUSR1); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_socket.rs b/vendor/nix-v0.23.1-patched/test/sys/test_socket.rs new file mode 100644 index 000000000..0f6fac666 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_socket.rs @@ -0,0 +1,1941 @@ +use nix::sys::socket::{AddressFamily, InetAddr, SockAddr, UnixAddr, getsockname, sockaddr, sockaddr_in6, sockaddr_storage_to_addr}; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; +use std::mem::{self, MaybeUninit}; +use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV6}; +use std::os::unix::io::RawFd; +use std::path::Path; +use std::slice; +use std::str::FromStr; +use libc::{c_char, sockaddr_storage}; +#[cfg(any(target_os = "linux", target_os= "android"))] +use crate::*; + +#[test] +pub fn test_inetv4_addr_to_sock_addr() { + let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap(); + let addr = InetAddr::from_std(&actual); + + match addr { + InetAddr::V4(addr) => { + let ip: u32 = 0x7f00_0001; + let port: u16 = 3000; + let saddr = addr.sin_addr.s_addr; + + assert_eq!(saddr, ip.to_be()); + assert_eq!(addr.sin_port, port.to_be()); + } + _ => panic!("nope"), + } + + assert_eq!(addr.to_string(), "127.0.0.1:3000"); + + let inet = addr.to_std(); + assert_eq!(actual, inet); +} + +#[test] +pub fn test_inetv4_addr_roundtrip_sockaddr_storage_to_addr() { + let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap(); + let addr = InetAddr::from_std(&actual); + let sockaddr = SockAddr::new_inet(addr); + + let (storage, ffi_size) = { + let mut storage = MaybeUninit::::zeroed(); + let storage_ptr = storage.as_mut_ptr().cast::(); + let (ffi_ptr, ffi_size) = sockaddr.as_ffi_pair(); + assert_eq!(mem::size_of::(), ffi_size as usize); + unsafe { + storage_ptr.copy_from_nonoverlapping(ffi_ptr as *const sockaddr, 1); + (storage.assume_init(), ffi_size) + } + }; + + let from_storage = sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap(); + assert_eq!(from_storage, sockaddr); + let from_storage = sockaddr_storage_to_addr(&storage, mem::size_of::()).unwrap(); + assert_eq!(from_storage, sockaddr); +} + +#[test] +pub fn test_inetv6_addr_to_sock_addr() { + let port: u16 = 3000; + let flowinfo: u32 = 1; + let scope_id: u32 = 2; + let ip: Ipv6Addr = "fe80::1".parse().unwrap(); + + let actual = SocketAddr::V6(SocketAddrV6::new(ip, port, flowinfo, scope_id)); + let addr = InetAddr::from_std(&actual); + + match addr { + InetAddr::V6(addr) => { + assert_eq!(addr.sin6_port, port.to_be()); + assert_eq!(addr.sin6_flowinfo, flowinfo); + assert_eq!(addr.sin6_scope_id, scope_id); + } + _ => panic!("nope"), + } + + assert_eq!(actual, addr.to_std()); +} +#[test] +pub fn test_inetv6_addr_roundtrip_sockaddr_storage_to_addr() { + let port: u16 = 3000; + let flowinfo: u32 = 1; + let scope_id: u32 = 2; + let ip: Ipv6Addr = "fe80::1".parse().unwrap(); + + let actual = SocketAddr::V6(SocketAddrV6::new(ip, port, flowinfo, scope_id)); + let addr = InetAddr::from_std(&actual); + let sockaddr = SockAddr::new_inet(addr); + + let (storage, ffi_size) = { + let mut storage = MaybeUninit::::zeroed(); + let storage_ptr = storage.as_mut_ptr().cast::(); + let (ffi_ptr, ffi_size) = sockaddr.as_ffi_pair(); + assert_eq!(mem::size_of::(), ffi_size as usize); + unsafe { + storage_ptr.copy_from_nonoverlapping((ffi_ptr as *const sockaddr).cast::(), 1); + (storage.assume_init(), ffi_size) + } + }; + + let from_storage = sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap(); + assert_eq!(from_storage, sockaddr); + let from_storage = sockaddr_storage_to_addr(&storage, mem::size_of::()).unwrap(); + assert_eq!(from_storage, sockaddr); +} + +#[test] +pub fn test_path_to_sock_addr() { + let path = "/foo/bar"; + let actual = Path::new(path); + let addr = UnixAddr::new(actual).unwrap(); + + let expect: &[c_char] = unsafe { + slice::from_raw_parts(path.as_ptr() as *const c_char, path.len()) + }; + assert_eq!(unsafe { &(*addr.as_ptr()).sun_path[..8] }, expect); + + assert_eq!(addr.path(), Some(actual)); +} + +fn calculate_hash(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} + +#[test] +pub fn test_addr_equality_path() { + let path = "/foo/bar"; + let actual = Path::new(path); + let addr1 = UnixAddr::new(actual).unwrap(); + let mut addr2 = addr1; + + unsafe { (*addr2.as_mut_ptr()).sun_path[10] = 127 }; + + assert_eq!(addr1, addr2); + assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +pub fn test_abstract_sun_path_too_long() { + let name = String::from("nix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0testttttnix\0abstract\0test\0make\0sure\0this\0is\0long\0enough"); + let addr = UnixAddr::new_abstract(name.as_bytes()); + assert!(addr.is_err()); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +pub fn test_addr_equality_abstract() { + let name = String::from("nix\0abstract\0test"); + let addr1 = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + let mut addr2 = addr1; + + assert_eq!(addr1, addr2); + assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); + + unsafe { (*addr2.as_mut_ptr()).sun_path[17] = 127 }; + assert_ne!(addr1, addr2); + assert_ne!(calculate_hash(&addr1), calculate_hash(&addr2)); +} + +// Test getting/setting abstract addresses (without unix socket creation) +#[cfg(target_os = "linux")] +#[test] +pub fn test_abstract_uds_addr() { + let empty = String::new(); + let addr = UnixAddr::new_abstract(empty.as_bytes()).unwrap(); + let sun_path: [u8; 0] = []; + assert_eq!(addr.as_abstract(), Some(&sun_path[..])); + + let name = String::from("nix\0abstract\0test"); + let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + let sun_path = [ + 110u8, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116 + ]; + assert_eq!(addr.as_abstract(), Some(&sun_path[..])); + assert_eq!(addr.path(), None); + + // Internally, name is null-prefixed (abstract namespace) + assert_eq!(unsafe { (*addr.as_ptr()).sun_path[0] }, 0); +} + +#[test] +pub fn test_getsockname() { + use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag}; + use nix::sys::socket::{bind, SockAddr}; + + let tempdir = tempfile::tempdir().unwrap(); + let sockname = tempdir.path().join("sock"); + let sock = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None) + .expect("socket failed"); + let sockaddr = SockAddr::new_unix(&sockname).unwrap(); + bind(sock, &sockaddr).expect("bind failed"); + assert_eq!(sockaddr, getsockname(sock).expect("getsockname failed")); +} + +#[test] +pub fn test_socketpair() { + use nix::unistd::{read, write}; + use nix::sys::socket::{socketpair, AddressFamily, SockType, SockFlag}; + + let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + write(fd1, b"hello").unwrap(); + let mut buf = [0;5]; + read(fd2, &mut buf).unwrap(); + + assert_eq!(&buf[..], b"hello"); +} + +mod recvfrom { + use nix::Result; + use nix::sys::socket::*; + use std::thread; + use super::*; + + const MSG: &[u8] = b"Hello, World!"; + + fn sendrecv(rsock: RawFd, ssock: RawFd, f_send: Fs, mut f_recv: Fr) -> Option + where + Fs: Fn(RawFd, &[u8], MsgFlags) -> Result + Send + 'static, + Fr: FnMut(usize, Option), + { + let mut buf: [u8; 13] = [0u8; 13]; + let mut l = 0; + let mut from = None; + + let send_thread = thread::spawn(move || { + let mut l = 0; + while l < std::mem::size_of_val(MSG) { + l += f_send(ssock, &MSG[l..], MsgFlags::empty()).unwrap(); + } + }); + + while l < std::mem::size_of_val(MSG) { + let (len, from_) = recvfrom(rsock, &mut buf[l..]).unwrap(); + f_recv(len, from_); + from = from_; + l += len; + } + assert_eq!(&buf, MSG); + send_thread.join().unwrap(); + from + } + + #[test] + pub fn stream() { + let (fd2, fd1) = socketpair(AddressFamily::Unix, SockType::Stream, + None, SockFlag::empty()).unwrap(); + // Ignore from for stream sockets + let _ = sendrecv(fd1, fd2, |s, m, flags| { + send(s, m, flags) + }, |_, _| {}); + } + + #[test] + pub fn udp() { + let std_sa = SocketAddr::from_str("127.0.0.1:6789").unwrap(); + let inet_addr = InetAddr::from_std(&std_sa); + let sock_addr = SockAddr::new_inet(inet_addr); + let rsock = socket(AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None + ).unwrap(); + bind(rsock, &sock_addr).unwrap(); + let ssock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + let from = sendrecv(rsock, ssock, move |s, m, flags| { + sendto(s, m, &sock_addr, flags) + },|_, _| {}); + // UDP sockets should set the from address + assert_eq!(AddressFamily::Inet, from.unwrap().family()); + } + + #[cfg(target_os = "linux")] + mod udp_offload { + use super::*; + use nix::sys::uio::IoVec; + use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment}; + + #[test] + // Disable the test under emulation because it fails in Cirrus-CI. Lack + // of QEMU support is suspected. + #[cfg_attr(qemu, ignore)] + pub fn gso() { + require_kernel_version!(udp_offload::gso, ">= 4.18"); + + // In this test, we send the data and provide a GSO segment size. + // Since we are sending the buffer of size 13, six UDP packets + // with size 2 and two UDP packet with size 1 will be sent. + let segment_size: u16 = 2; + + let std_sa = SocketAddr::from_str("127.0.0.1:6791").unwrap(); + let inet_addr = InetAddr::from_std(&std_sa); + let sock_addr = SockAddr::new_inet(inet_addr); + let rsock = socket(AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None + ).unwrap(); + + setsockopt(rsock, UdpGsoSegment, &(segment_size as _)) + .expect("setsockopt UDP_SEGMENT failed"); + + bind(rsock, &sock_addr).unwrap(); + let ssock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + + let mut num_packets_received: i32 = 0; + + sendrecv(rsock, ssock, move |s, m, flags| { + let iov = [IoVec::from_slice(m)]; + let cmsg = ControlMessage::UdpGsoSegments(&segment_size); + sendmsg(s, &iov, &[cmsg], flags, Some(&sock_addr)) + }, { + let num_packets_received_ref = &mut num_packets_received; + + move |len, _| { + // check that we receive UDP packets with payload size + // less or equal to segment size + assert!(len <= segment_size as usize); + *num_packets_received_ref += 1; + } + }); + + // Buffer size is 13, we will receive six packets of size 2, + // and one packet of size 1. + assert_eq!(7, num_packets_received); + } + + #[test] + // Disable the test on emulated platforms because it fails in Cirrus-CI. + // Lack of QEMU support is suspected. + #[cfg_attr(qemu, ignore)] + pub fn gro() { + require_kernel_version!(udp_offload::gro, ">= 5.3"); + + // It's hard to guarantee receiving GRO packets. Just checking + // that `setsockopt` doesn't fail with error + + let rsock = socket(AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None + ).unwrap(); + + setsockopt(rsock, UdpGroSegment, &true) + .expect("setsockopt UDP_GRO failed"); + } + } + + #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + ))] + #[test] + pub fn udp_sendmmsg() { + use nix::sys::uio::IoVec; + + let std_sa = SocketAddr::from_str("127.0.0.1:6793").unwrap(); + let std_sa2 = SocketAddr::from_str("127.0.0.1:6794").unwrap(); + let inet_addr = InetAddr::from_std(&std_sa); + let inet_addr2 = InetAddr::from_std(&std_sa2); + let sock_addr = SockAddr::new_inet(inet_addr); + let sock_addr2 = SockAddr::new_inet(inet_addr2); + + let rsock = socket(AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None + ).unwrap(); + bind(rsock, &sock_addr).unwrap(); + let ssock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + + let from = sendrecv(rsock, ssock, move |s, m, flags| { + let iov = [IoVec::from_slice(m)]; + let mut msgs = vec![ + SendMmsgData { + iov: &iov, + cmsgs: &[], + addr: Some(sock_addr), + _lt: Default::default(), + } + ]; + + let batch_size = 15; + + for _ in 0..batch_size { + msgs.push( + SendMmsgData { + iov: &iov, + cmsgs: &[], + addr: Some(sock_addr2), + _lt: Default::default(), + } + ); + } + sendmmsg(s, msgs.iter(), flags) + .map(move |sent_bytes| { + assert!(!sent_bytes.is_empty()); + for sent in &sent_bytes { + assert_eq!(*sent, m.len()); + } + sent_bytes.len() + }) + }, |_, _ | {}); + // UDP sockets should set the from address + assert_eq!(AddressFamily::Inet, from.unwrap().family()); + } + + #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + ))] + #[test] + pub fn udp_recvmmsg() { + use nix::sys::uio::IoVec; + use nix::sys::socket::{MsgFlags, recvmmsg}; + + const NUM_MESSAGES_SENT: usize = 2; + const DATA: [u8; 2] = [1,2]; + + let std_sa = SocketAddr::from_str("127.0.0.1:6798").unwrap(); + let inet_addr = InetAddr::from_std(&std_sa); + let sock_addr = SockAddr::new_inet(inet_addr); + + let rsock = socket(AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None + ).unwrap(); + bind(rsock, &sock_addr).unwrap(); + let ssock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + + let send_thread = thread::spawn(move || { + for _ in 0..NUM_MESSAGES_SENT { + sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty()).unwrap(); + } + }); + + let mut msgs = std::collections::LinkedList::new(); + + // Buffers to receive exactly `NUM_MESSAGES_SENT` messages + let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT]; + let iovs: Vec<_> = receive_buffers.iter_mut().map(|buf| { + [IoVec::from_mut_slice(&mut buf[..])] + }).collect(); + + for iov in &iovs { + msgs.push_back(RecvMmsgData { + iov, + cmsg_buffer: None, + }) + }; + + let res = recvmmsg(rsock, &mut msgs, MsgFlags::empty(), None).expect("recvmmsg"); + assert_eq!(res.len(), DATA.len()); + + for RecvMsg { address, bytes, .. } in res.into_iter() { + assert_eq!(AddressFamily::Inet, address.unwrap().family()); + assert_eq!(DATA.len(), bytes); + } + + for buf in &receive_buffers { + assert_eq!(&buf[..DATA.len()], DATA); + } + + send_thread.join().unwrap(); + } + + #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + ))] + #[test] + pub fn udp_recvmmsg_dontwait_short_read() { + use nix::sys::uio::IoVec; + use nix::sys::socket::{MsgFlags, recvmmsg}; + + const NUM_MESSAGES_SENT: usize = 2; + const DATA: [u8; 4] = [1,2,3,4]; + + let std_sa = SocketAddr::from_str("127.0.0.1:6799").unwrap(); + let inet_addr = InetAddr::from_std(&std_sa); + let sock_addr = SockAddr::new_inet(inet_addr); + + let rsock = socket(AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None + ).unwrap(); + bind(rsock, &sock_addr).unwrap(); + let ssock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + + let send_thread = thread::spawn(move || { + for _ in 0..NUM_MESSAGES_SENT { + sendto(ssock, &DATA[..], &sock_addr, MsgFlags::empty()).unwrap(); + } + }); + // Ensure we've sent all the messages before continuing so `recvmmsg` + // will return right away + send_thread.join().unwrap(); + + let mut msgs = std::collections::LinkedList::new(); + + // Buffers to receive >`NUM_MESSAGES_SENT` messages to ensure `recvmmsg` + // will return when there are fewer than requested messages in the + // kernel buffers when using `MSG_DONTWAIT`. + let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT + 2]; + let iovs: Vec<_> = receive_buffers.iter_mut().map(|buf| { + [IoVec::from_mut_slice(&mut buf[..])] + }).collect(); + + for iov in &iovs { + msgs.push_back(RecvMmsgData { + iov, + cmsg_buffer: None, + }) + }; + + let res = recvmmsg(rsock, &mut msgs, MsgFlags::MSG_DONTWAIT, None).expect("recvmmsg"); + assert_eq!(res.len(), NUM_MESSAGES_SENT); + + for RecvMsg { address, bytes, .. } in res.into_iter() { + assert_eq!(AddressFamily::Inet, address.unwrap().family()); + assert_eq!(DATA.len(), bytes); + } + + for buf in &receive_buffers[..NUM_MESSAGES_SENT] { + assert_eq!(&buf[..DATA.len()], DATA); + } + } +} + +// Test error handling of our recvmsg wrapper +#[test] +pub fn test_recvmsg_ebadf() { + use nix::errno::Errno; + use nix::sys::socket::{MsgFlags, recvmsg}; + use nix::sys::uio::IoVec; + + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let fd = -1; // Bad file descriptor + let r = recvmsg(fd, &iov, None, MsgFlags::empty()); + assert_eq!(r.err().unwrap(), Errno::EBADF); +} + +// Disable the test on emulated platforms due to a bug in QEMU versions < +// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808 +#[cfg_attr(qemu, ignore)] +#[test] +pub fn test_scm_rights() { + use nix::sys::uio::IoVec; + use nix::unistd::{pipe, read, write, close}; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, + AddressFamily, SockType, SockFlag, + ControlMessage, ControlMessageOwned, MsgFlags}; + + let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + let (r, w) = pipe().unwrap(); + let mut received_r: Option = None; + + { + let iov = [IoVec::from_slice(b"hello")]; + let fds = [r]; + let cmsg = ControlMessage::ScmRights(&fds); + assert_eq!(sendmsg(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5); + close(r).unwrap(); + close(fd1).unwrap(); + } + + { + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let mut cmsgspace = cmsg_space!([RawFd; 1]); + let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); + + for cmsg in msg.cmsgs() { + if let ControlMessageOwned::ScmRights(fd) = cmsg { + assert_eq!(received_r, None); + assert_eq!(fd.len(), 1); + received_r = Some(fd[0]); + } else { + panic!("unexpected cmsg"); + } + } + assert_eq!(msg.bytes, 5); + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + close(fd2).unwrap(); + } + + let received_r = received_r.expect("Did not receive passed fd"); + // Ensure that the received file descriptor works + write(w, b"world").unwrap(); + let mut buf = [0u8; 5]; + read(received_r, &mut buf).unwrap(); + assert_eq!(&buf[..], b"world"); + close(received_r).unwrap(); + close(w).unwrap(); +} + +// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross +#[cfg(any(target_os = "linux", target_os= "android"))] +#[cfg_attr(qemu, ignore)] +#[test] +pub fn test_af_alg_cipher() { + use nix::sys::uio::IoVec; + use nix::unistd::read; + use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt, + AddressFamily, SockType, SockFlag, SockAddr, + ControlMessage, MsgFlags}; + use nix::sys::socket::sockopt::AlgSetKey; + + skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352"); + // Travis's seccomp profile blocks AF_ALG + // https://docs.docker.com/engine/security/seccomp/ + skip_if_seccomp!(test_af_alg_cipher); + + let alg_type = "skcipher"; + let alg_name = "ctr-aes-aesni"; + // 256-bits secret key + let key = vec![0u8; 32]; + // 16-bytes IV + let iv_len = 16; + let iv = vec![1u8; iv_len]; + // 256-bytes plain payload + let payload_len = 256; + let payload = vec![2u8; payload_len]; + + let sock = socket(AddressFamily::Alg, SockType::SeqPacket, SockFlag::empty(), None) + .expect("socket failed"); + + let sockaddr = SockAddr::new_alg(alg_type, alg_name); + bind(sock, &sockaddr).expect("bind failed"); + + if let SockAddr::Alg(alg) = sockaddr { + assert_eq!(alg.alg_name().to_string_lossy(), alg_name); + assert_eq!(alg.alg_type().to_string_lossy(), alg_type); + } else { + panic!("unexpected SockAddr"); + } + + setsockopt(sock, AlgSetKey::default(), &key).expect("setsockopt"); + let session_socket = accept(sock).expect("accept failed"); + + let msgs = [ControlMessage::AlgSetOp(&libc::ALG_OP_ENCRYPT), ControlMessage::AlgSetIv(iv.as_slice())]; + let iov = IoVec::from_slice(&payload); + sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg encrypt"); + + // allocate buffer for encrypted data + let mut encrypted = vec![0u8; payload_len]; + let num_bytes = read(session_socket, &mut encrypted).expect("read encrypt"); + assert_eq!(num_bytes, payload_len); + + let iov = IoVec::from_slice(&encrypted); + + let iv = vec![1u8; iv_len]; + + let msgs = [ControlMessage::AlgSetOp(&libc::ALG_OP_DECRYPT), ControlMessage::AlgSetIv(iv.as_slice())]; + sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg decrypt"); + + // allocate buffer for decrypted data + let mut decrypted = vec![0u8; payload_len]; + let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt"); + + assert_eq!(num_bytes, payload_len); + assert_eq!(decrypted, payload); +} + +// Disable the test on emulated platforms due to not enabled support of AF_ALG +// in QEMU from rust cross +#[cfg(any(target_os = "linux", target_os= "android"))] +#[cfg_attr(qemu, ignore)] +#[test] +pub fn test_af_alg_aead() { + use libc::{ALG_OP_DECRYPT, ALG_OP_ENCRYPT}; + use nix::fcntl::{fcntl, FcntlArg, OFlag}; + use nix::sys::uio::IoVec; + use nix::unistd::{read, close}; + use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt, + AddressFamily, SockType, SockFlag, SockAddr, + ControlMessage, MsgFlags}; + use nix::sys::socket::sockopt::{AlgSetKey, AlgSetAeadAuthSize}; + + skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352"); + // Travis's seccomp profile blocks AF_ALG + // https://docs.docker.com/engine/security/seccomp/ + skip_if_seccomp!(test_af_alg_aead); + + let auth_size = 4usize; + let assoc_size = 16u32; + + let alg_type = "aead"; + let alg_name = "gcm(aes)"; + // 256-bits secret key + let key = vec![0u8; 32]; + // 12-bytes IV + let iv_len = 12; + let iv = vec![1u8; iv_len]; + // 256-bytes plain payload + let payload_len = 256; + let mut payload = vec![2u8; payload_len + (assoc_size as usize) + auth_size]; + + for i in 0..assoc_size { + payload[i as usize] = 10; + } + + let len = payload.len(); + + for i in 0..auth_size { + payload[len - 1 - i] = 0; + } + + let sock = socket(AddressFamily::Alg, SockType::SeqPacket, SockFlag::empty(), None) + .expect("socket failed"); + + let sockaddr = SockAddr::new_alg(alg_type, alg_name); + bind(sock, &sockaddr).expect("bind failed"); + + setsockopt(sock, AlgSetAeadAuthSize, &auth_size).expect("setsockopt AlgSetAeadAuthSize"); + setsockopt(sock, AlgSetKey::default(), &key).expect("setsockopt AlgSetKey"); + let session_socket = accept(sock).expect("accept failed"); + + let msgs = [ + ControlMessage::AlgSetOp(&ALG_OP_ENCRYPT), + ControlMessage::AlgSetIv(iv.as_slice()), + ControlMessage::AlgSetAeadAssoclen(&assoc_size)]; + let iov = IoVec::from_slice(&payload); + sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg encrypt"); + + // allocate buffer for encrypted data + let mut encrypted = vec![0u8; (assoc_size as usize) + payload_len + auth_size]; + let num_bytes = read(session_socket, &mut encrypted).expect("read encrypt"); + assert_eq!(num_bytes, payload_len + auth_size + (assoc_size as usize)); + close(session_socket).expect("close"); + + for i in 0..assoc_size { + encrypted[i as usize] = 10; + } + + let iov = IoVec::from_slice(&encrypted); + + let iv = vec![1u8; iv_len]; + + let session_socket = accept(sock).expect("accept failed"); + + let msgs = [ + ControlMessage::AlgSetOp(&ALG_OP_DECRYPT), + ControlMessage::AlgSetIv(iv.as_slice()), + ControlMessage::AlgSetAeadAssoclen(&assoc_size), + ]; + sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg decrypt"); + + // allocate buffer for decrypted data + let mut decrypted = vec![0u8; payload_len + (assoc_size as usize) + auth_size]; + // Starting with kernel 4.9, the interface changed slightly such that the + // authentication tag memory is only needed in the output buffer for encryption + // and in the input buffer for decryption. + // Do not block on read, as we may have fewer bytes than buffer size + fcntl(session_socket,FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).expect("fcntl non_blocking"); + let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt"); + + assert!(num_bytes >= payload_len + (assoc_size as usize)); + assert_eq!(decrypted[(assoc_size as usize)..(payload_len + (assoc_size as usize))], payload[(assoc_size as usize)..payload_len + (assoc_size as usize)]); +} + +// Verify `ControlMessage::Ipv4PacketInfo` for `sendmsg`. +// This creates a (udp) socket bound to localhost, then sends a message to +// itself but uses Ipv4PacketInfo to force the source address to be localhost. +// +// This would be a more interesting test if we could assume that the test host +// has more than one IP address (since we could select a different address to +// test from). +#[cfg(any(target_os = "linux", + target_os = "macos", + target_os = "netbsd"))] +#[test] +pub fn test_sendmsg_ipv4packetinfo() { + use cfg_if::cfg_if; + use nix::sys::uio::IoVec; + use nix::sys::socket::{socket, sendmsg, bind, + AddressFamily, SockType, SockFlag, SockAddr, + ControlMessage, MsgFlags}; + + let sock = socket(AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None) + .expect("socket failed"); + + let std_sa = SocketAddr::from_str("127.0.0.1:4000").unwrap(); + let inet_addr = InetAddr::from_std(&std_sa); + let sock_addr = SockAddr::new_inet(inet_addr); + + bind(sock, &sock_addr).expect("bind failed"); + + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&slice)]; + + if let InetAddr::V4(sin) = inet_addr { + cfg_if! { + if #[cfg(target_os = "netbsd")] { + let _dontcare = sin; + let pi = libc::in_pktinfo { + ipi_ifindex: 0, /* Unspecified interface */ + ipi_addr: libc::in_addr { s_addr: 0 }, + }; + } else { + let pi = libc::in_pktinfo { + ipi_ifindex: 0, /* Unspecified interface */ + ipi_addr: libc::in_addr { s_addr: 0 }, + ipi_spec_dst: sin.sin_addr, + }; + } + } + + let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)]; + + sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr)) + .expect("sendmsg"); + } else { + panic!("No IPv4 addresses available for testing?"); + } +} + +// Verify `ControlMessage::Ipv6PacketInfo` for `sendmsg`. +// This creates a (udp) socket bound to ip6-localhost, then sends a message to +// itself but uses Ipv6PacketInfo to force the source address to be +// ip6-localhost. +// +// This would be a more interesting test if we could assume that the test host +// has more than one IP address (since we could select a different address to +// test from). +#[cfg(any(target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "freebsd"))] +#[test] +pub fn test_sendmsg_ipv6packetinfo() { + use nix::errno::Errno; + use nix::sys::uio::IoVec; + use nix::sys::socket::{socket, sendmsg, bind, + AddressFamily, SockType, SockFlag, SockAddr, + ControlMessage, MsgFlags}; + + let sock = socket(AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None) + .expect("socket failed"); + + let std_sa = SocketAddr::from_str("[::1]:6000").unwrap(); + let inet_addr = InetAddr::from_std(&std_sa); + let sock_addr = SockAddr::new_inet(inet_addr); + + if let Err(Errno::EADDRNOTAVAIL) = bind(sock, &sock_addr) { + println!("IPv6 not available, skipping test."); + return; + } + + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&slice)]; + + if let InetAddr::V6(sin) = inet_addr { + let pi = libc::in6_pktinfo { + ipi6_ifindex: 0, /* Unspecified interface */ + ipi6_addr: sin.sin6_addr, + }; + + let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)]; + + sendmsg(sock, &iov, &cmsg, MsgFlags::empty(), Some(&sock_addr)) + .expect("sendmsg"); + } else { + println!("No IPv6 addresses available for testing: skipping testing Ipv6PacketInfo"); + } +} + +/// Tests that passing multiple fds using a single `ControlMessage` works. +// Disable the test on emulated platforms due to a bug in QEMU versions < +// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808 +#[cfg_attr(qemu, ignore)] +#[test] +fn test_scm_rights_single_cmsg_multiple_fds() { + use std::os::unix::net::UnixDatagram; + use std::os::unix::io::{RawFd, AsRawFd}; + use std::thread; + use nix::sys::socket::{ControlMessage, ControlMessageOwned, MsgFlags, + sendmsg, recvmsg}; + use nix::sys::uio::IoVec; + + let (send, receive) = UnixDatagram::pair().unwrap(); + let thread = thread::spawn(move || { + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut space = cmsg_space!([RawFd; 2]); + let msg = recvmsg( + receive.as_raw_fd(), + &iovec, + Some(&mut space), + MsgFlags::empty() + ).unwrap(); + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + + let mut cmsgs = msg.cmsgs(); + match cmsgs.next() { + Some(ControlMessageOwned::ScmRights(fds)) => { + assert_eq!(fds.len(), 2, + "unexpected fd count (expected 2 fds, got {})", + fds.len()); + }, + _ => panic!(), + } + assert!(cmsgs.next().is_none(), "unexpected control msg"); + + assert_eq!(msg.bytes, 8); + assert_eq!(iovec[0].as_slice(), [1u8, 2, 3, 4, 5, 6, 7, 8]); + }); + + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&slice)]; + let fds = [libc::STDIN_FILENO, libc::STDOUT_FILENO]; // pass stdin and stdout + let cmsg = [ControlMessage::ScmRights(&fds)]; + sendmsg(send.as_raw_fd(), &iov, &cmsg, MsgFlags::empty(), None).unwrap(); + thread.join().unwrap(); +} + +// Verify `sendmsg` builds a valid `msghdr` when passing an empty +// `cmsgs` argument. This should result in a msghdr with a nullptr +// msg_control field and a msg_controllen of 0 when calling into the +// raw `sendmsg`. +#[test] +pub fn test_sendmsg_empty_cmsgs() { + use nix::sys::uio::IoVec; + use nix::unistd::close; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, + AddressFamily, SockType, SockFlag, MsgFlags}; + + let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + + { + let iov = [IoVec::from_slice(b"hello")]; + assert_eq!(sendmsg(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(), 5); + close(fd1).unwrap(); + } + + { + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let mut cmsgspace = cmsg_space!([RawFd; 1]); + let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); + + for _ in msg.cmsgs() { + panic!("unexpected cmsg"); + } + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + assert_eq!(msg.bytes, 5); + close(fd2).unwrap(); + } +} + +#[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", +))] +#[test] +fn test_scm_credentials() { + use nix::sys::uio::IoVec; + use nix::unistd::{close, getpid, getuid, getgid}; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, + AddressFamily, SockType, SockFlag, + ControlMessage, ControlMessageOwned, MsgFlags, + UnixCredentials}; + #[cfg(any(target_os = "android", target_os = "linux"))] + use nix::sys::socket::{setsockopt, sockopt::PassCred}; + + let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + #[cfg(any(target_os = "android", target_os = "linux"))] + setsockopt(recv, PassCred, &true).unwrap(); + + { + let iov = [IoVec::from_slice(b"hello")]; + #[cfg(any(target_os = "android", target_os = "linux"))] + let cred = UnixCredentials::new(); + #[cfg(any(target_os = "android", target_os = "linux"))] + let cmsg = ControlMessage::ScmCredentials(&cred); + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + let cmsg = ControlMessage::ScmCreds; + assert_eq!(sendmsg(send, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5); + close(send).unwrap(); + } + + { + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let mut cmsgspace = cmsg_space!(UnixCredentials); + let msg = recvmsg(recv, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); + let mut received_cred = None; + + for cmsg in msg.cmsgs() { + let cred = match cmsg { + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessageOwned::ScmCredentials(cred) => cred, + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + ControlMessageOwned::ScmCreds(cred) => cred, + other => panic!("unexpected cmsg {:?}", other), + }; + assert!(received_cred.is_none()); + assert_eq!(cred.pid(), getpid().as_raw()); + assert_eq!(cred.uid(), getuid().as_raw()); + assert_eq!(cred.gid(), getgid().as_raw()); + received_cred = Some(cred); + } + received_cred.expect("no creds received"); + assert_eq!(msg.bytes, 5); + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + close(recv).unwrap(); + } +} + +/// Ensure that we can send `SCM_CREDENTIALS` and `SCM_RIGHTS` with a single +/// `sendmsg` call. +#[cfg(any(target_os = "android", target_os = "linux"))] +// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation +// see https://bugs.launchpad.net/qemu/+bug/1781280 +#[cfg_attr(qemu, ignore)] +#[test] +fn test_scm_credentials_and_rights() { + let space = cmsg_space!(libc::ucred, RawFd); + test_impl_scm_credentials_and_rights(space); +} + +/// Ensure that passing a an oversized control message buffer to recvmsg +/// still works. +#[cfg(any(target_os = "android", target_os = "linux"))] +// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation +// see https://bugs.launchpad.net/qemu/+bug/1781280 +#[cfg_attr(qemu, ignore)] +#[test] +fn test_too_large_cmsgspace() { + let space = vec![0u8; 1024]; + test_impl_scm_credentials_and_rights(space); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_impl_scm_credentials_and_rights(mut space: Vec) { + use libc::ucred; + use nix::sys::uio::IoVec; + use nix::unistd::{pipe, write, close, getpid, getuid, getgid}; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt, + SockType, SockFlag, + ControlMessage, ControlMessageOwned, MsgFlags}; + use nix::sys::socket::sockopt::PassCred; + + let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + setsockopt(recv, PassCred, &true).unwrap(); + + let (r, w) = pipe().unwrap(); + let mut received_r: Option = None; + + { + let iov = [IoVec::from_slice(b"hello")]; + let cred = ucred { + pid: getpid().as_raw(), + uid: getuid().as_raw(), + gid: getgid().as_raw(), + }.into(); + let fds = [r]; + let cmsgs = [ + ControlMessage::ScmCredentials(&cred), + ControlMessage::ScmRights(&fds), + ]; + assert_eq!(sendmsg(send, &iov, &cmsgs, MsgFlags::empty(), None).unwrap(), 5); + close(r).unwrap(); + close(send).unwrap(); + } + + { + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let msg = recvmsg(recv, &iov, Some(&mut space), MsgFlags::empty()).unwrap(); + let mut received_cred = None; + + assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs"); + + for cmsg in msg.cmsgs() { + match cmsg { + ControlMessageOwned::ScmRights(fds) => { + assert_eq!(received_r, None, "already received fd"); + assert_eq!(fds.len(), 1); + received_r = Some(fds[0]); + } + ControlMessageOwned::ScmCredentials(cred) => { + assert!(received_cred.is_none()); + assert_eq!(cred.pid(), getpid().as_raw()); + assert_eq!(cred.uid(), getuid().as_raw()); + assert_eq!(cred.gid(), getgid().as_raw()); + received_cred = Some(cred); + } + _ => panic!("unexpected cmsg"), + } + } + received_cred.expect("no creds received"); + assert_eq!(msg.bytes, 5); + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + close(recv).unwrap(); + } + + let received_r = received_r.expect("Did not receive passed fd"); + // Ensure that the received file descriptor works + write(w, b"world").unwrap(); + let mut buf = [0u8; 5]; + read(received_r, &mut buf).unwrap(); + assert_eq!(&buf[..], b"world"); + close(received_r).unwrap(); + close(w).unwrap(); +} + +// Test creating and using named unix domain sockets +#[test] +pub fn test_unixdomain() { + use nix::sys::socket::{SockType, SockFlag}; + use nix::sys::socket::{bind, socket, connect, listen, accept, SockAddr}; + use nix::unistd::{read, write, close}; + use std::thread; + + let tempdir = tempfile::tempdir().unwrap(); + let sockname = tempdir.path().join("sock"); + let s1 = socket(AddressFamily::Unix, SockType::Stream, + SockFlag::empty(), None).expect("socket failed"); + let sockaddr = SockAddr::new_unix(&sockname).unwrap(); + bind(s1, &sockaddr).expect("bind failed"); + listen(s1, 10).expect("listen failed"); + + let thr = thread::spawn(move || { + let s2 = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None) + .expect("socket failed"); + connect(s2, &sockaddr).expect("connect failed"); + write(s2, b"hello").expect("write failed"); + close(s2).unwrap(); + }); + + let s3 = accept(s1).expect("accept failed"); + + let mut buf = [0;5]; + read(s3, &mut buf).unwrap(); + close(s3).unwrap(); + close(s1).unwrap(); + thr.join().unwrap(); + + assert_eq!(&buf[..], b"hello"); +} + +// Test creating and using named system control sockets +#[cfg(any(target_os = "macos", target_os = "ios"))] +#[test] +pub fn test_syscontrol() { + use nix::errno::Errno; + use nix::sys::socket::{socket, SockAddr, SockType, SockFlag, SockProtocol}; + + let fd = socket(AddressFamily::System, SockType::Datagram, + SockFlag::empty(), SockProtocol::KextControl) + .expect("socket failed"); + let _sockaddr = SockAddr::new_sys_control(fd, "com.apple.net.utun_control", 0).expect("resolving sys_control name failed"); + assert_eq!(SockAddr::new_sys_control(fd, "foo.bar.lol", 0).err(), Some(Errno::ENOENT)); + + // requires root privileges + // connect(fd, &sockaddr).expect("connect failed"); +} + +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +))] +fn loopback_address(family: AddressFamily) -> Option { + use std::io; + use std::io::Write; + use nix::ifaddrs::getifaddrs; + use nix::net::if_::*; + + let addrs = match getifaddrs() { + Ok(iter) => iter, + Err(e) => { + let stdioerr = io::stderr(); + let mut handle = stdioerr.lock(); + writeln!(handle, "getifaddrs: {:?}", e).unwrap(); + return None; + }, + }; + // return first address matching family + for ifaddr in addrs { + if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) { + match ifaddr.address { + Some(SockAddr::Inet(InetAddr::V4(..))) => { + match family { + AddressFamily::Inet => return Some(ifaddr), + _ => continue + } + }, + Some(SockAddr::Inet(InetAddr::V6(..))) => { + match family { + AddressFamily::Inet6 => return Some(ifaddr), + _ => continue + } + }, + _ => continue, + } + } + } + None +} + +#[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", +))] +// qemu doesn't seem to be emulating this correctly in these architectures +#[cfg_attr(all( + qemu, + any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "powerpc64", + ) +), ignore)] +#[test] +pub fn test_recv_ipv4pktinfo() { + use nix::sys::socket::sockopt::Ipv4PacketInfo; + use nix::sys::socket::{bind, SockFlag, SockType}; + use nix::sys::socket::{getsockname, setsockopt, socket}; + use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; + use nix::sys::uio::IoVec; + use nix::net::if_::*; + + let lo_ifaddr = loopback_address(AddressFamily::Inet); + let (lo_name, lo) = match lo_ifaddr { + Some(ifaddr) => (ifaddr.interface_name, + ifaddr.address.expect("Expect IPv4 address on interface")), + None => return, + }; + let receive = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("receive socket failed"); + bind(receive, &lo).expect("bind failed"); + let sa = getsockname(receive).expect("getsockname failed"); + setsockopt(receive, Ipv4PacketInfo, &true).expect("setsockopt failed"); + + { + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&slice)]; + + let send = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed"); + } + + { + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut space = cmsg_space!(libc::in_pktinfo); + let msg = recvmsg( + receive, + &iovec, + Some(&mut space), + MsgFlags::empty(), + ).expect("recvmsg failed"); + assert!( + !msg.flags + .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC) + ); + + let mut cmsgs = msg.cmsgs(); + if let Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) = cmsgs.next() { + let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); + assert_eq!( + pktinfo.ipi_ifindex as libc::c_uint, + i, + "unexpected ifindex (expected {}, got {})", + i, + pktinfo.ipi_ifindex + ); + } + assert!(cmsgs.next().is_none(), "unexpected additional control msg"); + assert_eq!(msg.bytes, 8); + assert_eq!( + iovec[0].as_slice(), + [1u8, 2, 3, 4, 5, 6, 7, 8] + ); + } +} + +#[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +))] +// qemu doesn't seem to be emulating this correctly in these architectures +#[cfg_attr(all( + qemu, + any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "powerpc64", + ) +), ignore)] +#[test] +pub fn test_recvif() { + use nix::net::if_::*; + use nix::sys::socket::sockopt::{Ipv4RecvIf, Ipv4RecvDstAddr}; + use nix::sys::socket::{bind, SockFlag, SockType}; + use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr}; + use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; + use nix::sys::uio::IoVec; + + let lo_ifaddr = loopback_address(AddressFamily::Inet); + let (lo_name, lo) = match lo_ifaddr { + Some(ifaddr) => (ifaddr.interface_name, + ifaddr.address.expect("Expect IPv4 address on interface")), + None => return, + }; + let receive = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("receive socket failed"); + bind(receive, &lo).expect("bind failed"); + let sa = getsockname(receive).expect("getsockname failed"); + setsockopt(receive, Ipv4RecvIf, &true).expect("setsockopt IP_RECVIF failed"); + setsockopt(receive, Ipv4RecvDstAddr, &true).expect("setsockopt IP_RECVDSTADDR failed"); + + { + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&slice)]; + + let send = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed"); + } + + { + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut space = cmsg_space!(libc::sockaddr_dl, libc::in_addr); + let msg = recvmsg( + receive, + &iovec, + Some(&mut space), + MsgFlags::empty(), + ).expect("recvmsg failed"); + assert!( + !msg.flags + .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC) + ); + assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs"); + + let mut rx_recvif = false; + let mut rx_recvdstaddr = false; + for cmsg in msg.cmsgs() { + match cmsg { + ControlMessageOwned::Ipv4RecvIf(dl) => { + rx_recvif = true; + let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); + assert_eq!( + dl.sdl_index as libc::c_uint, + i, + "unexpected ifindex (expected {}, got {})", + i, + dl.sdl_index + ); + }, + ControlMessageOwned::Ipv4RecvDstAddr(addr) => { + rx_recvdstaddr = true; + if let SockAddr::Inet(InetAddr::V4(a)) = lo { + assert_eq!(a.sin_addr.s_addr, + addr.s_addr, + "unexpected destination address (expected {}, got {})", + a.sin_addr.s_addr, + addr.s_addr); + } else { + panic!("unexpected Sockaddr"); + } + }, + _ => panic!("unexpected additional control msg"), + } + } + assert!(rx_recvif); + assert!(rx_recvdstaddr); + assert_eq!(msg.bytes, 8); + assert_eq!( + iovec[0].as_slice(), + [1u8, 2, 3, 4, 5, 6, 7, 8] + ); + } +} + +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +))] +// qemu doesn't seem to be emulating this correctly in these architectures +#[cfg_attr(all( + qemu, + any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "powerpc64", + ) +), ignore)] +#[test] +pub fn test_recv_ipv6pktinfo() { + use nix::net::if_::*; + use nix::sys::socket::sockopt::Ipv6RecvPacketInfo; + use nix::sys::socket::{bind, SockFlag, SockType}; + use nix::sys::socket::{getsockname, setsockopt, socket}; + use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; + use nix::sys::uio::IoVec; + + let lo_ifaddr = loopback_address(AddressFamily::Inet6); + let (lo_name, lo) = match lo_ifaddr { + Some(ifaddr) => (ifaddr.interface_name, + ifaddr.address.expect("Expect IPv4 address on interface")), + None => return, + }; + let receive = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("receive socket failed"); + bind(receive, &lo).expect("bind failed"); + let sa = getsockname(receive).expect("getsockname failed"); + setsockopt(receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed"); + + { + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&slice)]; + + let send = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed"); + } + + { + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut space = cmsg_space!(libc::in6_pktinfo); + let msg = recvmsg( + receive, + &iovec, + Some(&mut space), + MsgFlags::empty(), + ).expect("recvmsg failed"); + assert!( + !msg.flags + .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC) + ); + + let mut cmsgs = msg.cmsgs(); + if let Some(ControlMessageOwned::Ipv6PacketInfo(pktinfo)) = cmsgs.next() + { + let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); + assert_eq!( + pktinfo.ipi6_ifindex as libc::c_uint, + i, + "unexpected ifindex (expected {}, got {})", + i, + pktinfo.ipi6_ifindex + ); + } + assert!(cmsgs.next().is_none(), "unexpected additional control msg"); + assert_eq!(msg.bytes, 8); + assert_eq!( + iovec[0].as_slice(), + [1u8, 2, 3, 4, 5, 6, 7, 8] + ); + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg_attr(graviton, ignore = "Not supported by the CI environment")] +#[test] +pub fn test_vsock() { + use nix::errno::Errno; + use nix::sys::socket::{AddressFamily, socket, bind, connect, listen, + SockAddr, SockType, SockFlag}; + use nix::unistd::{close}; + use std::thread; + + let port: u32 = 3000; + + let s1 = socket(AddressFamily::Vsock, SockType::Stream, + SockFlag::empty(), None) + .expect("socket failed"); + + // VMADDR_CID_HYPERVISOR is reserved, so we expect an EADDRNOTAVAIL error. + let sockaddr = SockAddr::new_vsock(libc::VMADDR_CID_HYPERVISOR, port); + assert_eq!(bind(s1, &sockaddr).err(), + Some(Errno::EADDRNOTAVAIL)); + + let sockaddr = SockAddr::new_vsock(libc::VMADDR_CID_ANY, port); + assert_eq!(bind(s1, &sockaddr), Ok(())); + listen(s1, 10).expect("listen failed"); + + let thr = thread::spawn(move || { + let cid: u32 = libc::VMADDR_CID_HOST; + + let s2 = socket(AddressFamily::Vsock, SockType::Stream, + SockFlag::empty(), None) + .expect("socket failed"); + + let sockaddr = SockAddr::new_vsock(cid, port); + + // The current implementation does not support loopback devices, so, + // for now, we expect a failure on the connect. + assert_ne!(connect(s2, &sockaddr), Ok(())); + + close(s2).unwrap(); + }); + + close(s1).unwrap(); + thr.join().unwrap(); +} + +// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack +// of QEMU support is suspected. +#[cfg_attr(qemu, ignore)] +#[cfg(all(target_os = "linux"))] +#[test] +fn test_recvmsg_timestampns() { + use nix::sys::socket::*; + use nix::sys::uio::IoVec; + use nix::sys::time::*; + use std::time::*; + + // Set up + let message = "Ohayō!".as_bytes(); + let in_socket = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None).unwrap(); + setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap(); + let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0); + bind(in_socket, &SockAddr::new_inet(localhost)).unwrap(); + let address = getsockname(in_socket).unwrap(); + // Get initial time + let time0 = SystemTime::now(); + // Send the message + let iov = [IoVec::from_slice(message)]; + let flags = MsgFlags::empty(); + let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); + assert_eq!(message.len(), l); + // Receive the message + let mut buffer = vec![0u8; message.len()]; + let mut cmsgspace = nix::cmsg_space!(TimeSpec); + let iov = [IoVec::from_mut_slice(&mut buffer)]; + let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap(); + let rtime = match r.cmsgs().next() { + Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime, + Some(_) => panic!("Unexpected control message"), + None => panic!("No control message") + }; + // Check the final time + let time1 = SystemTime::now(); + // the packet's received timestamp should lie in-between the two system + // times, unless the system clock was adjusted in the meantime. + let rduration = Duration::new(rtime.tv_sec() as u64, + rtime.tv_nsec() as u32); + assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); + assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); + // Close socket + nix::unistd::close(in_socket).unwrap(); +} + +// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack +// of QEMU support is suspected. +#[cfg_attr(qemu, ignore)] +#[cfg(all(target_os = "linux"))] +#[test] +fn test_recvmmsg_timestampns() { + use nix::sys::socket::*; + use nix::sys::uio::IoVec; + use nix::sys::time::*; + use std::time::*; + + // Set up + let message = "Ohayō!".as_bytes(); + let in_socket = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None).unwrap(); + setsockopt(in_socket, sockopt::ReceiveTimestampns, &true).unwrap(); + let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0); + bind(in_socket, &SockAddr::new_inet(localhost)).unwrap(); + let address = getsockname(in_socket).unwrap(); + // Get initial time + let time0 = SystemTime::now(); + // Send the message + let iov = [IoVec::from_slice(message)]; + let flags = MsgFlags::empty(); + let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); + assert_eq!(message.len(), l); + // Receive the message + let mut buffer = vec![0u8; message.len()]; + let mut cmsgspace = nix::cmsg_space!(TimeSpec); + let iov = [IoVec::from_mut_slice(&mut buffer)]; + let mut data = vec![ + RecvMmsgData { + iov, + cmsg_buffer: Some(&mut cmsgspace), + }, + ]; + let r = recvmmsg(in_socket, &mut data, flags, None).unwrap(); + let rtime = match r[0].cmsgs().next() { + Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime, + Some(_) => panic!("Unexpected control message"), + None => panic!("No control message") + }; + // Check the final time + let time1 = SystemTime::now(); + // the packet's received timestamp should lie in-between the two system + // times, unless the system clock was adjusted in the meantime. + let rduration = Duration::new(rtime.tv_sec() as u64, + rtime.tv_nsec() as u32); + assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); + assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); + // Close socket + nix::unistd::close(in_socket).unwrap(); +} + +// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack +// of QEMU support is suspected. +#[cfg_attr(qemu, ignore)] +#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] +#[test] +fn test_recvmsg_rxq_ovfl() { + use nix::Error; + use nix::sys::socket::*; + use nix::sys::uio::IoVec; + use nix::sys::socket::sockopt::{RxqOvfl, RcvBuf}; + + let message = [0u8; 2048]; + let bufsize = message.len() * 2; + + let in_socket = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None).unwrap(); + let out_socket = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None).unwrap(); + + let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0); + bind(in_socket, &SockAddr::new_inet(localhost)).unwrap(); + + let address = getsockname(in_socket).unwrap(); + connect(out_socket, &address).unwrap(); + + // Set SO_RXQ_OVFL flag. + setsockopt(in_socket, RxqOvfl, &1).unwrap(); + + // Set the receiver buffer size to hold only 2 messages. + setsockopt(in_socket, RcvBuf, &bufsize).unwrap(); + + let mut drop_counter = 0; + + for _ in 0..2 { + let iov = [IoVec::from_slice(&message)]; + let flags = MsgFlags::empty(); + + // Send the 3 messages (the receiver buffer can only hold 2 messages) + // to create an overflow. + for _ in 0..3 { + let l = sendmsg(out_socket, &iov, &[], flags, Some(&address)).unwrap(); + assert_eq!(message.len(), l); + } + + // Receive the message and check the drop counter if any. + loop { + let mut buffer = vec![0u8; message.len()]; + let mut cmsgspace = nix::cmsg_space!(u32); + + let iov = [IoVec::from_mut_slice(&mut buffer)]; + + match recvmsg( + in_socket, + &iov, + Some(&mut cmsgspace), + MsgFlags::MSG_DONTWAIT) { + Ok(r) => { + drop_counter = match r.cmsgs().next() { + Some(ControlMessageOwned::RxqOvfl(drop_counter)) => drop_counter, + Some(_) => panic!("Unexpected control message"), + None => 0, + }; + }, + Err(Error::EAGAIN) => { break; }, + _ => { panic!("unknown recvmsg() error"); }, + } + } + } + + // One packet lost. + assert_eq!(drop_counter, 1); + + // Close sockets + nix::unistd::close(in_socket).unwrap(); + nix::unistd::close(out_socket).unwrap(); +} + +#[cfg(any( + target_os = "linux", + target_os = "android", +))] +mod linux_errqueue { + use nix::sys::socket::*; + use super::{FromStr, SocketAddr}; + + // Send a UDP datagram to a bogus destination address and observe an ICMP error (v4). + // + // Disable the test on QEMU because QEMU emulation of IP_RECVERR is broken (as documented on PR + // #1514). + #[cfg_attr(qemu, ignore)] + #[test] + fn test_recverr_v4() { + #[repr(u8)] + enum IcmpTypes { + DestUnreach = 3, // ICMP_DEST_UNREACH + } + #[repr(u8)] + enum IcmpUnreachCodes { + PortUnreach = 3, // ICMP_PORT_UNREACH + } + + test_recverr_impl::( + "127.0.0.1:6800", + AddressFamily::Inet, + sockopt::Ipv4RecvErr, + libc::SO_EE_ORIGIN_ICMP, + IcmpTypes::DestUnreach as u8, + IcmpUnreachCodes::PortUnreach as u8, + // Closure handles protocol-specific testing and returns generic sock_extended_err for + // protocol-independent test impl. + |cmsg| { + if let ControlMessageOwned::Ipv4RecvErr(ext_err, err_addr) = cmsg { + if let Some(origin) = err_addr { + // Validate that our network error originated from 127.0.0.1:0. + assert_eq!(origin.sin_family, AddressFamily::Inet as _); + assert_eq!(Ipv4Addr(origin.sin_addr), Ipv4Addr::new(127, 0, 0, 1)); + assert_eq!(origin.sin_port, 0); + } else { + panic!("Expected some error origin"); + } + *ext_err + } else { + panic!("Unexpected control message {:?}", cmsg); + } + }, + ) + } + + // Essentially the same test as v4. + // + // Disable the test on QEMU because QEMU emulation of IPV6_RECVERR is broken (as documented on + // PR #1514). + #[cfg_attr(qemu, ignore)] + #[test] + fn test_recverr_v6() { + #[repr(u8)] + enum IcmpV6Types { + DestUnreach = 1, // ICMPV6_DEST_UNREACH + } + #[repr(u8)] + enum IcmpV6UnreachCodes { + PortUnreach = 4, // ICMPV6_PORT_UNREACH + } + + test_recverr_impl::( + "[::1]:6801", + AddressFamily::Inet6, + sockopt::Ipv6RecvErr, + libc::SO_EE_ORIGIN_ICMP6, + IcmpV6Types::DestUnreach as u8, + IcmpV6UnreachCodes::PortUnreach as u8, + // Closure handles protocol-specific testing and returns generic sock_extended_err for + // protocol-independent test impl. + |cmsg| { + if let ControlMessageOwned::Ipv6RecvErr(ext_err, err_addr) = cmsg { + if let Some(origin) = err_addr { + // Validate that our network error originated from localhost:0. + assert_eq!(origin.sin6_family, AddressFamily::Inet6 as _); + assert_eq!( + Ipv6Addr(origin.sin6_addr), + Ipv6Addr::from_std(&"::1".parse().unwrap()), + ); + assert_eq!(origin.sin6_port, 0); + } else { + panic!("Expected some error origin"); + } + *ext_err + } else { + panic!("Unexpected control message {:?}", cmsg); + } + }, + ) + } + + fn test_recverr_impl(sa: &str, + af: AddressFamily, + opt: OPT, + ee_origin: u8, + ee_type: u8, + ee_code: u8, + testf: TESTF) + where + OPT: SetSockOpt, + TESTF: FnOnce(&ControlMessageOwned) -> libc::sock_extended_err, + { + use nix::errno::Errno; + use nix::sys::uio::IoVec; + + const MESSAGE_CONTENTS: &str = "ABCDEF"; + + let sock_addr = { + let std_sa = SocketAddr::from_str(sa).unwrap(); + let inet_addr = InetAddr::from_std(&std_sa); + SockAddr::new_inet(inet_addr) + }; + let sock = socket(af, SockType::Datagram, SockFlag::SOCK_CLOEXEC, None).unwrap(); + setsockopt(sock, opt, &true).unwrap(); + if let Err(e) = sendto(sock, MESSAGE_CONTENTS.as_bytes(), &sock_addr, MsgFlags::empty()) { + assert_eq!(e, Errno::EADDRNOTAVAIL); + println!("{:?} not available, skipping test.", af); + return; + } + + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut cspace = cmsg_space!(libc::sock_extended_err, SA); + + let msg = recvmsg(sock, &iovec, Some(&mut cspace), MsgFlags::MSG_ERRQUEUE).unwrap(); + // The sent message / destination associated with the error is returned: + assert_eq!(msg.bytes, MESSAGE_CONTENTS.as_bytes().len()); + assert_eq!(&buf[..msg.bytes], MESSAGE_CONTENTS.as_bytes()); + // recvmsg(2): "The original destination address of the datagram that caused the error is + // supplied via msg_name;" however, this is not literally true. E.g., an earlier version + // of this test used 0.0.0.0 (::0) as the destination address, which was mutated into + // 127.0.0.1 (::1). + assert_eq!(msg.address, Some(sock_addr)); + + // Check for expected control message. + let ext_err = match msg.cmsgs().next() { + Some(cmsg) => testf(&cmsg), + None => panic!("No control message"), + }; + + assert_eq!(ext_err.ee_errno, libc::ECONNREFUSED as u32); + assert_eq!(ext_err.ee_origin, ee_origin); + // ip(7): ee_type and ee_code are set from the type and code fields of the ICMP (ICMPv6) + // header. + assert_eq!(ext_err.ee_type, ee_type); + assert_eq!(ext_err.ee_code, ee_code); + // ip(7): ee_info contains the discovered MTU for EMSGSIZE errors. + assert_eq!(ext_err.ee_info, 0); + } +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_sockopt.rs b/vendor/nix-v0.23.1-patched/test/sys/test_sockopt.rs new file mode 100644 index 000000000..01920fd40 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_sockopt.rs @@ -0,0 +1,199 @@ +use rand::{thread_rng, Rng}; +use nix::sys::socket::{socket, sockopt, getsockopt, setsockopt, AddressFamily, SockType, SockFlag, SockProtocol}; +#[cfg(any(target_os = "android", target_os = "linux"))] +use crate::*; + +// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not. +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", +))] +#[test] +pub fn test_local_peercred_seqpacket() { + use nix::{ + unistd::{Gid, Uid}, + sys::socket::socketpair + }; + + let (fd1, _fd2) = socketpair(AddressFamily::Unix, SockType::SeqPacket, None, + SockFlag::empty()).unwrap(); + let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap(); + assert_eq!(xucred.version(), 0); + assert_eq!(Uid::from_raw(xucred.uid()), Uid::current()); + assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current()); +} + +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "ios" +))] +#[test] +pub fn test_local_peercred_stream() { + use nix::{ + unistd::{Gid, Uid}, + sys::socket::socketpair + }; + + let (fd1, _fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, + SockFlag::empty()).unwrap(); + let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap(); + assert_eq!(xucred.version(), 0); + assert_eq!(Uid::from_raw(xucred.uid()), Uid::current()); + assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current()); +} + +#[cfg(target_os = "linux")] +#[test] +fn is_so_mark_functional() { + use nix::sys::socket::sockopt; + + require_capability!("is_so_mark_functional", CAP_NET_ADMIN); + + let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap(); + setsockopt(s, sockopt::Mark, &1337).unwrap(); + let mark = getsockopt(s, sockopt::Mark).unwrap(); + assert_eq!(mark, 1337); +} + +#[test] +fn test_so_buf() { + let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), SockProtocol::Udp) + .unwrap(); + let bufsize: usize = thread_rng().gen_range(4096..131_072); + setsockopt(fd, sockopt::SndBuf, &bufsize).unwrap(); + let actual = getsockopt(fd, sockopt::SndBuf).unwrap(); + assert!(actual >= bufsize); + setsockopt(fd, sockopt::RcvBuf, &bufsize).unwrap(); + let actual = getsockopt(fd, sockopt::RcvBuf).unwrap(); + assert!(actual >= bufsize); +} + +#[test] +fn test_so_tcp_maxseg() { + use std::net::SocketAddr; + use std::str::FromStr; + use nix::sys::socket::{accept, bind, connect, listen, InetAddr, SockAddr}; + use nix::unistd::{close, write}; + + let std_sa = SocketAddr::from_str("127.0.0.1:4001").unwrap(); + let inet_addr = InetAddr::from_std(&std_sa); + let sock_addr = SockAddr::new_inet(inet_addr); + + let rsock = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp) + .unwrap(); + bind(rsock, &sock_addr).unwrap(); + listen(rsock, 10).unwrap(); + let initial = getsockopt(rsock, sockopt::TcpMaxSeg).unwrap(); + // Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some + // platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger + // than 700 + cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + let segsize: u32 = 873; + assert!(initial < segsize); + setsockopt(rsock, sockopt::TcpMaxSeg, &segsize).unwrap(); + } else { + assert!(initial < 700); + } + } + + // Connect and check the MSS that was advertised + let ssock = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp) + .unwrap(); + connect(ssock, &sock_addr).unwrap(); + let rsess = accept(rsock).unwrap(); + write(rsess, b"hello").unwrap(); + let actual = getsockopt(ssock, sockopt::TcpMaxSeg).unwrap(); + // Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max + // TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary. + cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + assert!((segsize - 100) <= actual); + assert!(actual <= segsize); + } else { + assert!(initial < actual); + assert!(536 < actual); + } + } + close(rsock).unwrap(); + close(ssock).unwrap(); +} + +// The CI doesn't supported getsockopt and setsockopt on emulated processors. +// It's beleived that a QEMU issue, the tests run ok on a fully emulated system. +// Current CI just run the binary with QEMU but the Kernel remains the same as the host. +// So the syscall doesn't work properly unless the kernel is also emulated. +#[test] +#[cfg(all( + any(target_arch = "x86", target_arch = "x86_64"), + any(target_os = "freebsd", target_os = "linux") +))] +fn test_tcp_congestion() { + use std::ffi::OsString; + + let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap(); + + let val = getsockopt(fd, sockopt::TcpCongestion).unwrap(); + setsockopt(fd, sockopt::TcpCongestion, &val).unwrap(); + + setsockopt(fd, sockopt::TcpCongestion, &OsString::from("tcp_congestion_does_not_exist")).unwrap_err(); + + assert_eq!( + getsockopt(fd, sockopt::TcpCongestion).unwrap(), + val + ); +} + +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_bindtodevice() { + skip_if_not_root!("test_bindtodevice"); + + let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap(); + + let val = getsockopt(fd, sockopt::BindToDevice).unwrap(); + setsockopt(fd, sockopt::BindToDevice, &val).unwrap(); + + assert_eq!( + getsockopt(fd, sockopt::BindToDevice).unwrap(), + val + ); +} + +#[test] +fn test_so_tcp_keepalive() { + let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp).unwrap(); + setsockopt(fd, sockopt::KeepAlive, &true).unwrap(); + assert!(getsockopt(fd, sockopt::KeepAlive).unwrap()); + + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "nacl"))] { + let x = getsockopt(fd, sockopt::TcpKeepIdle).unwrap(); + setsockopt(fd, sockopt::TcpKeepIdle, &(x + 1)).unwrap(); + assert_eq!(getsockopt(fd, sockopt::TcpKeepIdle).unwrap(), x + 1); + + let x = getsockopt(fd, sockopt::TcpKeepCount).unwrap(); + setsockopt(fd, sockopt::TcpKeepCount, &(x + 1)).unwrap(); + assert_eq!(getsockopt(fd, sockopt::TcpKeepCount).unwrap(), x + 1); + + let x = getsockopt(fd, sockopt::TcpKeepInterval).unwrap(); + setsockopt(fd, sockopt::TcpKeepInterval, &(x + 1)).unwrap(); + assert_eq!(getsockopt(fd, sockopt::TcpKeepInterval).unwrap(), x + 1); + } +} + +#[test] +#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +fn test_ttl_opts() { + let fd4 = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap(); + setsockopt(fd4, sockopt::Ipv4Ttl, &1) + .expect("setting ipv4ttl on an inet socket should succeed"); + let fd6 = socket(AddressFamily::Inet6, SockType::Datagram, SockFlag::empty(), None).unwrap(); + setsockopt(fd6, sockopt::Ipv6Ttl, &1) + .expect("setting ipv6ttl on an inet6 socket should succeed"); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_sysinfo.rs b/vendor/nix-v0.23.1-patched/test/sys/test_sysinfo.rs new file mode 100644 index 000000000..73e6586f6 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_sysinfo.rs @@ -0,0 +1,18 @@ +use nix::sys::sysinfo::*; + +#[test] +fn sysinfo_works() { + let info = sysinfo().unwrap(); + + let (l1, l5, l15) = info.load_average(); + assert!(l1 >= 0.0); + assert!(l5 >= 0.0); + assert!(l15 >= 0.0); + + info.uptime(); // just test Duration construction + + assert!(info.swap_free() <= info.swap_total(), + "more swap available than installed (free: {}, total: {})", + info.swap_free(), + info.swap_total()); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_termios.rs b/vendor/nix-v0.23.1-patched/test/sys/test_termios.rs new file mode 100644 index 000000000..63d6a51fa --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_termios.rs @@ -0,0 +1,130 @@ +use std::os::unix::prelude::*; +use tempfile::tempfile; + +use nix::fcntl; +use nix::errno::Errno; +use nix::pty::openpty; +use nix::sys::termios::{self, LocalFlags, OutputFlags, tcgetattr}; +use nix::unistd::{read, write, close}; + +/// Helper function analogous to `std::io::Write::write_all`, but for `RawFd`s +fn write_all(f: RawFd, buf: &[u8]) { + let mut len = 0; + while len < buf.len() { + len += write(f, &buf[len..]).unwrap(); + } +} + +// Test tcgetattr on a terminal +#[test] +fn test_tcgetattr_pty() { + // openpty uses ptname(3) internally + let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + let pty = openpty(None, None).expect("openpty failed"); + assert!(termios::tcgetattr(pty.slave).is_ok()); + close(pty.master).expect("closing the master failed"); + close(pty.slave).expect("closing the slave failed"); +} + +// Test tcgetattr on something that isn't a terminal +#[test] +fn test_tcgetattr_enotty() { + let file = tempfile().unwrap(); + assert_eq!(termios::tcgetattr(file.as_raw_fd()).err(), + Some(Errno::ENOTTY)); +} + +// Test tcgetattr on an invalid file descriptor +#[test] +fn test_tcgetattr_ebadf() { + assert_eq!(termios::tcgetattr(-1).err(), + Some(Errno::EBADF)); +} + +// Test modifying output flags +#[test] +fn test_output_flags() { + // openpty uses ptname(3) internally + let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open one pty to get attributes for the second one + let mut termios = { + let pty = openpty(None, None).expect("openpty failed"); + assert!(pty.master > 0); + assert!(pty.slave > 0); + let termios = tcgetattr(pty.slave).expect("tcgetattr failed"); + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + termios + }; + + // Make sure postprocessing '\r' isn't specified by default or this test is useless. + assert!(!termios.output_flags.contains(OutputFlags::OPOST | OutputFlags::OCRNL)); + + // Specify that '\r' characters should be transformed to '\n' + // OPOST is specified to enable post-processing + termios.output_flags.insert(OutputFlags::OPOST | OutputFlags::OCRNL); + + // Open a pty + let pty = openpty(None, &termios).unwrap(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + + // Write into the master + let string = "foofoofoo\r"; + write_all(pty.master, string.as_bytes()); + + // Read from the slave verifying that the output has been properly transformed + let mut buf = [0u8; 10]; + crate::read_exact(pty.slave, &mut buf); + let transformed_string = "foofoofoo\n"; + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + assert_eq!(&buf, transformed_string.as_bytes()); +} + +// Test modifying local flags +#[test] +fn test_local_flags() { + // openpty uses ptname(3) internally + let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open one pty to get attributes for the second one + let mut termios = { + let pty = openpty(None, None).unwrap(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + let termios = tcgetattr(pty.slave).unwrap(); + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + termios + }; + + // Make sure echo is specified by default or this test is useless. + assert!(termios.local_flags.contains(LocalFlags::ECHO)); + + // Disable local echo + termios.local_flags.remove(LocalFlags::ECHO); + + // Open a new pty with our modified termios settings + let pty = openpty(None, &termios).unwrap(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + + // Set the master is in nonblocking mode or reading will never return. + let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap(); + let new_flags = fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK; + fcntl::fcntl(pty.master, fcntl::F_SETFL(new_flags)).unwrap(); + + // Write into the master + let string = "foofoofoo\r"; + write_all(pty.master, string.as_bytes()); + + // Try to read from the master, which should not have anything as echoing was disabled. + let mut buf = [0u8; 10]; + let read = read(pty.master, &mut buf).unwrap_err(); + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + assert_eq!(read, Errno::EAGAIN); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_timerfd.rs b/vendor/nix-v0.23.1-patched/test/sys/test_timerfd.rs new file mode 100644 index 000000000..24fb2ac00 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_timerfd.rs @@ -0,0 +1,61 @@ +use nix::sys::time::{TimeSpec, TimeValLike}; +use nix::sys::timerfd::{ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags}; +use std::time::Instant; + +#[test] +pub fn test_timerfd_oneshot() { + let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap(); + + let before = Instant::now(); + + timer + .set( + Expiration::OneShot(TimeSpec::seconds(1)), + TimerSetTimeFlags::empty(), + ) + .unwrap(); + + timer.wait().unwrap(); + + let millis = before.elapsed().as_millis(); + assert!(millis > 900); +} + +#[test] +pub fn test_timerfd_interval() { + let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap(); + + let before = Instant::now(); + timer + .set( + Expiration::IntervalDelayed(TimeSpec::seconds(1), TimeSpec::seconds(2)), + TimerSetTimeFlags::empty(), + ) + .unwrap(); + + timer.wait().unwrap(); + + let start_delay = before.elapsed().as_millis(); + assert!(start_delay > 900); + + timer.wait().unwrap(); + + let interval_delay = before.elapsed().as_millis(); + assert!(interval_delay > 2900); +} + +#[test] +pub fn test_timerfd_unset() { + let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap(); + + timer + .set( + Expiration::OneShot(TimeSpec::seconds(1)), + TimerSetTimeFlags::empty(), + ) + .unwrap(); + + timer.unset().unwrap(); + + assert!(timer.get().unwrap() == None); +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_uio.rs b/vendor/nix-v0.23.1-patched/test/sys/test_uio.rs new file mode 100644 index 000000000..24df04355 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_uio.rs @@ -0,0 +1,255 @@ +use nix::sys::uio::*; +use nix::unistd::*; +use rand::{thread_rng, Rng}; +use rand::distributions::Alphanumeric; +use std::{cmp, iter}; +use std::fs::{OpenOptions}; +use std::os::unix::io::AsRawFd; + +#[cfg(not(target_os = "redox"))] +use tempfile::tempfile; +use tempfile::tempdir; + +#[test] +fn test_writev() { + let mut to_write = Vec::with_capacity(16 * 128); + for _ in 0..16 { + let s: String = thread_rng() + .sample_iter(&Alphanumeric) + .map(char::from) + .take(128) + .collect(); + let b = s.as_bytes(); + to_write.extend(b.iter().cloned()); + } + // Allocate and fill iovecs + let mut iovecs = Vec::new(); + let mut consumed = 0; + while consumed < to_write.len() { + let left = to_write.len() - consumed; + let slice_len = if left <= 64 { left } else { thread_rng().gen_range(64..cmp::min(256, left)) }; + let b = &to_write[consumed..consumed+slice_len]; + iovecs.push(IoVec::from_slice(b)); + consumed += slice_len; + } + let pipe_res = pipe(); + assert!(pipe_res.is_ok()); + let (reader, writer) = pipe_res.ok().unwrap(); + // FileDesc will close its filedesc (reader). + let mut read_buf: Vec = iter::repeat(0u8).take(128 * 16).collect(); + // Blocking io, should write all data. + let write_res = writev(writer, &iovecs); + // Successful write + assert!(write_res.is_ok()); + let written = write_res.ok().unwrap(); + // Check whether we written all data + assert_eq!(to_write.len(), written); + let read_res = read(reader, &mut read_buf[..]); + // Successful read + assert!(read_res.is_ok()); + let read = read_res.ok().unwrap() as usize; + // Check we have read as much as we written + assert_eq!(read, written); + // Check equality of written and read data + assert_eq!(&to_write, &read_buf); + let close_res = close(writer); + assert!(close_res.is_ok()); + let close_res = close(reader); + assert!(close_res.is_ok()); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_readv() { + let s:String = thread_rng() + .sample_iter(&Alphanumeric) + .map(char::from) + .take(128) + .collect(); + let to_write = s.as_bytes().to_vec(); + let mut storage = Vec::new(); + let mut allocated = 0; + while allocated < to_write.len() { + let left = to_write.len() - allocated; + let vec_len = if left <= 64 { left } else { thread_rng().gen_range(64..cmp::min(256, left)) }; + let v: Vec = iter::repeat(0u8).take(vec_len).collect(); + storage.push(v); + allocated += vec_len; + } + let mut iovecs = Vec::with_capacity(storage.len()); + for v in &mut storage { + iovecs.push(IoVec::from_mut_slice(&mut v[..])); + } + let pipe_res = pipe(); + assert!(pipe_res.is_ok()); + let (reader, writer) = pipe_res.ok().unwrap(); + // Blocking io, should write all data. + let write_res = write(writer, &to_write); + // Successful write + assert!(write_res.is_ok()); + let read_res = readv(reader, &mut iovecs[..]); + assert!(read_res.is_ok()); + let read = read_res.ok().unwrap(); + // Check whether we've read all data + assert_eq!(to_write.len(), read); + // Cccumulate data from iovecs + let mut read_buf = Vec::with_capacity(to_write.len()); + for iovec in &iovecs { + read_buf.extend(iovec.as_slice().iter().cloned()); + } + // Check whether iovecs contain all written data + assert_eq!(read_buf.len(), to_write.len()); + // Check equality of written and read data + assert_eq!(&read_buf, &to_write); + let close_res = close(reader); + assert!(close_res.is_ok()); + let close_res = close(writer); + assert!(close_res.is_ok()); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_pwrite() { + use std::io::Read; + + let mut file = tempfile().unwrap(); + let buf = [1u8;8]; + assert_eq!(Ok(8), pwrite(file.as_raw_fd(), &buf, 8)); + let mut file_content = Vec::new(); + file.read_to_end(&mut file_content).unwrap(); + let mut expected = vec![0u8;8]; + expected.extend(vec![1;8]); + assert_eq!(file_content, expected); +} + +#[test] +fn test_pread() { + use std::io::Write; + + let tempdir = tempdir().unwrap(); + + let path = tempdir.path().join("pread_test_file"); + let mut file = OpenOptions::new().write(true).read(true).create(true) + .truncate(true).open(path).unwrap(); + let file_content: Vec = (0..64).collect(); + file.write_all(&file_content).unwrap(); + + let mut buf = [0u8;16]; + assert_eq!(Ok(16), pread(file.as_raw_fd(), &mut buf, 16)); + let expected: Vec<_> = (16..32).collect(); + assert_eq!(&buf[..], &expected[..]); +} + +#[test] +#[cfg(not(any(target_os = "macos", target_os = "redox")))] +fn test_pwritev() { + use std::io::Read; + + let to_write: Vec = (0..128).collect(); + let expected: Vec = [vec![0;100], to_write.clone()].concat(); + + let iovecs = [ + IoVec::from_slice(&to_write[0..17]), + IoVec::from_slice(&to_write[17..64]), + IoVec::from_slice(&to_write[64..128]), + ]; + + let tempdir = tempdir().unwrap(); + + // pwritev them into a temporary file + let path = tempdir.path().join("pwritev_test_file"); + let mut file = OpenOptions::new().write(true).read(true).create(true) + .truncate(true).open(path).unwrap(); + + let written = pwritev(file.as_raw_fd(), &iovecs, 100).ok().unwrap(); + assert_eq!(written, to_write.len()); + + // Read the data back and make sure it matches + let mut contents = Vec::new(); + file.read_to_end(&mut contents).unwrap(); + assert_eq!(contents, expected); +} + +#[test] +#[cfg(not(any(target_os = "macos", target_os = "redox")))] +fn test_preadv() { + use std::io::Write; + + let to_write: Vec = (0..200).collect(); + let expected: Vec = (100..200).collect(); + + let tempdir = tempdir().unwrap(); + + let path = tempdir.path().join("preadv_test_file"); + + let mut file = OpenOptions::new().read(true).write(true).create(true) + .truncate(true).open(path).unwrap(); + file.write_all(&to_write).unwrap(); + + let mut buffers: Vec> = vec![ + vec![0; 24], + vec![0; 1], + vec![0; 75], + ]; + + { + // Borrow the buffers into IoVecs and preadv into them + let iovecs: Vec<_> = buffers.iter_mut().map( + |buf| IoVec::from_mut_slice(&mut buf[..])).collect(); + assert_eq!(Ok(100), preadv(file.as_raw_fd(), &iovecs, 100)); + } + + let all = buffers.concat(); + assert_eq!(all, expected); +} + +#[test] +#[cfg(target_os = "linux")] +// qemu-user doesn't implement process_vm_readv/writev on most arches +#[cfg_attr(qemu, ignore)] +fn test_process_vm_readv() { + use nix::unistd::ForkResult::*; + use nix::sys::signal::*; + use nix::sys::wait::*; + use crate::*; + + require_capability!("test_process_vm_readv", CAP_SYS_PTRACE); + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Pre-allocate memory in the child, since allocation isn't safe + // post-fork (~= async-signal-safe) + let mut vector = vec![1u8, 2, 3, 4, 5]; + + let (r, w) = pipe().unwrap(); + match unsafe{fork()}.expect("Error: Fork Failed") { + Parent { child } => { + close(w).unwrap(); + // wait for child + read(r, &mut [0u8]).unwrap(); + close(r).unwrap(); + + let ptr = vector.as_ptr() as usize; + let remote_iov = RemoteIoVec { base: ptr, len: 5 }; + let mut buf = vec![0u8; 5]; + + let ret = process_vm_readv(child, + &[IoVec::from_mut_slice(&mut buf)], + &[remote_iov]); + + kill(child, SIGTERM).unwrap(); + waitpid(child, None).unwrap(); + + assert_eq!(Ok(5), ret); + assert_eq!(20u8, buf.iter().sum()); + }, + Child => { + let _ = close(r); + for i in &mut vector { + *i += 1; + } + let _ = write(w, b"\0"); + let _ = close(w); + loop { let _ = pause(); } + }, + } +} diff --git a/vendor/nix-v0.23.1-patched/test/sys/test_wait.rs b/vendor/nix-v0.23.1-patched/test/sys/test_wait.rs new file mode 100644 index 000000000..4a5b9661f --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/sys/test_wait.rs @@ -0,0 +1,107 @@ +use nix::errno::Errno; +use nix::unistd::*; +use nix::unistd::ForkResult::*; +use nix::sys::signal::*; +use nix::sys::wait::*; +use libc::_exit; + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_wait_signal() { + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe. + match unsafe{fork()}.expect("Error: Fork Failed") { + Child => { + pause(); + unsafe { _exit(123) } + }, + Parent { child } => { + kill(child, Some(SIGKILL)).expect("Error: Kill Failed"); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, SIGKILL, false))); + }, + } +} + +#[test] +fn test_wait_exit() { + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Safe: Child only calls `_exit`, which is async-signal-safe. + match unsafe{fork()}.expect("Error: Fork Failed") { + Child => unsafe { _exit(12); }, + Parent { child } => { + assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12))); + }, + } +} + +#[test] +fn test_waitstatus_from_raw() { + let pid = Pid::from_raw(1); + assert_eq!(WaitStatus::from_raw(pid, 0x0002), Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false))); + assert_eq!(WaitStatus::from_raw(pid, 0x0200), Ok(WaitStatus::Exited(pid, 2))); + assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Errno::EINVAL)); +} + +#[test] +fn test_waitstatus_pid() { + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + match unsafe{fork()}.unwrap() { + Child => unsafe { _exit(0) }, + Parent { child } => { + let status = waitpid(child, None).unwrap(); + assert_eq!(status.pid(), Some(child)); + } + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +// FIXME: qemu-user doesn't implement ptrace on most arches +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod ptrace { + use nix::sys::ptrace::{self, Options, Event}; + use nix::sys::signal::*; + use nix::sys::wait::*; + use nix::unistd::*; + use nix::unistd::ForkResult::*; + use libc::_exit; + use crate::*; + + fn ptrace_child() -> ! { + ptrace::traceme().unwrap(); + // As recommended by ptrace(2), raise SIGTRAP to pause the child + // until the parent is ready to continue + raise(SIGTRAP).unwrap(); + unsafe { _exit(0) } + } + + fn ptrace_parent(child: Pid) { + // Wait for the raised SIGTRAP + assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, SIGTRAP))); + // We want to test a syscall stop and a PTRACE_EVENT stop + assert!(ptrace::setoptions(child, Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT).is_ok()); + + // First, stop on the next system call, which will be exit() + assert!(ptrace::syscall(child, None).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + // Then get the ptrace event for the process exiting + assert!(ptrace::cont(child, None).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, Event::PTRACE_EVENT_EXIT as i32))); + // Finally get the normal wait() result, now that the process has exited + assert!(ptrace::cont(child, None).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0))); + } + + #[test] + fn test_wait_ptrace() { + require_capability!("test_wait_ptrace", CAP_SYS_PTRACE); + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + match unsafe{fork()}.expect("Error: Fork Failed") { + Child => ptrace_child(), + Parent { child } => ptrace_parent(child), + } + } +} diff --git a/vendor/nix-v0.23.1-patched/test/test.rs b/vendor/nix-v0.23.1-patched/test/test.rs new file mode 100644 index 000000000..b882d1748 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test.rs @@ -0,0 +1,103 @@ +#[macro_use] +extern crate cfg_if; +#[cfg_attr(not(target_os = "redox"), macro_use)] +extern crate nix; +#[macro_use] +extern crate lazy_static; + +mod common; +mod sys; +#[cfg(not(target_os = "redox"))] +mod test_dir; +mod test_fcntl; +#[cfg(any(target_os = "android", + target_os = "linux"))] +mod test_kmod; +#[cfg(target_os = "freebsd")] +mod test_nmount; +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "fushsia", + target_os = "linux", + target_os = "netbsd"))] +mod test_mq; +#[cfg(not(target_os = "redox"))] +mod test_net; +mod test_nix_path; +mod test_resource; +mod test_poll; +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +mod test_pty; +#[cfg(any(target_os = "android", + target_os = "linux"))] +mod test_sched; +#[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] +mod test_sendfile; +mod test_stat; +mod test_time; +mod test_unistd; + +use std::os::unix::io::RawFd; +use std::path::PathBuf; +use std::sync::{Mutex, RwLock, RwLockWriteGuard}; +use nix::unistd::{chdir, getcwd, read}; + + +/// Helper function analogous to `std::io::Read::read_exact`, but for `RawFD`s +fn read_exact(f: RawFd, buf: &mut [u8]) { + let mut len = 0; + while len < buf.len() { + // get_mut would be better than split_at_mut, but it requires nightly + let (_, remaining) = buf.split_at_mut(len); + len += read(f, remaining).unwrap(); + } +} + +lazy_static! { + /// Any test that changes the process's current working directory must grab + /// the RwLock exclusively. Any process that cares about the current + /// working directory must grab it shared. + pub static ref CWD_LOCK: RwLock<()> = RwLock::new(()); + /// Any test that creates child processes must grab this mutex, regardless + /// of what it does with those children. + pub static ref FORK_MTX: Mutex<()> = Mutex::new(()); + /// Any test that changes the process's supplementary groups must grab this + /// mutex + pub static ref GROUPS_MTX: Mutex<()> = Mutex::new(()); + /// Any tests that loads or unloads kernel modules must grab this mutex + pub static ref KMOD_MTX: Mutex<()> = Mutex::new(()); + /// Any test that calls ptsname(3) must grab this mutex. + pub static ref PTSNAME_MTX: Mutex<()> = Mutex::new(()); + /// Any test that alters signal handling must grab this mutex. + pub static ref SIGNAL_MTX: Mutex<()> = Mutex::new(()); +} + +/// RAII object that restores a test's original directory on drop +struct DirRestore<'a> { + d: PathBuf, + _g: RwLockWriteGuard<'a, ()> +} + +impl<'a> DirRestore<'a> { + fn new() -> Self { + let guard = crate::CWD_LOCK.write() + .expect("Lock got poisoned by another test"); + DirRestore{ + _g: guard, + d: getcwd().unwrap(), + } + } +} + +impl<'a> Drop for DirRestore<'a> { + fn drop(&mut self) { + let r = chdir(&self.d); + if std::thread::panicking() { + r.unwrap(); + } + } +} diff --git a/vendor/nix-v0.23.1-patched/test/test_clearenv.rs b/vendor/nix-v0.23.1-patched/test/test_clearenv.rs new file mode 100644 index 000000000..28a776804 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_clearenv.rs @@ -0,0 +1,9 @@ +use std::env; + +#[test] +fn clearenv() { + env::set_var("FOO", "BAR"); + unsafe { nix::env::clearenv() }.unwrap(); + assert_eq!(env::var("FOO").unwrap_err(), env::VarError::NotPresent); + assert_eq!(env::vars().count(), 0); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_dir.rs b/vendor/nix-v0.23.1-patched/test/test_dir.rs new file mode 100644 index 000000000..2940b6eaf --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_dir.rs @@ -0,0 +1,56 @@ +use nix::dir::{Dir, Type}; +use nix::fcntl::OFlag; +use nix::sys::stat::Mode; +use std::fs::File; +use tempfile::tempdir; + + +#[cfg(test)] +fn flags() -> OFlag { + #[cfg(target_os = "illumos")] + let f = OFlag::O_RDONLY | OFlag::O_CLOEXEC; + + #[cfg(not(target_os = "illumos"))] + let f = OFlag::O_RDONLY | OFlag::O_CLOEXEC | OFlag::O_DIRECTORY; + + f +} + +#[test] +#[allow(clippy::unnecessary_sort_by)] // False positive +fn read() { + let tmp = tempdir().unwrap(); + File::create(&tmp.path().join("foo")).unwrap(); + ::std::os::unix::fs::symlink("foo", tmp.path().join("bar")).unwrap(); + let mut dir = Dir::open(tmp.path(), flags(), Mode::empty()).unwrap(); + let mut entries: Vec<_> = dir.iter().map(|e| e.unwrap()).collect(); + entries.sort_by(|a, b| a.file_name().cmp(b.file_name())); + let entry_names: Vec<_> = entries + .iter() + .map(|e| e.file_name().to_str().unwrap().to_owned()) + .collect(); + assert_eq!(&entry_names[..], &[".", "..", "bar", "foo"]); + + // Check file types. The system is allowed to return DT_UNKNOWN (aka None here) but if it does + // return a type, ensure it's correct. + assert!(&[Some(Type::Directory), None].contains(&entries[0].file_type())); // .: dir + assert!(&[Some(Type::Directory), None].contains(&entries[1].file_type())); // ..: dir + assert!(&[Some(Type::Symlink), None].contains(&entries[2].file_type())); // bar: symlink + assert!(&[Some(Type::File), None].contains(&entries[3].file_type())); // foo: regular file +} + +#[test] +fn rewind() { + let tmp = tempdir().unwrap(); + let mut dir = Dir::open(tmp.path(), flags(), Mode::empty()).unwrap(); + let entries1: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect(); + let entries2: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect(); + let entries3: Vec<_> = dir.into_iter().map(|e| e.unwrap().file_name().to_owned()).collect(); + assert_eq!(entries1, entries2); + assert_eq!(entries2, entries3); +} + +#[test] +fn ebadf() { + assert_eq!(Dir::from_fd(-1).unwrap_err(), nix::Error::EBADF); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_fcntl.rs b/vendor/nix-v0.23.1-patched/test/test_fcntl.rs new file mode 100644 index 000000000..db2acfbf5 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_fcntl.rs @@ -0,0 +1,540 @@ +#[cfg(not(target_os = "redox"))] +use nix::errno::*; +#[cfg(not(target_os = "redox"))] +use nix::fcntl::{open, OFlag, readlink}; +#[cfg(not(target_os = "redox"))] +use nix::fcntl::{openat, readlinkat, renameat}; +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x32", + target_arch = "powerpc", + target_arch = "s390x" + ) +))] +use nix::fcntl::{RenameFlags, renameat2}; +#[cfg(not(target_os = "redox"))] +use nix::sys::stat::Mode; +#[cfg(not(target_os = "redox"))] +use nix::unistd::{close, read}; +#[cfg(not(target_os = "redox"))] +use tempfile::{self, NamedTempFile}; +#[cfg(not(target_os = "redox"))] +use std::fs::File; +#[cfg(not(target_os = "redox"))] +use std::io::prelude::*; +#[cfg(not(target_os = "redox"))] +use std::os::unix::fs; + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_openat() { + const CONTENTS: &[u8] = b"abcd"; + let mut tmp = NamedTempFile::new().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + + let dirfd = open(tmp.path().parent().unwrap(), + OFlag::empty(), + Mode::empty()).unwrap(); + let fd = openat(dirfd, + tmp.path().file_name().unwrap(), + OFlag::O_RDONLY, + Mode::empty()).unwrap(); + + let mut buf = [0u8; 1024]; + assert_eq!(4, read(fd, &mut buf).unwrap()); + assert_eq!(CONTENTS, &buf[0..4]); + + close(fd).unwrap(); + close(dirfd).unwrap(); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_renameat() { + let old_dir = tempfile::tempdir().unwrap(); + let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let old_path = old_dir.path().join("old"); + File::create(&old_path).unwrap(); + let new_dir = tempfile::tempdir().unwrap(); + let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap(); + renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap(); + assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(), + Errno::ENOENT); + close(old_dirfd).unwrap(); + close(new_dirfd).unwrap(); + assert!(new_dir.path().join("new").exists()); +} + +#[test] +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x32", + target_arch = "powerpc", + target_arch = "s390x" + ) +))] +fn test_renameat2_behaves_like_renameat_with_no_flags() { + let old_dir = tempfile::tempdir().unwrap(); + let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let old_path = old_dir.path().join("old"); + File::create(&old_path).unwrap(); + let new_dir = tempfile::tempdir().unwrap(); + let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap(); + renameat2( + Some(old_dirfd), + "old", + Some(new_dirfd), + "new", + RenameFlags::empty(), + ) + .unwrap(); + assert_eq!( + renameat2( + Some(old_dirfd), + "old", + Some(new_dirfd), + "new", + RenameFlags::empty() + ) + .unwrap_err(), + Errno::ENOENT + ); + close(old_dirfd).unwrap(); + close(new_dirfd).unwrap(); + assert!(new_dir.path().join("new").exists()); +} + +#[test] +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x32", + target_arch = "powerpc", + target_arch = "s390x" + ) +))] +fn test_renameat2_exchange() { + let old_dir = tempfile::tempdir().unwrap(); + let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let old_path = old_dir.path().join("old"); + { + let mut old_f = File::create(&old_path).unwrap(); + old_f.write_all(b"old").unwrap(); + } + let new_dir = tempfile::tempdir().unwrap(); + let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let new_path = new_dir.path().join("new"); + { + let mut new_f = File::create(&new_path).unwrap(); + new_f.write_all(b"new").unwrap(); + } + renameat2( + Some(old_dirfd), + "old", + Some(new_dirfd), + "new", + RenameFlags::RENAME_EXCHANGE, + ) + .unwrap(); + let mut buf = String::new(); + let mut new_f = File::open(&new_path).unwrap(); + new_f.read_to_string(&mut buf).unwrap(); + assert_eq!(buf, "old"); + buf = "".to_string(); + let mut old_f = File::open(&old_path).unwrap(); + old_f.read_to_string(&mut buf).unwrap(); + assert_eq!(buf, "new"); + close(old_dirfd).unwrap(); + close(new_dirfd).unwrap(); +} + +#[test] +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x32", + target_arch = "powerpc", + target_arch = "s390x" + ) +))] +fn test_renameat2_noreplace() { + let old_dir = tempfile::tempdir().unwrap(); + let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let old_path = old_dir.path().join("old"); + File::create(&old_path).unwrap(); + let new_dir = tempfile::tempdir().unwrap(); + let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let new_path = new_dir.path().join("new"); + File::create(&new_path).unwrap(); + assert_eq!( + renameat2( + Some(old_dirfd), + "old", + Some(new_dirfd), + "new", + RenameFlags::RENAME_NOREPLACE + ) + .unwrap_err(), + Errno::EEXIST + ); + close(old_dirfd).unwrap(); + close(new_dirfd).unwrap(); + assert!(new_dir.path().join("new").exists()); + assert!(old_dir.path().join("old").exists()); +} + + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_readlink() { + let tempdir = tempfile::tempdir().unwrap(); + let src = tempdir.path().join("a"); + let dst = tempdir.path().join("b"); + println!("a: {:?}, b: {:?}", &src, &dst); + fs::symlink(&src.as_path(), &dst.as_path()).unwrap(); + let dirfd = open(tempdir.path(), + OFlag::empty(), + Mode::empty()).unwrap(); + let expected_dir = src.to_str().unwrap(); + + assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir); + assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir); + +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod linux_android { + use std::io::prelude::*; + use std::io::SeekFrom; + use std::os::unix::prelude::*; + use libc::loff_t; + + use nix::fcntl::*; + use nix::sys::uio::IoVec; + use nix::unistd::{close, pipe, read, write}; + + use tempfile::tempfile; + #[cfg(any(target_os = "linux"))] + use tempfile::NamedTempFile; + + use crate::*; + + /// This test creates a temporary file containing the contents + /// 'foobarbaz' and uses the `copy_file_range` call to transfer + /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The + /// resulting file is read and should contain the contents `bar`. + /// The from_offset should be updated by the call to reflect + /// the 3 bytes read (6). + #[test] + // QEMU does not support copy_file_range. Skip under qemu + #[cfg_attr(qemu, ignore)] + fn test_copy_file_range() { + const CONTENTS: &[u8] = b"foobarbaz"; + + let mut tmp1 = tempfile().unwrap(); + let mut tmp2 = tempfile().unwrap(); + + tmp1.write_all(CONTENTS).unwrap(); + tmp1.flush().unwrap(); + + let mut from_offset: i64 = 3; + copy_file_range( + tmp1.as_raw_fd(), + Some(&mut from_offset), + tmp2.as_raw_fd(), + None, + 3, + ) + .unwrap(); + + let mut res: String = String::new(); + tmp2.seek(SeekFrom::Start(0)).unwrap(); + tmp2.read_to_string(&mut res).unwrap(); + + assert_eq!(res, String::from("bar")); + assert_eq!(from_offset, 6); + } + + #[test] + fn test_splice() { + const CONTENTS: &[u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + + let (rd, wr) = pipe().unwrap(); + let mut offset: loff_t = 5; + let res = splice(tmp.as_raw_fd(), Some(&mut offset), + wr, None, 2, SpliceFFlags::empty()).unwrap(); + + assert_eq!(2, res); + + let mut buf = [0u8; 1024]; + assert_eq!(2, read(rd, &mut buf).unwrap()); + assert_eq!(b"f1", &buf[0..2]); + assert_eq!(7, offset); + + close(rd).unwrap(); + close(wr).unwrap(); + } + + #[test] + fn test_tee() { + let (rd1, wr1) = pipe().unwrap(); + let (rd2, wr2) = pipe().unwrap(); + + write(wr1, b"abc").unwrap(); + let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap(); + + assert_eq!(2, res); + + let mut buf = [0u8; 1024]; + + // Check the tee'd bytes are at rd2. + assert_eq!(2, read(rd2, &mut buf).unwrap()); + assert_eq!(b"ab", &buf[0..2]); + + // Check all the bytes are still at rd1. + assert_eq!(3, read(rd1, &mut buf).unwrap()); + assert_eq!(b"abc", &buf[0..3]); + + close(rd1).unwrap(); + close(wr1).unwrap(); + close(rd2).unwrap(); + close(wr2).unwrap(); + } + + #[test] + fn test_vmsplice() { + let (rd, wr) = pipe().unwrap(); + + let buf1 = b"abcdef"; + let buf2 = b"defghi"; + let iovecs = vec![ + IoVec::from_slice(&buf1[0..3]), + IoVec::from_slice(&buf2[0..3]) + ]; + + let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap(); + + assert_eq!(6, res); + + // Check the bytes can be read at rd. + let mut buf = [0u8; 32]; + assert_eq!(6, read(rd, &mut buf).unwrap()); + assert_eq!(b"abcdef", &buf[0..6]); + + close(rd).unwrap(); + close(wr).unwrap(); + } + + #[cfg(any(target_os = "linux"))] + #[test] + fn test_fallocate() { + let tmp = NamedTempFile::new().unwrap(); + + let fd = tmp.as_raw_fd(); + fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap(); + + // Check if we read exactly 100 bytes + let mut buf = [0u8; 200]; + assert_eq!(100, read(fd, &mut buf).unwrap()); + } + + // The tests below are disabled for the listed targets + // due to OFD locks not being available in the kernel/libc + // versions used in the CI environment, probably because + // they run under QEMU. + + #[test] + #[cfg(all(target_os = "linux", not(target_env = "musl")))] + fn test_ofd_write_lock() { + use nix::sys::stat::fstat; + use std::mem; + + let tmp = NamedTempFile::new().unwrap(); + + let fd = tmp.as_raw_fd(); + let statfs = nix::sys::statfs::fstatfs(&tmp).unwrap(); + if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC { + // OverlayFS is a union file system. It returns one inode value in + // stat(2), but a different one shows up in /proc/locks. So we must + // skip the test. + skip!("/proc/locks does not work on overlayfs"); + } + let inode = fstat(fd).expect("fstat failed").st_ino as usize; + + let mut flock: libc::flock = unsafe { + mem::zeroed() // required for Linux/mips + }; + flock.l_type = libc::F_WRLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + flock.l_start = 0; + flock.l_len = 0; + flock.l_pid = 0; + fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed"); + assert_eq!( + Some(("OFDLCK".to_string(), "WRITE".to_string())), + lock_info(inode) + ); + + flock.l_type = libc::F_UNLCK as libc::c_short; + fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed"); + assert_eq!(None, lock_info(inode)); + } + + #[test] + #[cfg(all(target_os = "linux", not(target_env = "musl")))] + fn test_ofd_read_lock() { + use nix::sys::stat::fstat; + use std::mem; + + let tmp = NamedTempFile::new().unwrap(); + + let fd = tmp.as_raw_fd(); + let statfs = nix::sys::statfs::fstatfs(&tmp).unwrap(); + if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC { + // OverlayFS is a union file system. It returns one inode value in + // stat(2), but a different one shows up in /proc/locks. So we must + // skip the test. + skip!("/proc/locks does not work on overlayfs"); + } + let inode = fstat(fd).expect("fstat failed").st_ino as usize; + + let mut flock: libc::flock = unsafe { + mem::zeroed() // required for Linux/mips + }; + flock.l_type = libc::F_RDLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + flock.l_start = 0; + flock.l_len = 0; + flock.l_pid = 0; + fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed"); + assert_eq!( + Some(("OFDLCK".to_string(), "READ".to_string())), + lock_info(inode) + ); + + flock.l_type = libc::F_UNLCK as libc::c_short; + fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed"); + assert_eq!(None, lock_info(inode)); + } + + #[cfg(all(target_os = "linux", not(target_env = "musl")))] + fn lock_info(inode: usize) -> Option<(String, String)> { + use std::{ + fs::File, + io::BufReader + }; + + let file = File::open("/proc/locks").expect("open /proc/locks failed"); + let buf = BufReader::new(file); + + for line in buf.lines() { + let line = line.unwrap(); + let parts: Vec<_> = line.split_whitespace().collect(); + let lock_type = parts[1]; + let lock_access = parts[3]; + let ino_parts: Vec<_> = parts[5].split(':').collect(); + let ino: usize = ino_parts[2].parse().unwrap(); + if ino == inode { + return Some((lock_type.to_string(), lock_access.to_string())); + } + } + None + } +} + +#[cfg(any(target_os = "linux", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + any(target_os = "wasi", target_env = "wasi"), + target_env = "uclibc", + target_os = "freebsd"))] +mod test_posix_fadvise { + + use tempfile::NamedTempFile; + use std::os::unix::io::{RawFd, AsRawFd}; + use nix::errno::Errno; + use nix::fcntl::*; + use nix::unistd::pipe; + + #[test] + fn test_success() { + let tmp = NamedTempFile::new().unwrap(); + let fd = tmp.as_raw_fd(); + let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED); + + assert!(res.is_ok()); + } + + #[test] + fn test_errno() { + let (rd, _wr) = pipe().unwrap(); + let res = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED); + assert_eq!(res, Err(Errno::ESPIPE)); + } +} + +#[cfg(any(target_os = "linux", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + any(target_os = "wasi", target_env = "wasi"), + target_os = "freebsd"))] +mod test_posix_fallocate { + + use tempfile::NamedTempFile; + use std::{io::Read, os::unix::io::{RawFd, AsRawFd}}; + use nix::errno::Errno; + use nix::fcntl::*; + use nix::unistd::pipe; + + #[test] + fn success() { + const LEN: usize = 100; + let mut tmp = NamedTempFile::new().unwrap(); + let fd = tmp.as_raw_fd(); + let res = posix_fallocate(fd, 0, LEN as libc::off_t); + match res { + Ok(_) => { + let mut data = [1u8; LEN]; + assert_eq!(tmp.read(&mut data).expect("read failure"), LEN); + assert_eq!(&data[..], &[0u8; LEN][..]); + } + Err(Errno::EINVAL) => { + // POSIX requires posix_fallocate to return EINVAL both for + // invalid arguments (i.e. len < 0) and if the operation is not + // supported by the file system. + // There's no way to tell for sure whether the file system + // supports posix_fallocate, so we must pass the test if it + // returns EINVAL. + } + _ => res.unwrap(), + } + } + + #[test] + fn errno() { + let (rd, _wr) = pipe().unwrap(); + let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err(); + match err { + Errno::EINVAL | Errno::ENODEV | Errno::ESPIPE | Errno::EBADF => (), + errno => + panic!( + "unexpected errno {}", + errno, + ), + } + } +} diff --git a/vendor/nix-v0.23.1-patched/test/test_kmod/hello_mod/Makefile b/vendor/nix-v0.23.1-patched/test/test_kmod/hello_mod/Makefile new file mode 100644 index 000000000..74c99b77e --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_kmod/hello_mod/Makefile @@ -0,0 +1,7 @@ +obj-m += hello.o + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean diff --git a/vendor/nix-v0.23.1-patched/test/test_kmod/hello_mod/hello.c b/vendor/nix-v0.23.1-patched/test/test_kmod/hello_mod/hello.c new file mode 100644 index 000000000..1c34987d2 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_kmod/hello_mod/hello.c @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: GPL-2.0+ or MIT + */ +#include +#include + +static int number= 1; +static char *who = "World"; + +module_param(number, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(myint, "Just some number"); +module_param(who, charp, 0000); +MODULE_PARM_DESC(who, "Whot to greet"); + +int init_module(void) +{ + printk(KERN_INFO "Hello %s (%d)!\n", who, number); + return 0; +} + +void cleanup_module(void) +{ + printk(KERN_INFO "Goodbye %s (%d)!\n", who, number); +} + +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/vendor/nix-v0.23.1-patched/test/test_kmod/mod.rs b/vendor/nix-v0.23.1-patched/test/test_kmod/mod.rs new file mode 100644 index 000000000..0f7fc48e2 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_kmod/mod.rs @@ -0,0 +1,166 @@ +use std::fs::copy; +use std::path::PathBuf; +use std::process::Command; +use tempfile::{tempdir, TempDir}; +use crate::*; + +fn compile_kernel_module() -> (PathBuf, String, TempDir) { + let _m = crate::FORK_MTX + .lock() + .expect("Mutex got poisoned by another test"); + + let tmp_dir = tempdir().expect("unable to create temporary build directory"); + + copy( + "test/test_kmod/hello_mod/hello.c", + &tmp_dir.path().join("hello.c"), + ).expect("unable to copy hello.c to temporary build directory"); + copy( + "test/test_kmod/hello_mod/Makefile", + &tmp_dir.path().join("Makefile"), + ).expect("unable to copy Makefile to temporary build directory"); + + let status = Command::new("make") + .current_dir(tmp_dir.path()) + .status() + .expect("failed to run make"); + + assert!(status.success()); + + // Return the relative path of the build kernel module + (tmp_dir.path().join("hello.ko"), "hello".to_owned(), tmp_dir) +} + +use nix::errno::Errno; +use nix::kmod::{delete_module, DeleteModuleFlags}; +use nix::kmod::{finit_module, init_module, ModuleInitFlags}; +use std::ffi::CString; +use std::fs::File; +use std::io::Read; + +#[test] +fn test_finit_and_delete_module() { + require_capability!("test_finit_and_delete_module", CAP_SYS_MODULE); + let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test"); + let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module(); + + let f = File::open(kmod_path).expect("unable to open kernel module"); + finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()) + .expect("unable to load kernel module"); + + delete_module( + &CString::new(kmod_name).unwrap(), + DeleteModuleFlags::empty(), + ).expect("unable to unload kernel module"); +} + +#[test] +fn test_finit_and_delete_module_with_params() { + require_capability!("test_finit_and_delete_module_with_params", CAP_SYS_MODULE); + let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test"); + let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module(); + + let f = File::open(kmod_path).expect("unable to open kernel module"); + finit_module( + &f, + &CString::new("who=Rust number=2018").unwrap(), + ModuleInitFlags::empty(), + ).expect("unable to load kernel module"); + + delete_module( + &CString::new(kmod_name).unwrap(), + DeleteModuleFlags::empty(), + ).expect("unable to unload kernel module"); +} + +#[test] +fn test_init_and_delete_module() { + require_capability!("test_init_and_delete_module", CAP_SYS_MODULE); + let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test"); + let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module(); + + let mut f = File::open(kmod_path).expect("unable to open kernel module"); + let mut contents: Vec = Vec::new(); + f.read_to_end(&mut contents) + .expect("unable to read kernel module content to buffer"); + init_module(&contents, &CString::new("").unwrap()).expect("unable to load kernel module"); + + delete_module( + &CString::new(kmod_name).unwrap(), + DeleteModuleFlags::empty(), + ).expect("unable to unload kernel module"); +} + +#[test] +fn test_init_and_delete_module_with_params() { + require_capability!("test_init_and_delete_module_with_params", CAP_SYS_MODULE); + let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test"); + let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module(); + + let mut f = File::open(kmod_path).expect("unable to open kernel module"); + let mut contents: Vec = Vec::new(); + f.read_to_end(&mut contents) + .expect("unable to read kernel module content to buffer"); + init_module(&contents, &CString::new("who=Nix number=2015").unwrap()) + .expect("unable to load kernel module"); + + delete_module( + &CString::new(kmod_name).unwrap(), + DeleteModuleFlags::empty(), + ).expect("unable to unload kernel module"); +} + +#[test] +fn test_finit_module_invalid() { + require_capability!("test_finit_module_invalid", CAP_SYS_MODULE); + let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test"); + let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let kmod_path = "/dev/zero"; + + let f = File::open(kmod_path).expect("unable to open kernel module"); + let result = finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()); + + assert_eq!(result.unwrap_err(), Errno::EINVAL); +} + +#[test] +fn test_finit_module_twice_and_delete_module() { + require_capability!("test_finit_module_twice_and_delete_module", CAP_SYS_MODULE); + let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test"); + let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module(); + + let f = File::open(kmod_path).expect("unable to open kernel module"); + finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()) + .expect("unable to load kernel module"); + + let result = finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()); + + assert_eq!(result.unwrap_err(), Errno::EEXIST); + + delete_module( + &CString::new(kmod_name).unwrap(), + DeleteModuleFlags::empty(), + ).expect("unable to unload kernel module"); +} + +#[test] +fn test_delete_module_not_loaded() { + require_capability!("test_delete_module_not_loaded", CAP_SYS_MODULE); + let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test"); + let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let result = delete_module(&CString::new("hello").unwrap(), DeleteModuleFlags::empty()); + + assert_eq!(result.unwrap_err(), Errno::ENOENT); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_mount.rs b/vendor/nix-v0.23.1-patched/test/test_mount.rs new file mode 100644 index 000000000..44287f975 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_mount.rs @@ -0,0 +1,236 @@ +mod common; + +// Impelmentation note: to allow unprivileged users to run it, this test makes +// use of user and mount namespaces. On systems that allow unprivileged user +// namespaces (Linux >= 3.8 compiled with CONFIG_USER_NS), the test should run +// without root. + +#[cfg(target_os = "linux")] +mod test_mount { + use std::fs::{self, File}; + use std::io::{self, Read, Write}; + use std::os::unix::fs::OpenOptionsExt; + use std::os::unix::fs::PermissionsExt; + use std::process::{self, Command}; + + use libc::{EACCES, EROFS}; + + use nix::errno::Errno; + use nix::mount::{mount, umount, MsFlags}; + use nix::sched::{unshare, CloneFlags}; + use nix::sys::stat::{self, Mode}; + use nix::unistd::getuid; + + static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh +exit 23"; + + const EXPECTED_STATUS: i32 = 23; + + const NONE: Option<&'static [u8]> = None; + #[allow(clippy::bind_instead_of_map)] // False positive + pub fn test_mount_tmpfs_without_flags_allows_rwx() { + let tempdir = tempfile::tempdir().unwrap(); + + mount(NONE, + tempdir.path(), + Some(b"tmpfs".as_ref()), + MsFlags::empty(), + NONE) + .unwrap_or_else(|e| panic!("mount failed: {}", e)); + + let test_path = tempdir.path().join("test"); + + // Verify write. + fs::OpenOptions::new() + .create(true) + .write(true) + .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits()) + .open(&test_path) + .or_else(|e| + if Errno::from_i32(e.raw_os_error().unwrap()) == Errno::EOVERFLOW { + // Skip tests on certain Linux kernels which have a bug + // regarding tmpfs in namespaces. + // Ubuntu 14.04 and 16.04 are known to be affected; 16.10 is + // not. There is no legitimate reason for open(2) to return + // EOVERFLOW here. + // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1659087 + let stderr = io::stderr(); + let mut handle = stderr.lock(); + writeln!(handle, "Buggy Linux kernel detected. Skipping test.") + .unwrap(); + process::exit(0); + } else { + panic!("open failed: {}", e); + } + ) + .and_then(|mut f| f.write(SCRIPT_CONTENTS)) + .unwrap_or_else(|e| panic!("write failed: {}", e)); + + // Verify read. + let mut buf = Vec::new(); + File::open(&test_path) + .and_then(|mut f| f.read_to_end(&mut buf)) + .unwrap_or_else(|e| panic!("read failed: {}", e)); + assert_eq!(buf, SCRIPT_CONTENTS); + + // Verify execute. + assert_eq!(EXPECTED_STATUS, + Command::new(&test_path) + .status() + .unwrap_or_else(|e| panic!("exec failed: {}", e)) + .code() + .unwrap_or_else(|| panic!("child killed by signal"))); + + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e)); + } + + pub fn test_mount_rdonly_disallows_write() { + let tempdir = tempfile::tempdir().unwrap(); + + mount(NONE, + tempdir.path(), + Some(b"tmpfs".as_ref()), + MsFlags::MS_RDONLY, + NONE) + .unwrap_or_else(|e| panic!("mount failed: {}", e)); + + // EROFS: Read-only file system + assert_eq!(EROFS as i32, + File::create(tempdir.path().join("test")).unwrap_err().raw_os_error().unwrap()); + + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e)); + } + + pub fn test_mount_noexec_disallows_exec() { + let tempdir = tempfile::tempdir().unwrap(); + + mount(NONE, + tempdir.path(), + Some(b"tmpfs".as_ref()), + MsFlags::MS_NOEXEC, + NONE) + .unwrap_or_else(|e| panic!("mount failed: {}", e)); + + let test_path = tempdir.path().join("test"); + + fs::OpenOptions::new() + .create(true) + .write(true) + .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits()) + .open(&test_path) + .and_then(|mut f| f.write(SCRIPT_CONTENTS)) + .unwrap_or_else(|e| panic!("write failed: {}", e)); + + // Verify that we cannot execute despite a+x permissions being set. + let mode = stat::Mode::from_bits_truncate(fs::metadata(&test_path) + .map(|md| md.permissions().mode()) + .unwrap_or_else(|e| { + panic!("metadata failed: {}", e) + })); + + assert!(mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH), + "{:?} did not have execute permissions", + &test_path); + + // EACCES: Permission denied + assert_eq!(EACCES as i32, + Command::new(&test_path).status().unwrap_err().raw_os_error().unwrap()); + + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e)); + } + + pub fn test_mount_bind() { + let tempdir = tempfile::tempdir().unwrap(); + let file_name = "test"; + + { + let mount_point = tempfile::tempdir().unwrap(); + + mount(Some(tempdir.path()), + mount_point.path(), + NONE, + MsFlags::MS_BIND, + NONE) + .unwrap_or_else(|e| panic!("mount failed: {}", e)); + + fs::OpenOptions::new() + .create(true) + .write(true) + .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits()) + .open(mount_point.path().join(file_name)) + .and_then(|mut f| f.write(SCRIPT_CONTENTS)) + .unwrap_or_else(|e| panic!("write failed: {}", e)); + + umount(mount_point.path()).unwrap_or_else(|e| panic!("umount failed: {}", e)); + } + + // Verify the file written in the mount shows up in source directory, even + // after unmounting. + + let mut buf = Vec::new(); + File::open(tempdir.path().join(file_name)) + .and_then(|mut f| f.read_to_end(&mut buf)) + .unwrap_or_else(|e| panic!("read failed: {}", e)); + assert_eq!(buf, SCRIPT_CONTENTS); + } + + pub fn setup_namespaces() { + // Hold on to the uid in the parent namespace. + let uid = getuid(); + + unshare(CloneFlags::CLONE_NEWNS | CloneFlags::CLONE_NEWUSER).unwrap_or_else(|e| { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + writeln!(handle, + "unshare failed: {}. Are unprivileged user namespaces available?", + e).unwrap(); + writeln!(handle, "mount is not being tested").unwrap(); + // Exit with success because not all systems support unprivileged user namespaces, and + // that's not what we're testing for. + process::exit(0); + }); + + // Map user as uid 1000. + fs::OpenOptions::new() + .write(true) + .open("/proc/self/uid_map") + .and_then(|mut f| f.write(format!("1000 {} 1\n", uid).as_bytes())) + .unwrap_or_else(|e| panic!("could not write uid map: {}", e)); + } +} + + +// Test runner + +/// Mimic normal test output (hackishly). +#[cfg(target_os = "linux")] +macro_rules! run_tests { + ( $($test_fn:ident),* ) => {{ + println!(); + + $( + print!("test test_mount::{} ... ", stringify!($test_fn)); + $test_fn(); + println!("ok"); + )* + + println!(); + }} +} + +#[cfg(target_os = "linux")] +fn main() { + use test_mount::{setup_namespaces, test_mount_tmpfs_without_flags_allows_rwx, + test_mount_rdonly_disallows_write, test_mount_noexec_disallows_exec, + test_mount_bind}; + skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1351"); + setup_namespaces(); + + run_tests!(test_mount_tmpfs_without_flags_allows_rwx, + test_mount_rdonly_disallows_write, + test_mount_noexec_disallows_exec, + test_mount_bind); +} + +#[cfg(not(target_os = "linux"))] +fn main() {} diff --git a/vendor/nix-v0.23.1-patched/test/test_mq.rs b/vendor/nix-v0.23.1-patched/test/test_mq.rs new file mode 100644 index 000000000..430df5ddc --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_mq.rs @@ -0,0 +1,157 @@ +use std::ffi::CString; +use std::str; + +use nix::errno::Errno; +use nix::mqueue::{mq_open, mq_close, mq_send, mq_receive, mq_attr_member_t}; +use nix::mqueue::{MqAttr, MQ_OFlag}; +use nix::sys::stat::Mode; + +#[test] +fn test_mq_send_and_receive() { + const MSG_SIZE: mq_attr_member_t = 32; + let attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name= &CString::new(b"/a_nix_test_queue".as_ref()).unwrap(); + + let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; + let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; + let r0 = mq_open(mq_name, oflag0, mode, Some(&attr)); + if let Err(Errno::ENOSYS) = r0 { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd0 = r0.unwrap(); + let msg_to_send = "msg_1"; + mq_send(mqd0, msg_to_send.as_bytes(), 1).unwrap(); + + let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY; + let mqd1 = mq_open(mq_name, oflag1, mode, Some(&attr)).unwrap(); + let mut buf = [0u8; 32]; + let mut prio = 0u32; + let len = mq_receive(mqd1, &mut buf, &mut prio).unwrap(); + assert_eq!(prio, 1); + + mq_close(mqd1).unwrap(); + mq_close(mqd0).unwrap(); + assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap()); +} + + +#[test] +#[cfg(not(any(target_os = "netbsd")))] +fn test_mq_getattr() { + use nix::mqueue::mq_getattr; + const MSG_SIZE: mq_attr_member_t = 32; + let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); + let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; + let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; + let r = mq_open(mq_name, oflag, mode, Some(&initial_attr)); + if let Err(Errno::ENOSYS) = r { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd = r.unwrap(); + + let read_attr = mq_getattr(mqd).unwrap(); + assert_eq!(read_attr, initial_attr); + mq_close(mqd).unwrap(); +} + +// FIXME: Fix failures for mips in QEMU +#[test] +#[cfg(not(any(target_os = "netbsd")))] +#[cfg_attr(all( + qemu, + any(target_arch = "mips", target_arch = "mips64") + ), ignore +)] +fn test_mq_setattr() { + use nix::mqueue::{mq_getattr, mq_setattr}; + const MSG_SIZE: mq_attr_member_t = 32; + let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); + let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; + let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; + let r = mq_open(mq_name, oflag, mode, Some(&initial_attr)); + if let Err(Errno::ENOSYS) = r { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd = r.unwrap(); + + let new_attr = MqAttr::new(0, 20, MSG_SIZE * 2, 100); + let old_attr = mq_setattr(mqd, &new_attr).unwrap(); + assert_eq!(old_attr, initial_attr); + + let new_attr_get = mq_getattr(mqd).unwrap(); + // The following tests make sense. No changes here because according to the Linux man page only + // O_NONBLOCK can be set (see tests below) + assert_ne!(new_attr_get, new_attr); + + let new_attr_non_blocking = MqAttr::new(MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t, 10, MSG_SIZE, 0); + mq_setattr(mqd, &new_attr_non_blocking).unwrap(); + let new_attr_get = mq_getattr(mqd).unwrap(); + + // now the O_NONBLOCK flag has been set + assert_ne!(new_attr_get, initial_attr); + assert_eq!(new_attr_get, new_attr_non_blocking); + mq_close(mqd).unwrap(); +} + +// FIXME: Fix failures for mips in QEMU +#[test] +#[cfg(not(any(target_os = "netbsd")))] +#[cfg_attr(all( + qemu, + any(target_arch = "mips", target_arch = "mips64") + ), ignore +)] +fn test_mq_set_nonblocking() { + use nix::mqueue::{mq_getattr, mq_set_nonblock, mq_remove_nonblock}; + const MSG_SIZE: mq_attr_member_t = 32; + let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); + let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; + let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; + let r = mq_open(mq_name, oflag, mode, Some(&initial_attr)); + if let Err(Errno::ENOSYS) = r { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd = r.unwrap(); + mq_set_nonblock(mqd).unwrap(); + let new_attr = mq_getattr(mqd); + assert_eq!(new_attr.unwrap().flags(), MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t); + mq_remove_nonblock(mqd).unwrap(); + let new_attr = mq_getattr(mqd); + assert_eq!(new_attr.unwrap().flags(), 0); + mq_close(mqd).unwrap(); +} + +#[test] +#[cfg(not(any(target_os = "netbsd")))] +fn test_mq_unlink() { + use nix::mqueue::mq_unlink; + const MSG_SIZE: mq_attr_member_t = 32; + let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap(); + let mq_name_not_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap(); + let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; + let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; + let r = mq_open(mq_name_opened, oflag, mode, Some(&initial_attr)); + if let Err(Errno::ENOSYS) = r { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd = r.unwrap(); + + let res_unlink = mq_unlink(mq_name_opened); + assert_eq!(res_unlink, Ok(()) ); + + let res_unlink_not_opened = mq_unlink(mq_name_not_opened); + assert_eq!(res_unlink_not_opened, Err(Errno::ENOENT) ); + + mq_close(mqd).unwrap(); + let res_unlink_after_close = mq_unlink(mq_name_opened); + assert_eq!(res_unlink_after_close, Err(Errno::ENOENT) ); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_net.rs b/vendor/nix-v0.23.1-patched/test/test_net.rs new file mode 100644 index 000000000..40ecd6bb7 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_net.rs @@ -0,0 +1,12 @@ +use nix::net::if_::*; + +#[cfg(any(target_os = "android", target_os = "linux"))] +const LOOPBACK: &[u8] = b"lo"; + +#[cfg(not(any(target_os = "android", target_os = "linux")))] +const LOOPBACK: &[u8] = b"lo0"; + +#[test] +fn test_if_nametoindex() { + assert!(if_nametoindex(LOOPBACK).is_ok()); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_nix_path.rs b/vendor/nix-v0.23.1-patched/test/test_nix_path.rs new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/nix-v0.23.1-patched/test/test_nmount.rs b/vendor/nix-v0.23.1-patched/test/test_nmount.rs new file mode 100644 index 000000000..4c74ecf62 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_nmount.rs @@ -0,0 +1,51 @@ +use crate::*; +use nix::{ + errno::Errno, + mount::{MntFlags, Nmount, unmount} +}; +use std::{ + ffi::CString, + fs::File, + path::Path +}; +use tempfile::tempdir; + +#[test] +fn ok() { + require_mount!("nullfs"); + + let mountpoint = tempdir().unwrap(); + let target = tempdir().unwrap(); + let _sentry = File::create(target.path().join("sentry")).unwrap(); + + let fstype = CString::new("fstype").unwrap(); + let nullfs = CString::new("nullfs").unwrap(); + Nmount::new() + .str_opt(&fstype, &nullfs) + .str_opt_owned("fspath", mountpoint.path().to_str().unwrap()) + .str_opt_owned("target", target.path().to_str().unwrap()) + .nmount(MntFlags::empty()).unwrap(); + + // Now check that the sentry is visible through the mountpoint + let exists = Path::exists(&mountpoint.path().join("sentry")); + + // Cleanup the mountpoint before asserting + unmount(mountpoint.path(), MntFlags::empty()).unwrap(); + + assert!(exists); +} + +#[test] +fn bad_fstype() { + let mountpoint = tempdir().unwrap(); + let target = tempdir().unwrap(); + let _sentry = File::create(target.path().join("sentry")).unwrap(); + + let e = Nmount::new() + .str_opt_owned("fspath", mountpoint.path().to_str().unwrap()) + .str_opt_owned("target", target.path().to_str().unwrap()) + .nmount(MntFlags::empty()).unwrap_err(); + + assert_eq!(e.error(), Errno::EINVAL); + assert_eq!(e.errmsg(), Some("Invalid fstype")); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_poll.rs b/vendor/nix-v0.23.1-patched/test/test_poll.rs new file mode 100644 index 000000000..e4b369f3f --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_poll.rs @@ -0,0 +1,82 @@ +use nix::{ + errno::Errno, + poll::{PollFlags, poll, PollFd}, + unistd::{write, pipe} +}; + +macro_rules! loop_while_eintr { + ($poll_expr: expr) => { + loop { + match $poll_expr { + Ok(nfds) => break nfds, + Err(Errno::EINTR) => (), + Err(e) => panic!("{}", e) + } + } + } +} + +#[test] +fn test_poll() { + let (r, w) = pipe().unwrap(); + let mut fds = [PollFd::new(r, PollFlags::POLLIN)]; + + // Poll an idle pipe. Should timeout + let nfds = loop_while_eintr!(poll(&mut fds, 100)); + assert_eq!(nfds, 0); + assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN)); + + write(w, b".").unwrap(); + + // Poll a readable pipe. Should return an event. + let nfds = poll(&mut fds, 100).unwrap(); + assert_eq!(nfds, 1); + assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN)); +} + +// ppoll(2) is the same as poll except for how it handles timeouts and signals. +// Repeating the test for poll(2) should be sufficient to check that our +// bindings are correct. +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux"))] +#[test] +fn test_ppoll() { + use nix::poll::ppoll; + use nix::sys::signal::SigSet; + use nix::sys::time::{TimeSpec, TimeValLike}; + + let timeout = TimeSpec::milliseconds(1); + let (r, w) = pipe().unwrap(); + let mut fds = [PollFd::new(r, PollFlags::POLLIN)]; + + // Poll an idle pipe. Should timeout + let sigset = SigSet::empty(); + let nfds = loop_while_eintr!(ppoll(&mut fds, Some(timeout), sigset)); + assert_eq!(nfds, 0); + assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN)); + + write(w, b".").unwrap(); + + // Poll a readable pipe. Should return an event. + let nfds = ppoll(&mut fds, Some(timeout), SigSet::empty()).unwrap(); + assert_eq!(nfds, 1); + assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN)); +} + +#[test] +fn test_pollfd_fd() { + use std::os::unix::io::AsRawFd; + + let pfd = PollFd::new(0x1234, PollFlags::empty()); + assert_eq!(pfd.as_raw_fd(), 0x1234); +} + +#[test] +fn test_pollfd_events() { + let mut pfd = PollFd::new(-1, PollFlags::POLLIN); + assert_eq!(pfd.events(), PollFlags::POLLIN); + pfd.set_events(PollFlags::POLLOUT); + assert_eq!(pfd.events(), PollFlags::POLLOUT); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_pty.rs b/vendor/nix-v0.23.1-patched/test/test_pty.rs new file mode 100644 index 000000000..57874de3d --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_pty.rs @@ -0,0 +1,301 @@ +use std::fs::File; +use std::io::{Read, Write}; +use std::path::Path; +use std::os::unix::prelude::*; +use tempfile::tempfile; + +use libc::{_exit, STDOUT_FILENO}; +use nix::fcntl::{OFlag, open}; +use nix::pty::*; +use nix::sys::stat; +use nix::sys::termios::*; +use nix::unistd::{write, close, pause}; + +/// Regression test for Issue #659 +/// This is the correct way to explicitly close a `PtyMaster` +#[test] +fn test_explicit_close() { + let mut f = { + let m = posix_openpt(OFlag::O_RDWR).unwrap(); + close(m.into_raw_fd()).unwrap(); + tempfile().unwrap() + }; + // This should work. But if there's been a double close, then it will + // return EBADF + f.write_all(b"whatever").unwrap(); +} + +/// Test equivalence of `ptsname` and `ptsname_r` +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptsname_equivalence() { + let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open a new PTTY master + let master_fd = posix_openpt(OFlag::O_RDWR).unwrap(); + assert!(master_fd.as_raw_fd() > 0); + + // Get the name of the slave + let slave_name = unsafe { ptsname(&master_fd) }.unwrap() ; + let slave_name_r = ptsname_r(&master_fd).unwrap(); + assert_eq!(slave_name, slave_name_r); +} + +/// Test data copying of `ptsname` +// TODO need to run in a subprocess, since ptsname is non-reentrant +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptsname_copy() { + let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open a new PTTY master + let master_fd = posix_openpt(OFlag::O_RDWR).unwrap(); + assert!(master_fd.as_raw_fd() > 0); + + // Get the name of the slave + let slave_name1 = unsafe { ptsname(&master_fd) }.unwrap(); + let slave_name2 = unsafe { ptsname(&master_fd) }.unwrap(); + assert_eq!(slave_name1, slave_name2); + // Also make sure that the string was actually copied and they point to different parts of + // memory. + assert!(slave_name1.as_ptr() != slave_name2.as_ptr()); +} + +/// Test data copying of `ptsname_r` +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptsname_r_copy() { + // Open a new PTTY master + let master_fd = posix_openpt(OFlag::O_RDWR).unwrap(); + assert!(master_fd.as_raw_fd() > 0); + + // Get the name of the slave + let slave_name1 = ptsname_r(&master_fd).unwrap(); + let slave_name2 = ptsname_r(&master_fd).unwrap(); + assert_eq!(slave_name1, slave_name2); + assert!(slave_name1.as_ptr() != slave_name2.as_ptr()); +} + +/// Test that `ptsname` returns different names for different devices +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptsname_unique() { + let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open a new PTTY master + let master1_fd = posix_openpt(OFlag::O_RDWR).unwrap(); + assert!(master1_fd.as_raw_fd() > 0); + + // Open a second PTTY master + let master2_fd = posix_openpt(OFlag::O_RDWR).unwrap(); + assert!(master2_fd.as_raw_fd() > 0); + + // Get the name of the slave + let slave_name1 = unsafe { ptsname(&master1_fd) }.unwrap(); + let slave_name2 = unsafe { ptsname(&master2_fd) }.unwrap(); + assert!(slave_name1 != slave_name2); +} + +/// Common setup for testing PTTY pairs +fn open_ptty_pair() -> (PtyMaster, File) { + let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open a new PTTY master + let master = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed"); + + // Allow a slave to be generated for it + grantpt(&master).expect("grantpt failed"); + unlockpt(&master).expect("unlockpt failed"); + + // Get the name of the slave + let slave_name = unsafe { ptsname(&master) }.expect("ptsname failed"); + + // Open the slave device + let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty()).unwrap(); + + #[cfg(target_os = "illumos")] + // TODO: rewrite using ioctl! + #[allow(clippy::comparison_chain)] + { + use libc::{ioctl, I_FIND, I_PUSH}; + + // On illumos systems, as per pts(7D), one must push STREAMS modules + // after opening a device path returned from ptsname(). + let ptem = b"ptem\0"; + let ldterm = b"ldterm\0"; + let r = unsafe { ioctl(slave_fd, I_FIND, ldterm.as_ptr()) }; + if r < 0 { + panic!("I_FIND failure"); + } else if r == 0 { + if unsafe { ioctl(slave_fd, I_PUSH, ptem.as_ptr()) } < 0 { + panic!("I_PUSH ptem failure"); + } + if unsafe { ioctl(slave_fd, I_PUSH, ldterm.as_ptr()) } < 0 { + panic!("I_PUSH ldterm failure"); + } + } + } + + let slave = unsafe { File::from_raw_fd(slave_fd) }; + + (master, slave) +} + +/// Test opening a master/slave PTTY pair +/// +/// This uses a common `open_ptty_pair` because much of these functions aren't useful by +/// themselves. So for this test we perform the basic act of getting a file handle for a +/// master/slave PTTY pair, then just sanity-check the raw values. +#[test] +fn test_open_ptty_pair() { + let (master, slave) = open_ptty_pair(); + assert!(master.as_raw_fd() > 0); + assert!(slave.as_raw_fd() > 0); +} + +/// Put the terminal in raw mode. +fn make_raw(fd: RawFd) { + let mut termios = tcgetattr(fd).unwrap(); + cfmakeraw(&mut termios); + tcsetattr(fd, SetArg::TCSANOW, &termios).unwrap(); +} + +/// Test `io::Read` on the PTTY master +#[test] +fn test_read_ptty_pair() { + let (mut master, mut slave) = open_ptty_pair(); + make_raw(slave.as_raw_fd()); + + let mut buf = [0u8; 5]; + slave.write_all(b"hello").unwrap(); + master.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"hello"); +} + +/// Test `io::Write` on the PTTY master +#[test] +fn test_write_ptty_pair() { + let (mut master, mut slave) = open_ptty_pair(); + make_raw(slave.as_raw_fd()); + + let mut buf = [0u8; 5]; + master.write_all(b"adios").unwrap(); + slave.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"adios"); +} + +#[test] +fn test_openpty() { + // openpty uses ptname(3) internally + let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + let pty = openpty(None, None).unwrap(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + + // Writing to one should be readable on the other one + let string = "foofoofoo\n"; + let mut buf = [0u8; 10]; + write(pty.master, string.as_bytes()).unwrap(); + crate::read_exact(pty.slave, &mut buf); + + assert_eq!(&buf, string.as_bytes()); + + // Read the echo as well + let echoed_string = "foofoofoo\r\n"; + let mut buf = [0u8; 11]; + crate::read_exact(pty.master, &mut buf); + assert_eq!(&buf, echoed_string.as_bytes()); + + let string2 = "barbarbarbar\n"; + let echoed_string2 = "barbarbarbar\r\n"; + let mut buf = [0u8; 14]; + write(pty.slave, string2.as_bytes()).unwrap(); + crate::read_exact(pty.master, &mut buf); + + assert_eq!(&buf, echoed_string2.as_bytes()); + + close(pty.master).unwrap(); + close(pty.slave).unwrap(); +} + +#[test] +fn test_openpty_with_termios() { + // openpty uses ptname(3) internally + let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open one pty to get attributes for the second one + let mut termios = { + let pty = openpty(None, None).unwrap(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + let termios = tcgetattr(pty.slave).unwrap(); + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + termios + }; + // Make sure newlines are not transformed so the data is preserved when sent. + termios.output_flags.remove(OutputFlags::ONLCR); + + let pty = openpty(None, &termios).unwrap(); + // Must be valid file descriptors + assert!(pty.master > 0); + assert!(pty.slave > 0); + + // Writing to one should be readable on the other one + let string = "foofoofoo\n"; + let mut buf = [0u8; 10]; + write(pty.master, string.as_bytes()).unwrap(); + crate::read_exact(pty.slave, &mut buf); + + assert_eq!(&buf, string.as_bytes()); + + // read the echo as well + let echoed_string = "foofoofoo\n"; + crate::read_exact(pty.master, &mut buf); + assert_eq!(&buf, echoed_string.as_bytes()); + + let string2 = "barbarbarbar\n"; + let echoed_string2 = "barbarbarbar\n"; + let mut buf = [0u8; 13]; + write(pty.slave, string2.as_bytes()).unwrap(); + crate::read_exact(pty.master, &mut buf); + + assert_eq!(&buf, echoed_string2.as_bytes()); + + close(pty.master).unwrap(); + close(pty.slave).unwrap(); +} + +#[test] +fn test_forkpty() { + use nix::unistd::ForkResult::*; + use nix::sys::signal::*; + use nix::sys::wait::wait; + // forkpty calls openpty which uses ptname(3) internally. + let _m0 = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + // forkpty spawns a child process + let _m1 = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + let string = "naninani\n"; + let echoed_string = "naninani\r\n"; + let pty = unsafe { + forkpty(None, None).unwrap() + }; + match pty.fork_result { + Child => { + write(STDOUT_FILENO, string.as_bytes()).unwrap(); + pause(); // we need the child to stay alive until the parent calls read + unsafe { _exit(0); } + }, + Parent { child } => { + let mut buf = [0u8; 10]; + assert!(child.as_raw() > 0); + crate::read_exact(pty.master, &mut buf); + kill(child, SIGTERM).unwrap(); + wait().unwrap(); // keep other tests using generic wait from getting our child + assert_eq!(&buf, echoed_string.as_bytes()); + close(pty.master).unwrap(); + }, + } +} diff --git a/vendor/nix-v0.23.1-patched/test/test_ptymaster_drop.rs b/vendor/nix-v0.23.1-patched/test/test_ptymaster_drop.rs new file mode 100644 index 000000000..a68f81ee1 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_ptymaster_drop.rs @@ -0,0 +1,20 @@ +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +mod t { + use nix::fcntl::OFlag; + use nix::pty::*; + use nix::unistd::close; + use std::os::unix::io::AsRawFd; + + /// Regression test for Issue #659 + /// + /// `PtyMaster` should panic rather than double close the file descriptor + /// This must run in its own test process because it deliberately creates a + /// race condition. + #[test] + #[should_panic(expected = "Closing an invalid file descriptor!")] + fn test_double_close() { + let m = posix_openpt(OFlag::O_RDWR).unwrap(); + close(m.as_raw_fd()).unwrap(); + drop(m); // should panic here + } +} diff --git a/vendor/nix-v0.23.1-patched/test/test_resource.rs b/vendor/nix-v0.23.1-patched/test/test_resource.rs new file mode 100644 index 000000000..596975009 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_resource.rs @@ -0,0 +1,23 @@ +#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))] +use nix::sys::resource::{getrlimit, setrlimit, Resource}; + +/// Tests the RLIMIT_NOFILE functionality of getrlimit(), where the resource RLIMIT_NOFILE refers +/// to the maximum file descriptor number that can be opened by the process (aka the maximum number +/// of file descriptors that the process can open, since Linux 4.5). +/// +/// We first fetch the existing file descriptor maximum values using getrlimit(), then edit the +/// soft limit to make sure it has a new and distinct value to the hard limit. We then setrlimit() +/// to put the new soft limit in effect, and then getrlimit() once more to ensure the limits have +/// been updated. +#[test] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))] +pub fn test_resource_limits_nofile() { + let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap(); + + let soft_limit = Some(soft_limit.map_or(1024, |v| v - 1)); + assert_ne!(soft_limit, hard_limit); + setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap(); + + let (new_soft_limit, _) = getrlimit(Resource::RLIMIT_NOFILE).unwrap(); + assert_eq!(new_soft_limit, soft_limit); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_sched.rs b/vendor/nix-v0.23.1-patched/test/test_sched.rs new file mode 100644 index 000000000..922196a3d --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_sched.rs @@ -0,0 +1,32 @@ +use nix::sched::{sched_getaffinity, sched_setaffinity, CpuSet}; +use nix::unistd::Pid; + +#[test] +fn test_sched_affinity() { + // If pid is zero, then the mask of the calling process is returned. + let initial_affinity = sched_getaffinity(Pid::from_raw(0)).unwrap(); + let mut at_least_one_cpu = false; + let mut last_valid_cpu = 0; + for field in 0..CpuSet::count() { + if initial_affinity.is_set(field).unwrap() { + at_least_one_cpu = true; + last_valid_cpu = field; + } + } + assert!(at_least_one_cpu); + + // Now restrict the running CPU + let mut new_affinity = CpuSet::new(); + new_affinity.set(last_valid_cpu).unwrap(); + sched_setaffinity(Pid::from_raw(0), &new_affinity).unwrap(); + + // And now re-check the affinity which should be only the one we set. + let updated_affinity = sched_getaffinity(Pid::from_raw(0)).unwrap(); + for field in 0..CpuSet::count() { + // Should be set only for the CPU we set previously + assert_eq!(updated_affinity.is_set(field).unwrap(), field==last_valid_cpu) + } + + // Finally, reset the initial CPU set + sched_setaffinity(Pid::from_raw(0), &initial_affinity).unwrap(); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_sendfile.rs b/vendor/nix-v0.23.1-patched/test/test_sendfile.rs new file mode 100644 index 000000000..b6559d329 --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_sendfile.rs @@ -0,0 +1,151 @@ +use std::io::prelude::*; +use std::os::unix::prelude::*; + +use libc::off_t; +use nix::sys::sendfile::*; +use tempfile::tempfile; + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + use nix::unistd::{close, pipe, read}; + } else if #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] { + use std::net::Shutdown; + use std::os::unix::net::UnixStream; + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_sendfile_linux() { + const CONTENTS: &[u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + + let (rd, wr) = pipe().unwrap(); + let mut offset: off_t = 5; + let res = sendfile(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap(); + + assert_eq!(2, res); + + let mut buf = [0u8; 1024]; + assert_eq!(2, read(rd, &mut buf).unwrap()); + assert_eq!(b"f1", &buf[0..2]); + assert_eq!(7, offset); + + close(rd).unwrap(); + close(wr).unwrap(); +} + +#[cfg(target_os = "linux")] +#[test] +fn test_sendfile64_linux() { + const CONTENTS: &[u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + + let (rd, wr) = pipe().unwrap(); + let mut offset: libc::off64_t = 5; + let res = sendfile64(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap(); + + assert_eq!(2, res); + + let mut buf = [0u8; 1024]; + assert_eq!(2, read(rd, &mut buf).unwrap()); + assert_eq!(b"f1", &buf[0..2]); + assert_eq!(7, offset); + + close(rd).unwrap(); + close(wr).unwrap(); +} + +#[cfg(target_os = "freebsd")] +#[test] +fn test_sendfile_freebsd() { + // Declare the content + let header_strings = vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; + let body = "Xabcdef123456"; + let body_offset = 1; + let trailer_strings = vec!["\n", "Served by Make Believe\n"]; + + // Write the body to a file + let mut tmp = tempfile().unwrap(); + tmp.write_all(body.as_bytes()).unwrap(); + + // Prepare headers and trailers for sendfile + let headers: Vec<&[u8]> = header_strings.iter().map(|s| s.as_bytes()).collect(); + let trailers: Vec<&[u8]> = trailer_strings.iter().map(|s| s.as_bytes()).collect(); + + // Prepare socket pair + let (mut rd, wr) = UnixStream::pair().unwrap(); + + // Call the test method + let (res, bytes_written) = sendfile( + tmp.as_raw_fd(), + wr.as_raw_fd(), + body_offset as off_t, + None, + Some(headers.as_slice()), + Some(trailers.as_slice()), + SfFlags::empty(), + 0, + ); + assert!(res.is_ok()); + wr.shutdown(Shutdown::Both).unwrap(); + + // Prepare the expected result + let expected_string = + header_strings.concat() + &body[body_offset..] + &trailer_strings.concat(); + + // Verify the message that was sent + assert_eq!(bytes_written as usize, expected_string.as_bytes().len()); + + let mut read_string = String::new(); + let bytes_read = rd.read_to_string(&mut read_string).unwrap(); + assert_eq!(bytes_written as usize, bytes_read); + assert_eq!(expected_string, read_string); +} + +#[cfg(any(target_os = "ios", target_os = "macos"))] +#[test] +fn test_sendfile_darwin() { + // Declare the content + let header_strings = vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; + let body = "Xabcdef123456"; + let body_offset = 1; + let trailer_strings = vec!["\n", "Served by Make Believe\n"]; + + // Write the body to a file + let mut tmp = tempfile().unwrap(); + tmp.write_all(body.as_bytes()).unwrap(); + + // Prepare headers and trailers for sendfile + let headers: Vec<&[u8]> = header_strings.iter().map(|s| s.as_bytes()).collect(); + let trailers: Vec<&[u8]> = trailer_strings.iter().map(|s| s.as_bytes()).collect(); + + // Prepare socket pair + let (mut rd, wr) = UnixStream::pair().unwrap(); + + // Call the test method + let (res, bytes_written) = sendfile( + tmp.as_raw_fd(), + wr.as_raw_fd(), + body_offset as off_t, + None, + Some(headers.as_slice()), + Some(trailers.as_slice()), + ); + assert!(res.is_ok()); + wr.shutdown(Shutdown::Both).unwrap(); + + // Prepare the expected result + let expected_string = + header_strings.concat() + &body[body_offset..] + &trailer_strings.concat(); + + // Verify the message that was sent + assert_eq!(bytes_written as usize, expected_string.as_bytes().len()); + + let mut read_string = String::new(); + let bytes_read = rd.read_to_string(&mut read_string).unwrap(); + assert_eq!(bytes_written as usize, bytes_read); + assert_eq!(expected_string, read_string); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_stat.rs b/vendor/nix-v0.23.1-patched/test/test_stat.rs new file mode 100644 index 000000000..33cf748da --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_stat.rs @@ -0,0 +1,358 @@ +#[cfg(not(target_os = "redox"))] +use std::fs; +use std::fs::File; +#[cfg(not(target_os = "redox"))] +use std::os::unix::fs::{symlink, PermissionsExt}; +use std::os::unix::prelude::AsRawFd; +#[cfg(not(target_os = "redox"))] +use std::time::{Duration, UNIX_EPOCH}; +#[cfg(not(target_os = "redox"))] +use std::path::Path; + +#[cfg(not(any(target_os = "netbsd", target_os = "redox")))] +use libc::{S_IFMT, S_IFLNK}; +use libc::mode_t; + +#[cfg(not(target_os = "redox"))] +use nix::fcntl; +#[cfg(not(target_os = "redox"))] +use nix::errno::Errno; +#[cfg(not(target_os = "redox"))] +use nix::sys::stat::{self, futimens, utimes}; +use nix::sys::stat::{fchmod, stat}; +#[cfg(not(target_os = "redox"))] +use nix::sys::stat::{fchmodat, utimensat, mkdirat}; +#[cfg(any(target_os = "linux", + target_os = "haiku", + target_os = "ios", + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd"))] +use nix::sys::stat::lutimes; +#[cfg(not(target_os = "redox"))] +use nix::sys::stat::{FchmodatFlags, UtimensatFlags}; +use nix::sys::stat::Mode; + +#[cfg(not(any(target_os = "netbsd", target_os = "redox")))] +use nix::sys::stat::FileStat; + +#[cfg(not(target_os = "redox"))] +use nix::sys::time::{TimeSpec, TimeVal, TimeValLike}; +#[cfg(not(target_os = "redox"))] +use nix::unistd::chdir; + +#[cfg(not(any(target_os = "netbsd", target_os = "redox")))] +use nix::Result; + +#[cfg(not(any(target_os = "netbsd", target_os = "redox")))] +fn assert_stat_results(stat_result: Result) { + let stats = stat_result.expect("stat call failed"); + assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent + assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent + assert!(stats.st_mode > 0); // must be positive integer + assert_eq!(stats.st_nlink, 1); // there links created, must be 1 + assert_eq!(stats.st_size, 0); // size is 0 because we did not write anything to the file + assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent + assert!(stats.st_blocks <= 16); // Up to 16 blocks can be allocated for a blank file +} + +#[cfg(not(any(target_os = "netbsd", target_os = "redox")))] +// (Android's st_blocks is ulonglong which is always non-negative.) +#[cfg_attr(target_os = "android", allow(unused_comparisons))] +#[allow(clippy::absurd_extreme_comparisons)] // Not absurd on all OSes +fn assert_lstat_results(stat_result: Result) { + let stats = stat_result.expect("stat call failed"); + assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent + assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent + assert!(stats.st_mode > 0); // must be positive integer + + // st_mode is c_uint (u32 on Android) while S_IFMT is mode_t + // (u16 on Android), and that will be a compile error. + // On other platforms they are the same (either both are u16 or u32). + assert_eq!((stats.st_mode as usize) & (S_IFMT as usize), S_IFLNK as usize); // should be a link + assert_eq!(stats.st_nlink, 1); // there links created, must be 1 + assert!(stats.st_size > 0); // size is > 0 because it points to another file + assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent + + // st_blocks depends on whether the machine's file system uses fast + // or slow symlinks, so just make sure it's not negative + assert!(stats.st_blocks >= 0); +} + +#[test] +#[cfg(not(any(target_os = "netbsd", target_os = "redox")))] +fn test_stat_and_fstat() { + use nix::sys::stat::fstat; + + let tempdir = tempfile::tempdir().unwrap(); + let filename = tempdir.path().join("foo.txt"); + let file = File::create(&filename).unwrap(); + + let stat_result = stat(&filename); + assert_stat_results(stat_result); + + let fstat_result = fstat(file.as_raw_fd()); + assert_stat_results(fstat_result); +} + +#[test] +#[cfg(not(any(target_os = "netbsd", target_os = "redox")))] +fn test_fstatat() { + let tempdir = tempfile::tempdir().unwrap(); + let filename = tempdir.path().join("foo.txt"); + File::create(&filename).unwrap(); + let dirfd = fcntl::open(tempdir.path(), + fcntl::OFlag::empty(), + stat::Mode::empty()); + + let result = stat::fstatat(dirfd.unwrap(), + &filename, + fcntl::AtFlags::empty()); + assert_stat_results(result); +} + +#[test] +#[cfg(not(any(target_os = "netbsd", target_os = "redox")))] +fn test_stat_fstat_lstat() { + use nix::sys::stat::{fstat, lstat}; + + let tempdir = tempfile::tempdir().unwrap(); + let filename = tempdir.path().join("bar.txt"); + let linkname = tempdir.path().join("barlink"); + + File::create(&filename).unwrap(); + symlink("bar.txt", &linkname).unwrap(); + let link = File::open(&linkname).unwrap(); + + // should be the same result as calling stat, + // since it's a regular file + let stat_result = stat(&filename); + assert_stat_results(stat_result); + + let lstat_result = lstat(&linkname); + assert_lstat_results(lstat_result); + + let fstat_result = fstat(link.as_raw_fd()); + assert_stat_results(fstat_result); +} + +#[test] +fn test_fchmod() { + let tempdir = tempfile::tempdir().unwrap(); + let filename = tempdir.path().join("foo.txt"); + let file = File::create(&filename).unwrap(); + + let mut mode1 = Mode::empty(); + mode1.insert(Mode::S_IRUSR); + mode1.insert(Mode::S_IWUSR); + fchmod(file.as_raw_fd(), mode1).unwrap(); + + let file_stat1 = stat(&filename).unwrap(); + assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits()); + + let mut mode2 = Mode::empty(); + mode2.insert(Mode::S_IROTH); + fchmod(file.as_raw_fd(), mode2).unwrap(); + + let file_stat2 = stat(&filename).unwrap(); + assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits()); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_fchmodat() { + let _dr = crate::DirRestore::new(); + let tempdir = tempfile::tempdir().unwrap(); + let filename = "foo.txt"; + let fullpath = tempdir.path().join(filename); + File::create(&fullpath).unwrap(); + + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + let mut mode1 = Mode::empty(); + mode1.insert(Mode::S_IRUSR); + mode1.insert(Mode::S_IWUSR); + fchmodat(Some(dirfd), filename, mode1, FchmodatFlags::FollowSymlink).unwrap(); + + let file_stat1 = stat(&fullpath).unwrap(); + assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits()); + + chdir(tempdir.path()).unwrap(); + + let mut mode2 = Mode::empty(); + mode2.insert(Mode::S_IROTH); + fchmodat(None, filename, mode2, FchmodatFlags::FollowSymlink).unwrap(); + + let file_stat2 = stat(&fullpath).unwrap(); + assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits()); +} + +/// Asserts that the atime and mtime in a file's metadata match expected values. +/// +/// The atime and mtime are expressed with a resolution of seconds because some file systems +/// (like macOS's HFS+) do not have higher granularity. +#[cfg(not(target_os = "redox"))] +fn assert_times_eq(exp_atime_sec: u64, exp_mtime_sec: u64, attr: &fs::Metadata) { + assert_eq!( + Duration::new(exp_atime_sec, 0), + attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()); + assert_eq!( + Duration::new(exp_mtime_sec, 0), + attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_utimes() { + let tempdir = tempfile::tempdir().unwrap(); + let fullpath = tempdir.path().join("file"); + drop(File::create(&fullpath).unwrap()); + + utimes(&fullpath, &TimeVal::seconds(9990), &TimeVal::seconds(5550)).unwrap(); + assert_times_eq(9990, 5550, &fs::metadata(&fullpath).unwrap()); +} + +#[test] +#[cfg(any(target_os = "linux", + target_os = "haiku", + target_os = "ios", + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd"))] +fn test_lutimes() { + let tempdir = tempfile::tempdir().unwrap(); + let target = tempdir.path().join("target"); + let fullpath = tempdir.path().join("symlink"); + drop(File::create(&target).unwrap()); + symlink(&target, &fullpath).unwrap(); + + let exp_target_metadata = fs::symlink_metadata(&target).unwrap(); + lutimes(&fullpath, &TimeVal::seconds(4560), &TimeVal::seconds(1230)).unwrap(); + assert_times_eq(4560, 1230, &fs::symlink_metadata(&fullpath).unwrap()); + + let target_metadata = fs::symlink_metadata(&target).unwrap(); + assert_eq!(exp_target_metadata.accessed().unwrap(), target_metadata.accessed().unwrap(), + "atime of symlink target was unexpectedly modified"); + assert_eq!(exp_target_metadata.modified().unwrap(), target_metadata.modified().unwrap(), + "mtime of symlink target was unexpectedly modified"); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_futimens() { + let tempdir = tempfile::tempdir().unwrap(); + let fullpath = tempdir.path().join("file"); + drop(File::create(&fullpath).unwrap()); + + let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + futimens(fd, &TimeSpec::seconds(10), &TimeSpec::seconds(20)).unwrap(); + assert_times_eq(10, 20, &fs::metadata(&fullpath).unwrap()); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_utimensat() { + let _dr = crate::DirRestore::new(); + let tempdir = tempfile::tempdir().unwrap(); + let filename = "foo.txt"; + let fullpath = tempdir.path().join(filename); + drop(File::create(&fullpath).unwrap()); + + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + utimensat(Some(dirfd), filename, &TimeSpec::seconds(12345), &TimeSpec::seconds(678), + UtimensatFlags::FollowSymlink).unwrap(); + assert_times_eq(12345, 678, &fs::metadata(&fullpath).unwrap()); + + chdir(tempdir.path()).unwrap(); + + utimensat(None, filename, &TimeSpec::seconds(500), &TimeSpec::seconds(800), + UtimensatFlags::FollowSymlink).unwrap(); + assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap()); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_mkdirat_success_path() { + let tempdir = tempfile::tempdir().unwrap(); + let filename = "example_subdir"; + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + assert!((mkdirat(dirfd, filename, Mode::S_IRWXU)).is_ok()); + assert!(Path::exists(&tempdir.path().join(filename))); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_mkdirat_success_mode() { + let expected_bits = stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits(); + let tempdir = tempfile::tempdir().unwrap(); + let filename = "example_subdir"; + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + assert!((mkdirat(dirfd, filename, Mode::S_IRWXU)).is_ok()); + let permissions = fs::metadata(tempdir.path().join(filename)).unwrap().permissions(); + let mode = permissions.mode(); + assert_eq!(mode as mode_t, expected_bits) +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_mkdirat_fail() { + let tempdir = tempfile::tempdir().unwrap(); + let not_dir_filename= "example_not_dir"; + let filename = "example_subdir_dir"; + let dirfd = fcntl::open(&tempdir.path().join(not_dir_filename), fcntl::OFlag::O_CREAT, + stat::Mode::empty()).unwrap(); + let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err(); + assert_eq!(result, Errno::ENOTDIR); +} + +#[test] +#[cfg(not(any(target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "redox")))] +fn test_mknod() { + use stat::{lstat, mknod, SFlag}; + + let file_name = "test_file"; + let tempdir = tempfile::tempdir().unwrap(); + let target = tempdir.path().join(file_name); + mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap(); + let mode = lstat(&target).unwrap().st_mode as mode_t; + assert!(mode & libc::S_IFREG == libc::S_IFREG); + assert!(mode & libc::S_IRWXU == libc::S_IRWXU); +} + +#[test] +#[cfg(not(any(target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "redox")))] +fn test_mknodat() { + use fcntl::{AtFlags, OFlag}; + use nix::dir::Dir; + use stat::{fstatat, mknodat, SFlag}; + + let file_name = "test_file"; + let tempdir = tempfile::tempdir().unwrap(); + let target_dir = Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap(); + mknodat( + target_dir.as_raw_fd(), + file_name, + SFlag::S_IFREG, + Mode::S_IRWXU, + 0, + ) + .unwrap(); + let mode = fstatat( + target_dir.as_raw_fd(), + file_name, + AtFlags::AT_SYMLINK_NOFOLLOW, + ) + .unwrap() + .st_mode as mode_t; + assert!(mode & libc::S_IFREG == libc::S_IFREG); + assert!(mode & libc::S_IRWXU == libc::S_IRWXU); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_time.rs b/vendor/nix-v0.23.1-patched/test/test_time.rs new file mode 100644 index 000000000..dc307e57b --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_time.rs @@ -0,0 +1,58 @@ +#[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux", + target_os = "android", + target_os = "emscripten", +))] +use nix::time::clock_getcpuclockid; +use nix::time::{clock_gettime, ClockId}; + +#[cfg(not(target_os = "redox"))] +#[test] +pub fn test_clock_getres() { + assert!(nix::time::clock_getres(ClockId::CLOCK_REALTIME).is_ok()); +} + +#[test] +pub fn test_clock_gettime() { + assert!(clock_gettime(ClockId::CLOCK_REALTIME).is_ok()); +} + +#[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux", + target_os = "android", + target_os = "emscripten", +))] +#[test] +pub fn test_clock_getcpuclockid() { + let clock_id = clock_getcpuclockid(nix::unistd::Pid::this()).unwrap(); + assert!(clock_gettime(clock_id).is_ok()); +} + +#[cfg(not(target_os = "redox"))] +#[test] +pub fn test_clock_id_res() { + assert!(ClockId::CLOCK_REALTIME.res().is_ok()); +} + +#[test] +pub fn test_clock_id_now() { + assert!(ClockId::CLOCK_REALTIME.now().is_ok()); +} + +#[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "linux", + target_os = "android", + target_os = "emscripten", +))] +#[test] +pub fn test_clock_id_pid_cpu_clock_id() { + assert!(ClockId::pid_cpu_clock_id(nix::unistd::Pid::this()) + .map(ClockId::now) + .is_ok()); +} diff --git a/vendor/nix-v0.23.1-patched/test/test_unistd.rs b/vendor/nix-v0.23.1-patched/test/test_unistd.rs new file mode 100644 index 000000000..3a3d49ddf --- /dev/null +++ b/vendor/nix-v0.23.1-patched/test/test_unistd.rs @@ -0,0 +1,1150 @@ +#[cfg(not(target_os = "redox"))] +use nix::fcntl::{self, open, readlink}; +use nix::fcntl::OFlag; +use nix::unistd::*; +use nix::unistd::ForkResult::*; +#[cfg(not(target_os = "redox"))] +use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction}; +use nix::sys::wait::*; +use nix::sys::stat::{self, Mode, SFlag}; +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +use nix::pty::{posix_openpt, grantpt, unlockpt, ptsname}; +use nix::errno::Errno; +use std::env; +#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))] +use std::ffi::CString; +#[cfg(not(target_os = "redox"))] +use std::fs::DirBuilder; +use std::fs::{self, File}; +use std::io::Write; +use std::os::unix::prelude::*; +#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))] +use std::path::Path; +use tempfile::{tempdir, tempfile}; +use libc::{_exit, mode_t, off_t}; + +use crate::*; + +#[test] +#[cfg(not(any(target_os = "netbsd")))] +fn test_fork_and_waitpid() { + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Safe: Child only calls `_exit`, which is signal-safe + match unsafe{fork()}.expect("Error: Fork Failed") { + Child => unsafe { _exit(0) }, + Parent { child } => { + // assert that child was created and pid > 0 + let child_raw: ::libc::pid_t = child.into(); + assert!(child_raw > 0); + let wait_status = waitpid(child, None); + match wait_status { + // assert that waitpid returned correct status and the pid is the one of the child + Ok(WaitStatus::Exited(pid_t, _)) => assert_eq!(pid_t, child), + + // panic, must never happen + s @ Ok(_) => panic!("Child exited {:?}, should never happen", s), + + // panic, waitpid should never fail + Err(s) => panic!("Error: waitpid returned Err({:?}", s) + } + + }, + } +} + +#[test] +fn test_wait() { + // Grab FORK_MTX so wait doesn't reap a different test's child process + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Safe: Child only calls `_exit`, which is signal-safe + match unsafe{fork()}.expect("Error: Fork Failed") { + Child => unsafe { _exit(0) }, + Parent { child } => { + let wait_status = wait(); + + // just assert that (any) one child returns with WaitStatus::Exited + assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0))); + }, + } +} + +#[test] +fn test_mkstemp() { + let mut path = env::temp_dir(); + path.push("nix_tempfile.XXXXXX"); + + let result = mkstemp(&path); + match result { + Ok((fd, path)) => { + close(fd).unwrap(); + unlink(path.as_path()).unwrap(); + }, + Err(e) => panic!("mkstemp failed: {}", e) + } +} + +#[test] +fn test_mkstemp_directory() { + // mkstemp should fail if a directory is given + assert!(mkstemp(&env::temp_dir()).is_err()); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_mkfifo() { + let tempdir = tempdir().unwrap(); + let mkfifo_fifo = tempdir.path().join("mkfifo_fifo"); + + mkfifo(&mkfifo_fifo, Mode::S_IRUSR).unwrap(); + + let stats = stat::stat(&mkfifo_fifo).unwrap(); + let typ = stat::SFlag::from_bits_truncate(stats.st_mode as mode_t); + assert!(typ == SFlag::S_IFIFO); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_mkfifo_directory() { + // mkfifo should fail if a directory is given + assert!(mkfifo(&env::temp_dir(), Mode::S_IRUSR).is_err()); +} + +#[test] +#[cfg(not(any( + target_os = "macos", target_os = "ios", + target_os = "android", target_os = "redox")))] +fn test_mkfifoat_none() { + let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let tempdir = tempdir().unwrap(); + let mkfifoat_fifo = tempdir.path().join("mkfifoat_fifo"); + + mkfifoat(None, &mkfifoat_fifo, Mode::S_IRUSR).unwrap(); + + let stats = stat::stat(&mkfifoat_fifo).unwrap(); + let typ = stat::SFlag::from_bits_truncate(stats.st_mode); + assert_eq!(typ, SFlag::S_IFIFO); +} + +#[test] +#[cfg(not(any( + target_os = "macos", target_os = "ios", + target_os = "android", target_os = "redox")))] +fn test_mkfifoat() { + use nix::fcntl; + + let tempdir = tempdir().unwrap(); + let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let mkfifoat_name = "mkfifoat_name"; + + mkfifoat(Some(dirfd), mkfifoat_name, Mode::S_IRUSR).unwrap(); + + let stats = stat::fstatat(dirfd, mkfifoat_name, fcntl::AtFlags::empty()).unwrap(); + let typ = stat::SFlag::from_bits_truncate(stats.st_mode); + assert_eq!(typ, SFlag::S_IFIFO); +} + +#[test] +#[cfg(not(any( + target_os = "macos", target_os = "ios", + target_os = "android", target_os = "redox")))] +fn test_mkfifoat_directory_none() { + let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + // mkfifoat should fail if a directory is given + assert!(!mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR).is_ok()); +} + +#[test] +#[cfg(not(any( + target_os = "macos", target_os = "ios", + target_os = "android", target_os = "redox")))] +fn test_mkfifoat_directory() { + // mkfifoat should fail if a directory is given + let tempdir = tempdir().unwrap(); + let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let mkfifoat_dir = "mkfifoat_dir"; + stat::mkdirat(dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap(); + + assert!(!mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR).is_ok()); +} + +#[test] +fn test_getpid() { + let pid: ::libc::pid_t = getpid().into(); + let ppid: ::libc::pid_t = getppid().into(); + assert!(pid > 0); + assert!(ppid > 0); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_getsid() { + let none_sid: ::libc::pid_t = getsid(None).unwrap().into(); + let pid_sid: ::libc::pid_t = getsid(Some(getpid())).unwrap().into(); + assert!(none_sid > 0); + assert_eq!(none_sid, pid_sid); +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod linux_android { + use nix::unistd::gettid; + + #[test] + fn test_gettid() { + let tid: ::libc::pid_t = gettid().into(); + assert!(tid > 0); + } +} + +#[test] +// `getgroups()` and `setgroups()` do not behave as expected on Apple platforms +#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "fuchsia")))] +fn test_setgroups() { + // Skip this test when not run as root as `setgroups()` requires root. + skip_if_not_root!("test_setgroups"); + + let _m = crate::GROUPS_MTX.lock().expect("Mutex got poisoned by another test"); + + // Save the existing groups + let old_groups = getgroups().unwrap(); + + // Set some new made up groups + let groups = [Gid::from_raw(123), Gid::from_raw(456)]; + setgroups(&groups).unwrap(); + + let new_groups = getgroups().unwrap(); + assert_eq!(new_groups, groups); + + // Revert back to the old groups + setgroups(&old_groups).unwrap(); +} + +#[test] +// `getgroups()` and `setgroups()` do not behave as expected on Apple platforms +#[cfg(not(any(target_os = "ios", + target_os = "macos", + target_os = "redox", + target_os = "fuchsia", + target_os = "illumos")))] +fn test_initgroups() { + // Skip this test when not run as root as `initgroups()` and `setgroups()` + // require root. + skip_if_not_root!("test_initgroups"); + + let _m = crate::GROUPS_MTX.lock().expect("Mutex got poisoned by another test"); + + // Save the existing groups + let old_groups = getgroups().unwrap(); + + // It doesn't matter if the root user is not called "root" or if a user + // called "root" doesn't exist. We are just checking that the extra, + // made-up group, `123`, is set. + // FIXME: Test the other half of initgroups' functionality: whether the + // groups that the user belongs to are also set. + let user = CString::new("root").unwrap(); + let group = Gid::from_raw(123); + let group_list = getgrouplist(&user, group).unwrap(); + assert!(group_list.contains(&group)); + + initgroups(&user, group).unwrap(); + + let new_groups = getgroups().unwrap(); + assert_eq!(new_groups, group_list); + + // Revert back to the old groups + setgroups(&old_groups).unwrap(); +} + +#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))] +macro_rules! execve_test_factory( + ($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => ( + + #[cfg(test)] + mod $test_name { + use std::ffi::CStr; + use super::*; + + const EMPTY: &'static [u8] = b"\0"; + const DASH_C: &'static [u8] = b"-c\0"; + const BIGARG: &'static [u8] = b"echo nix!!! && echo foo=$foo && echo baz=$baz\0"; + const FOO: &'static [u8] = b"foo=bar\0"; + const BAZ: &'static [u8] = b"baz=quux\0"; + + fn syscall_cstr_ref() -> Result { + $syscall( + $exe, + $(CString::new($pathname).unwrap().as_c_str(), )* + &[CStr::from_bytes_with_nul(EMPTY).unwrap(), + CStr::from_bytes_with_nul(DASH_C).unwrap(), + CStr::from_bytes_with_nul(BIGARG).unwrap()], + &[CStr::from_bytes_with_nul(FOO).unwrap(), + CStr::from_bytes_with_nul(BAZ).unwrap()] + $(, $flags)*) + } + + fn syscall_cstring() -> Result { + $syscall( + $exe, + $(CString::new($pathname).unwrap().as_c_str(), )* + &[CString::from(CStr::from_bytes_with_nul(EMPTY).unwrap()), + CString::from(CStr::from_bytes_with_nul(DASH_C).unwrap()), + CString::from(CStr::from_bytes_with_nul(BIGARG).unwrap())], + &[CString::from(CStr::from_bytes_with_nul(FOO).unwrap()), + CString::from(CStr::from_bytes_with_nul(BAZ).unwrap())] + $(, $flags)*) + } + + fn common_test(syscall: fn() -> Result) { + if "execveat" == stringify!($syscall) { + // Though undocumented, Docker's default seccomp profile seems to + // block this syscall. https://github.com/nix-rust/nix/issues/1122 + skip_if_seccomp!($test_name); + } + + let m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + // The `exec`d process will write to `writer`, and we'll read that + // data from `reader`. + let (reader, writer) = pipe().unwrap(); + + // Safe: Child calls `exit`, `dup`, `close` and the provided `exec*` family function. + // NOTE: Technically, this makes the macro unsafe to use because you could pass anything. + // The tests make sure not to do that, though. + match unsafe{fork()}.unwrap() { + Child => { + // Make `writer` be the stdout of the new process. + dup2(writer, 1).unwrap(); + let r = syscall(); + let _ = std::io::stderr() + .write_all(format!("{:?}", r).as_bytes()); + // Should only get here in event of error + unsafe{ _exit(1) }; + }, + Parent { child } => { + // Wait for the child to exit. + let ws = waitpid(child, None); + drop(m); + assert_eq!(ws, Ok(WaitStatus::Exited(child, 0))); + // Read 1024 bytes. + let mut buf = [0u8; 1024]; + read(reader, &mut buf).unwrap(); + // It should contain the things we printed using `/bin/sh`. + let string = String::from_utf8_lossy(&buf); + assert!(string.contains("nix!!!")); + assert!(string.contains("foo=bar")); + assert!(string.contains("baz=quux")); + } + } + } + + // These tests frequently fail on musl, probably due to + // https://github.com/nix-rust/nix/issues/555 + #[cfg_attr(target_env = "musl", ignore)] + #[test] + fn test_cstr_ref() { + common_test(syscall_cstr_ref); + } + + // These tests frequently fail on musl, probably due to + // https://github.com/nix-rust/nix/issues/555 + #[cfg_attr(target_env = "musl", ignore)] + #[test] + fn test_cstring() { + common_test(syscall_cstring); + } + } + + ) +); + +cfg_if!{ + if #[cfg(target_os = "android")] { + execve_test_factory!(test_execve, execve, CString::new("/system/bin/sh").unwrap().as_c_str()); + execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd()); + } else if #[cfg(any(target_os = "freebsd", + target_os = "linux"))] { + // These tests frequently fail on musl, probably due to + // https://github.com/nix-rust/nix/issues/555 + execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str()); + execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd()); + } else if #[cfg(any(target_os = "dragonfly", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris"))] { + execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str()); + // No fexecve() on DragonFly, ios, macos, NetBSD, OpenBSD. + // + // Note for NetBSD and OpenBSD: although rust-lang/libc includes it + // (under unix/bsd/netbsdlike/) fexecve is not currently implemented on + // NetBSD nor on OpenBSD. + } +} + +#[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))] +execve_test_factory!(test_execvpe, execvpe, &CString::new("sh").unwrap()); + +cfg_if!{ + if #[cfg(target_os = "android")] { + use nix::fcntl::AtFlags; + execve_test_factory!(test_execveat_empty, execveat, + File::open("/system/bin/sh").unwrap().into_raw_fd(), + "", AtFlags::AT_EMPTY_PATH); + execve_test_factory!(test_execveat_relative, execveat, + File::open("/system/bin/").unwrap().into_raw_fd(), + "./sh", AtFlags::empty()); + execve_test_factory!(test_execveat_absolute, execveat, + File::open("/").unwrap().into_raw_fd(), + "/system/bin/sh", AtFlags::empty()); + } else if #[cfg(all(target_os = "linux", any(target_arch ="x86_64", target_arch ="x86")))] { + use nix::fcntl::AtFlags; + execve_test_factory!(test_execveat_empty, execveat, File::open("/bin/sh").unwrap().into_raw_fd(), + "", AtFlags::AT_EMPTY_PATH); + execve_test_factory!(test_execveat_relative, execveat, File::open("/bin/").unwrap().into_raw_fd(), + "./sh", AtFlags::empty()); + execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(), + "/bin/sh", AtFlags::empty()); + } +} + +#[test] +#[cfg(not(target_os = "fuchsia"))] +fn test_fchdir() { + // fchdir changes the process's cwd + let _dr = crate::DirRestore::new(); + + let tmpdir = tempdir().unwrap(); + let tmpdir_path = tmpdir.path().canonicalize().unwrap(); + let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd(); + + assert!(fchdir(tmpdir_fd).is_ok()); + assert_eq!(getcwd().unwrap(), tmpdir_path); + + assert!(close(tmpdir_fd).is_ok()); +} + +#[test] +fn test_getcwd() { + // chdir changes the process's cwd + let _dr = crate::DirRestore::new(); + + let tmpdir = tempdir().unwrap(); + let tmpdir_path = tmpdir.path().canonicalize().unwrap(); + assert!(chdir(&tmpdir_path).is_ok()); + assert_eq!(getcwd().unwrap(), tmpdir_path); + + // make path 500 chars longer so that buffer doubling in getcwd + // kicks in. Note: One path cannot be longer than 255 bytes + // (NAME_MAX) whole path cannot be longer than PATH_MAX (usually + // 4096 on linux, 1024 on macos) + let mut inner_tmp_dir = tmpdir_path; + for _ in 0..5 { + let newdir = "a".repeat(100); + inner_tmp_dir.push(newdir); + assert!(mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU).is_ok()); + } + assert!(chdir(inner_tmp_dir.as_path()).is_ok()); + assert_eq!(getcwd().unwrap(), inner_tmp_dir.as_path()); +} + +#[test] +fn test_chown() { + // Testing for anything other than our own UID/GID is hard. + let uid = Some(getuid()); + let gid = Some(getgid()); + + let tempdir = tempdir().unwrap(); + let path = tempdir.path().join("file"); + { + File::create(&path).unwrap(); + } + + chown(&path, uid, gid).unwrap(); + chown(&path, uid, None).unwrap(); + chown(&path, None, gid).unwrap(); + + fs::remove_file(&path).unwrap(); + chown(&path, uid, gid).unwrap_err(); +} + +#[test] +fn test_fchown() { + // Testing for anything other than our own UID/GID is hard. + let uid = Some(getuid()); + let gid = Some(getgid()); + + let path = tempfile().unwrap(); + let fd = path.as_raw_fd(); + + fchown(fd, uid, gid).unwrap(); + fchown(fd, uid, None).unwrap(); + fchown(fd, None, gid).unwrap(); + fchown(999999999, uid, gid).unwrap_err(); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_fchownat() { + let _dr = crate::DirRestore::new(); + // Testing for anything other than our own UID/GID is hard. + let uid = Some(getuid()); + let gid = Some(getgid()); + + let tempdir = tempdir().unwrap(); + let path = tempdir.path().join("file"); + { + File::create(&path).unwrap(); + } + + let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); + + fchownat(Some(dirfd), "file", uid, gid, FchownatFlags::FollowSymlink).unwrap(); + + chdir(tempdir.path()).unwrap(); + fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap(); + + fs::remove_file(&path).unwrap(); + fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap_err(); +} + +#[test] +fn test_lseek() { + const CONTENTS: &[u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + let tmpfd = tmp.into_raw_fd(); + + let offset: off_t = 5; + lseek(tmpfd, offset, Whence::SeekSet).unwrap(); + + let mut buf = [0u8; 7]; + crate::read_exact(tmpfd, &mut buf); + assert_eq!(b"f123456", &buf); + + close(tmpfd).unwrap(); +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +#[test] +fn test_lseek64() { + const CONTENTS: &[u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + let tmpfd = tmp.into_raw_fd(); + + lseek64(tmpfd, 5, Whence::SeekSet).unwrap(); + + let mut buf = [0u8; 7]; + crate::read_exact(tmpfd, &mut buf); + assert_eq!(b"f123456", &buf); + + close(tmpfd).unwrap(); +} + +cfg_if!{ + if #[cfg(any(target_os = "android", target_os = "linux"))] { + macro_rules! require_acct{ + () => { + require_capability!("test_acct", CAP_SYS_PACCT); + } + } + } else if #[cfg(target_os = "freebsd")] { + macro_rules! require_acct{ + () => { + skip_if_not_root!("test_acct"); + skip_if_jailed!("test_acct"); + } + } + } else if #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] { + macro_rules! require_acct{ + () => { + skip_if_not_root!("test_acct"); + } + } + } +} + +#[test] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +fn test_acct() { + use tempfile::NamedTempFile; + use std::process::Command; + use std::{thread, time}; + + let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + require_acct!(); + + let file = NamedTempFile::new().unwrap(); + let path = file.path().to_str().unwrap(); + + acct::enable(path).unwrap(); + + loop { + Command::new("echo").arg("Hello world"); + let len = fs::metadata(path).unwrap().len(); + if len > 0 { break; } + thread::sleep(time::Duration::from_millis(10)); + } + acct::disable().unwrap(); +} + +#[test] +fn test_fpathconf_limited() { + let f = tempfile().unwrap(); + // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test + let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX); + assert!(path_max.expect("fpathconf failed").expect("PATH_MAX is unlimited") > 0); +} + +#[test] +fn test_pathconf_limited() { + // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test + let path_max = pathconf("/", PathconfVar::PATH_MAX); + assert!(path_max.expect("pathconf failed").expect("PATH_MAX is unlimited") > 0); +} + +#[test] +fn test_sysconf_limited() { + // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test + let open_max = sysconf(SysconfVar::OPEN_MAX); + assert!(open_max.expect("sysconf failed").expect("OPEN_MAX is unlimited") > 0); +} + +#[cfg(target_os = "freebsd")] +#[test] +fn test_sysconf_unsupported() { + // I know of no sysconf variables that are unsupported everywhere, but + // _XOPEN_CRYPT is unsupported on FreeBSD 11.0, which is one of the platforms + // we test. + let open_max = sysconf(SysconfVar::_XOPEN_CRYPT); + assert!(open_max.expect("sysconf failed").is_none()) +} + + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_getresuid() { + let resuids = getresuid().unwrap(); + assert!(resuids.real.as_raw() != libc::uid_t::max_value()); + assert!(resuids.effective.as_raw() != libc::uid_t::max_value()); + assert!(resuids.saved.as_raw() != libc::uid_t::max_value()); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_getresgid() { + let resgids = getresgid().unwrap(); + assert!(resgids.real.as_raw() != libc::gid_t::max_value()); + assert!(resgids.effective.as_raw() != libc::gid_t::max_value()); + assert!(resgids.saved.as_raw() != libc::gid_t::max_value()); +} + +// Test that we can create a pair of pipes. No need to verify that they pass +// data; that's the domain of the OS, not nix. +#[test] +fn test_pipe() { + let (fd0, fd1) = pipe().unwrap(); + let m0 = stat::SFlag::from_bits_truncate(stat::fstat(fd0).unwrap().st_mode as mode_t); + // S_IFIFO means it's a pipe + assert_eq!(m0, SFlag::S_IFIFO); + let m1 = stat::SFlag::from_bits_truncate(stat::fstat(fd1).unwrap().st_mode as mode_t); + assert_eq!(m1, SFlag::S_IFIFO); +} + +// pipe2(2) is the same as pipe(2), except it allows setting some flags. Check +// that we can set a flag. +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox", + target_os = "solaris"))] +#[test] +fn test_pipe2() { + use nix::fcntl::{fcntl, FcntlArg, FdFlag}; + + let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap(); + let f0 = FdFlag::from_bits_truncate(fcntl(fd0, FcntlArg::F_GETFD).unwrap()); + assert!(f0.contains(FdFlag::FD_CLOEXEC)); + let f1 = FdFlag::from_bits_truncate(fcntl(fd1, FcntlArg::F_GETFD).unwrap()); + assert!(f1.contains(FdFlag::FD_CLOEXEC)); +} + +#[test] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +fn test_truncate() { + let tempdir = tempdir().unwrap(); + let path = tempdir.path().join("file"); + + { + let mut tmp = File::create(&path).unwrap(); + const CONTENTS: &[u8] = b"12345678"; + tmp.write_all(CONTENTS).unwrap(); + } + + truncate(&path, 4).unwrap(); + + let metadata = fs::metadata(&path).unwrap(); + assert_eq!(4, metadata.len()); +} + +#[test] +fn test_ftruncate() { + let tempdir = tempdir().unwrap(); + let path = tempdir.path().join("file"); + + let tmpfd = { + let mut tmp = File::create(&path).unwrap(); + const CONTENTS: &[u8] = b"12345678"; + tmp.write_all(CONTENTS).unwrap(); + tmp.into_raw_fd() + }; + + ftruncate(tmpfd, 2).unwrap(); + close(tmpfd).unwrap(); + + let metadata = fs::metadata(&path).unwrap(); + assert_eq!(2, metadata.len()); +} + +// Used in `test_alarm`. +#[cfg(not(target_os = "redox"))] +static mut ALARM_CALLED: bool = false; + +// Used in `test_alarm`. +#[cfg(not(target_os = "redox"))] +pub extern fn alarm_signal_handler(raw_signal: libc::c_int) { + assert_eq!(raw_signal, libc::SIGALRM, "unexpected signal: {}", raw_signal); + unsafe { ALARM_CALLED = true }; +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_alarm() { + use std::{ + time::{Duration, Instant,}, + thread + }; + + // Maybe other tests that fork interfere with this one? + let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + let handler = SigHandler::Handler(alarm_signal_handler); + let signal_action = SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty()); + let old_handler = unsafe { + sigaction(Signal::SIGALRM, &signal_action) + .expect("unable to set signal handler for alarm") + }; + + // Set an alarm. + assert_eq!(alarm::set(60), None); + + // Overwriting an alarm should return the old alarm. + assert_eq!(alarm::set(1), Some(60)); + + // We should be woken up after 1 second by the alarm, so we'll sleep for 3 + // seconds to be sure. + let starttime = Instant::now(); + loop { + thread::sleep(Duration::from_millis(100)); + if unsafe { ALARM_CALLED} { + break; + } + if starttime.elapsed() > Duration::from_secs(3) { + panic!("Timeout waiting for SIGALRM"); + } + } + + // Reset the signal. + unsafe { + sigaction(Signal::SIGALRM, &old_handler) + .expect("unable to set signal handler for alarm"); + } +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_canceling_alarm() { + let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + assert_eq!(alarm::cancel(), None); + + assert_eq!(alarm::set(60), None); + assert_eq!(alarm::cancel(), Some(60)); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_symlinkat() { + let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let tempdir = tempdir().unwrap(); + + let target = tempdir.path().join("a"); + let linkpath = tempdir.path().join("b"); + symlinkat(&target, None, &linkpath).unwrap(); + assert_eq!( + readlink(&linkpath).unwrap().to_str().unwrap(), + target.to_str().unwrap() + ); + + let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let target = "c"; + let linkpath = "d"; + symlinkat(target, Some(dirfd), linkpath).unwrap(); + assert_eq!( + readlink(&tempdir.path().join(linkpath)) + .unwrap() + .to_str() + .unwrap(), + target + ); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_linkat_file() { + let tempdir = tempdir().unwrap(); + let oldfilename = "foo.txt"; + let oldfilepath = tempdir.path().join(oldfilename); + + let newfilename = "bar.txt"; + let newfilepath = tempdir.path().join(newfilename); + + // Create file + File::create(&oldfilepath).unwrap(); + + // Get file descriptor for base directory + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + // Attempt hard link file at relative path + linkat(Some(dirfd), oldfilename, Some(dirfd), newfilename, LinkatFlags::SymlinkFollow).unwrap(); + assert!(newfilepath.exists()); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_linkat_olddirfd_none() { + let _dr = crate::DirRestore::new(); + + let tempdir_oldfile = tempdir().unwrap(); + let oldfilename = "foo.txt"; + let oldfilepath = tempdir_oldfile.path().join(oldfilename); + + let tempdir_newfile = tempdir().unwrap(); + let newfilename = "bar.txt"; + let newfilepath = tempdir_newfile.path().join(newfilename); + + // Create file + File::create(&oldfilepath).unwrap(); + + // Get file descriptor for base directory of new file + let dirfd = fcntl::open(tempdir_newfile.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + // Attempt hard link file using curent working directory as relative path for old file path + chdir(tempdir_oldfile.path()).unwrap(); + linkat(None, oldfilename, Some(dirfd), newfilename, LinkatFlags::SymlinkFollow).unwrap(); + assert!(newfilepath.exists()); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_linkat_newdirfd_none() { + let _dr = crate::DirRestore::new(); + + let tempdir_oldfile = tempdir().unwrap(); + let oldfilename = "foo.txt"; + let oldfilepath = tempdir_oldfile.path().join(oldfilename); + + let tempdir_newfile = tempdir().unwrap(); + let newfilename = "bar.txt"; + let newfilepath = tempdir_newfile.path().join(newfilename); + + // Create file + File::create(&oldfilepath).unwrap(); + + // Get file descriptor for base directory of old file + let dirfd = fcntl::open(tempdir_oldfile.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + // Attempt hard link file using current working directory as relative path for new file path + chdir(tempdir_newfile.path()).unwrap(); + linkat(Some(dirfd), oldfilename, None, newfilename, LinkatFlags::SymlinkFollow).unwrap(); + assert!(newfilepath.exists()); +} + +#[test] +#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))] +fn test_linkat_no_follow_symlink() { + let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let tempdir = tempdir().unwrap(); + let oldfilename = "foo.txt"; + let oldfilepath = tempdir.path().join(oldfilename); + + let symoldfilename = "symfoo.txt"; + let symoldfilepath = tempdir.path().join(symoldfilename); + + let newfilename = "nofollowsymbar.txt"; + let newfilepath = tempdir.path().join(newfilename); + + // Create file + File::create(&oldfilepath).unwrap(); + + // Create symlink to file + symlinkat(&oldfilepath, None, &symoldfilepath).unwrap(); + + // Get file descriptor for base directory + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + // Attempt link symlink of file at relative path + linkat(Some(dirfd), symoldfilename, Some(dirfd), newfilename, LinkatFlags::NoSymlinkFollow).unwrap(); + + // Assert newfile is actually a symlink to oldfile. + assert_eq!( + readlink(&newfilepath) + .unwrap() + .to_str() + .unwrap(), + oldfilepath.to_str().unwrap() + ); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_linkat_follow_symlink() { + let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test"); + + let tempdir = tempdir().unwrap(); + let oldfilename = "foo.txt"; + let oldfilepath = tempdir.path().join(oldfilename); + + let symoldfilename = "symfoo.txt"; + let symoldfilepath = tempdir.path().join(symoldfilename); + + let newfilename = "nofollowsymbar.txt"; + let newfilepath = tempdir.path().join(newfilename); + + // Create file + File::create(&oldfilepath).unwrap(); + + // Create symlink to file + symlinkat(&oldfilepath, None, &symoldfilepath).unwrap(); + + // Get file descriptor for base directory + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + // Attempt link target of symlink of file at relative path + linkat(Some(dirfd), symoldfilename, Some(dirfd), newfilename, LinkatFlags::SymlinkFollow).unwrap(); + + let newfilestat = stat::stat(&newfilepath).unwrap(); + + // Check the file type of the new link + assert_eq!((stat::SFlag::from_bits_truncate(newfilestat.st_mode as mode_t) & SFlag::S_IFMT), + SFlag::S_IFREG + ); + + // Check the number of hard links to the original file + assert_eq!(newfilestat.st_nlink, 2); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_unlinkat_dir_noremovedir() { + let tempdir = tempdir().unwrap(); + let dirname = "foo_dir"; + let dirpath = tempdir.path().join(dirname); + + // Create dir + DirBuilder::new().recursive(true).create(&dirpath).unwrap(); + + // Get file descriptor for base directory + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + // Attempt unlink dir at relative path without proper flag + let err_result = unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err(); + assert!(err_result == Errno::EISDIR || err_result == Errno::EPERM); + } + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_unlinkat_dir_removedir() { + let tempdir = tempdir().unwrap(); + let dirname = "foo_dir"; + let dirpath = tempdir.path().join(dirname); + + // Create dir + DirBuilder::new().recursive(true).create(&dirpath).unwrap(); + + // Get file descriptor for base directory + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + // Attempt unlink dir at relative path with proper flag + unlinkat(Some(dirfd), dirname, UnlinkatFlags::RemoveDir).unwrap(); + assert!(!dirpath.exists()); + } + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_unlinkat_file() { + let tempdir = tempdir().unwrap(); + let filename = "foo.txt"; + let filepath = tempdir.path().join(filename); + + // Create file + File::create(&filepath).unwrap(); + + // Get file descriptor for base directory + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + + // Attempt unlink file at relative path + unlinkat(Some(dirfd), filename, UnlinkatFlags::NoRemoveDir).unwrap(); + assert!(!filepath.exists()); + } + +#[test] +fn test_access_not_existing() { + let tempdir = tempdir().unwrap(); + let dir = tempdir.path().join("does_not_exist.txt"); + assert_eq!(access(&dir, AccessFlags::F_OK).err().unwrap(), + Errno::ENOENT); +} + +#[test] +fn test_access_file_exists() { + let tempdir = tempdir().unwrap(); + let path = tempdir.path().join("does_exist.txt"); + let _file = File::create(path.clone()).unwrap(); + assert!(access(&path, AccessFlags::R_OK | AccessFlags::W_OK).is_ok()); +} + +#[cfg(not(target_os = "redox"))] +#[test] +fn test_user_into_passwd() { + // get the UID of the "nobody" user + let nobody = User::from_name("nobody").unwrap().unwrap(); + let pwd: libc::passwd = nobody.into(); + let _: User = (&pwd).into(); +} + +/// Tests setting the filesystem UID with `setfsuid`. +#[cfg(any(target_os = "linux", target_os = "android"))] +#[test] +fn test_setfsuid() { + use std::os::unix::fs::PermissionsExt; + use std::{fs, io, thread}; + require_capability!("test_setfsuid", CAP_SETUID); + + // get the UID of the "nobody" user + let nobody = User::from_name("nobody").unwrap().unwrap(); + + // create a temporary file with permissions '-rw-r-----' + let file = tempfile::NamedTempFile::new_in("/var/tmp").unwrap(); + let temp_path = file.into_temp_path(); + dbg!(&temp_path); + let temp_path_2 = (&temp_path).to_path_buf(); + let mut permissions = fs::metadata(&temp_path).unwrap().permissions(); + permissions.set_mode(0o640); + + // spawn a new thread where to test setfsuid + thread::spawn(move || { + // set filesystem UID + let fuid = setfsuid(nobody.uid); + // trying to open the temporary file should fail with EACCES + let res = fs::File::open(&temp_path); + assert!(res.is_err()); + assert_eq!(res.err().unwrap().kind(), io::ErrorKind::PermissionDenied); + + // assert fuid actually changes + let prev_fuid = setfsuid(Uid::from_raw(-1i32 as u32)); + assert_ne!(prev_fuid, fuid); + }) + .join() + .unwrap(); + + // open the temporary file with the current thread filesystem UID + fs::File::open(temp_path_2).unwrap(); +} + +#[test] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +fn test_ttyname() { + let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed"); + assert!(fd.as_raw_fd() > 0); + + // on linux, we can just call ttyname on the pty master directly, but + // apparently osx requires that ttyname is called on a slave pty (can't + // find this documented anywhere, but it seems to empirically be the case) + grantpt(&fd).expect("grantpt failed"); + unlockpt(&fd).expect("unlockpt failed"); + let sname = unsafe { ptsname(&fd) }.expect("ptsname failed"); + let fds = open( + Path::new(&sname), + OFlag::O_RDWR, + stat::Mode::empty(), + ).expect("open failed"); + assert!(fds > 0); + + let name = ttyname(fds).expect("ttyname failed"); + assert!(name.starts_with("/dev")); +} + +#[test] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +fn test_ttyname_not_pty() { + let fd = File::open("/dev/zero").unwrap(); + assert!(fd.as_raw_fd() > 0); + assert_eq!(ttyname(fd.as_raw_fd()), Err(Errno::ENOTTY)); +} + +#[test] +#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] +fn test_ttyname_invalid_fd() { + assert_eq!(ttyname(-1), Err(Errno::EBADF)); +} + +#[test] +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "dragonfly", +))] +fn test_getpeereid() { + use std::os::unix::net::UnixStream; + let (sock_a, sock_b) = UnixStream::pair().unwrap(); + + let (uid_a, gid_a) = getpeereid(sock_a.as_raw_fd()).unwrap(); + let (uid_b, gid_b) = getpeereid(sock_b.as_raw_fd()).unwrap(); + + let uid = geteuid(); + let gid = getegid(); + + assert_eq!(uid, uid_a); + assert_eq!(gid, gid_a); + assert_eq!(uid_a, uid_b); + assert_eq!(gid_a, gid_b); +} + +#[test] +#[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "dragonfly", +))] +fn test_getpeereid_invalid_fd() { + // getpeereid is not POSIX, so error codes are inconsistent between different Unices. + assert!(getpeereid(-1).is_err()); +} From 03e0cbb0208c97840dd9b1a17a23c3954fd91dfb Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sun, 14 Nov 2021 16:35:50 -0600 Subject: [PATCH 125/131] update 'nix' within workspace to force patched version --- src/uu/cat/Cargo.toml | 2 +- src/uu/more/Cargo.toml | 2 +- src/uu/nice/Cargo.toml | 2 +- src/uu/tail/Cargo.toml | 2 +- src/uu/timeout/Cargo.toml | 2 +- src/uu/wc/Cargo.toml | 2 +- src/uu/yes/Cargo.toml | 2 +- src/uucore/Cargo.toml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/uu/cat/Cargo.toml b/src/uu/cat/Cargo.toml index c292aade6..0374e80d9 100644 --- a/src/uu/cat/Cargo.toml +++ b/src/uu/cat/Cargo.toml @@ -23,7 +23,7 @@ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_p [target.'cfg(unix)'.dependencies] unix_socket = "0.5.0" -nix = "0.20.0" +nix = "=0.23.1" [target.'cfg(windows)'.dependencies] winapi-util = "0.1.5" diff --git a/src/uu/more/Cargo.toml b/src/uu/more/Cargo.toml index 41aa469c8..c720ba1de 100644 --- a/src/uu/more/Cargo.toml +++ b/src/uu/more/Cargo.toml @@ -28,7 +28,7 @@ redox_termios = "0.1" redox_syscall = "0.2" [target.'cfg(all(unix, not(target_os = "fuchsia")))'.dependencies] -nix = "0.19" +nix = "=0.23.1" [[bin]] name = "more" diff --git a/src/uu/nice/Cargo.toml b/src/uu/nice/Cargo.toml index b168ed98e..19625b7f2 100644 --- a/src/uu/nice/Cargo.toml +++ b/src/uu/nice/Cargo.toml @@ -17,7 +17,7 @@ path = "src/nice.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -nix = "0.20" +nix = "=0.23.1" uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/tail/Cargo.toml b/src/uu/tail/Cargo.toml index af36585a2..55878ca63 100644 --- a/src/uu/tail/Cargo.toml +++ b/src/uu/tail/Cargo.toml @@ -27,7 +27,7 @@ winapi = { version="0.3", features=["fileapi", "handleapi", "processthreadsapi", redox_syscall = "0.2" [target.'cfg(unix)'.dependencies] -nix = "0.20" +nix = "=0.23.1" [[bin]] name = "tail" diff --git a/src/uu/timeout/Cargo.toml b/src/uu/timeout/Cargo.toml index 36ad3cb75..87ef01a72 100644 --- a/src/uu/timeout/Cargo.toml +++ b/src/uu/timeout/Cargo.toml @@ -17,7 +17,7 @@ path = "src/timeout.rs" [dependencies] clap = { version = "2.33", features = ["wrap_help"] } libc = "0.2.42" -nix = "0.20.0" +nix = "=0.23.1" uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["process", "signals"] } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/wc/Cargo.toml b/src/uu/wc/Cargo.toml index aca35d7d6..ec649820b 100644 --- a/src/uu/wc/Cargo.toml +++ b/src/uu/wc/Cargo.toml @@ -23,7 +23,7 @@ utf-8 = "0.7.6" unicode-width = "0.1.8" [target.'cfg(unix)'.dependencies] -nix = "0.20" +nix = "=0.23.1" libc = "0.2" [[bin]] diff --git a/src/uu/yes/Cargo.toml b/src/uu/yes/Cargo.toml index ad8a87b46..3bf0ad7cb 100644 --- a/src/uu/yes/Cargo.toml +++ b/src/uu/yes/Cargo.toml @@ -20,7 +20,7 @@ uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=[ uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] -nix = "0.20.0" +nix = "=0.23.1" [[bin]] name = "yes" diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 153e3cd57..a97f82133 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -34,7 +34,7 @@ os_display = "0.1.0" [target.'cfg(unix)'.dependencies] walkdir = { version="2.3.2", optional=true } -nix = { version="0.20", optional=true } +nix = { version="=0.23.1", optional=true } [dev-dependencies] clap = "2.33.3" From 7834d9ffafd18b654d7da07cae29290e5b393250 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sun, 14 Nov 2021 16:40:11 -0600 Subject: [PATCH 126/131] update Cargo.lock --- Cargo.lock | 58 ++++++++++++++---------------------------------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1796dafd7..ec962ed3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - [[package]] name = "Inflector" version = "0.11.4" @@ -213,9 +211,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" [[package]] name = "cexpr" @@ -320,7 +318,7 @@ dependencies = [ "glob", "lazy_static", "libc", - "nix 0.20.0", + "nix 0.23.1", "pretty_assertions", "rand 0.7.3", "regex", @@ -625,7 +623,7 @@ version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf" dependencies = [ - "nix 0.23.0", + "nix 0.23.1", "winapi 0.3.9", ] @@ -1007,9 +1005,9 @@ checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" [[package]] name = "libloading" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0" +checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52" dependencies = [ "cfg-if 1.0.0", "winapi 0.3.9", @@ -1109,30 +1107,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "nix" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2" -dependencies = [ - "bitflags", - "cc", - "cfg-if 1.0.0", - "libc", -] - -[[package]] -name = "nix" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" -dependencies = [ - "bitflags", - "cc", - "cfg-if 1.0.0", - "libc", -] - [[package]] name = "nix" version = "0.21.0" @@ -1148,9 +1122,7 @@ dependencies = [ [[package]] name = "nix" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188" +version = "0.23.1" dependencies = [ "bitflags", "cc", @@ -2216,7 +2188,7 @@ version = "0.0.8" dependencies = [ "atty", "clap", - "nix 0.20.0", + "nix 0.23.1", "thiserror", "unix_socket", "uucore", @@ -2691,7 +2663,7 @@ dependencies = [ "atty", "clap", "crossterm", - "nix 0.19.1", + "nix 0.23.1", "redox_syscall", "redox_termios", "unicode-segmentation", @@ -2716,7 +2688,7 @@ version = "0.0.8" dependencies = [ "clap", "libc", - "nix 0.20.0", + "nix 0.23.1", "uucore", "uucore_procs", ] @@ -3065,7 +3037,7 @@ version = "0.0.8" dependencies = [ "clap", "libc", - "nix 0.20.0", + "nix 0.23.1", "redox_syscall", "uucore", "uucore_procs", @@ -3100,7 +3072,7 @@ version = "0.0.8" dependencies = [ "clap", "libc", - "nix 0.20.0", + "nix 0.23.1", "uucore", "uucore_procs", ] @@ -3231,7 +3203,7 @@ dependencies = [ "bytecount", "clap", "libc", - "nix 0.20.0", + "nix 0.23.1", "unicode-width", "utf-8", "uucore", @@ -3263,7 +3235,7 @@ name = "uu_yes" version = "0.0.8" dependencies = [ "clap", - "nix 0.20.0", + "nix 0.23.1", "uucore", "uucore_procs", ] @@ -3280,7 +3252,7 @@ dependencies = [ "getopts", "lazy_static", "libc", - "nix 0.20.0", + "nix 0.23.1", "once_cell", "os_display", "termion", From 7784a252f27e19bb12cb34feb4394f96b7d10c1a Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sun, 14 Nov 2021 13:18:45 -0600 Subject: [PATCH 127/131] fix/yes ~ revise to match 'nix' changes --- src/uu/yes/src/splice.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/uu/yes/src/splice.rs b/src/uu/yes/src/splice.rs index 6f025d6a9..84bd1cc24 100644 --- a/src/uu/yes/src/splice.rs +++ b/src/uu/yes/src/splice.rs @@ -55,16 +55,13 @@ type Result = std::result::Result; impl From for Error { fn from(error: nix::Error) -> Self { - match error { - nix::Error::Sys(errno) => Error::Io(io::Error::from_raw_os_error(errno as i32)), - _ => Error::Io(io::Error::last_os_error()), - } + Error::Io(io::Error::from_raw_os_error(error as i32)) } } fn maybe_unsupported(error: nix::Error) -> Error { - match error.as_errno() { - Some(Errno::EINVAL) | Some(Errno::ENOSYS) | Some(Errno::EBADF) => Error::Unsupported, + match error { + Errno::EINVAL | Errno::ENOSYS | Errno::EBADF => Error::Unsupported, _ => error.into(), } } From ae05bffbabf84dc319bbce4d0cb6579ce1afe72f Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Sun, 14 Nov 2021 17:21:49 -0600 Subject: [PATCH 128/131] docs/spell ~ add 'vendor' directory exception for spell-checker --- .vscode/cSpell.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/cSpell.json b/.vscode/cSpell.json index 498360139..95b6f0485 100644 --- a/.vscode/cSpell.json +++ b/.vscode/cSpell.json @@ -11,7 +11,7 @@ { "name": "workspace", "path": "./cspell.dictionaries/workspace.wordlist.txt" } ], // ignorePaths - a list of globs to specify which files are to be ignored - "ignorePaths": ["Cargo.lock", "target/**", "tests/**/fixtures/**", "src/uu/dd/test-resources/**"], + "ignorePaths": ["Cargo.lock", "target/**", "tests/**/fixtures/**", "src/uu/dd/test-resources/**", "vendor/**"], // ignoreWords - a list of words to be ignored (even if they are in the flagWords) "ignoreWords": [], // words - list of words to be always considered correct From 790884b1773aa9084231eebe16b683babe9a69af Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Wed, 10 Nov 2021 13:37:00 -0600 Subject: [PATCH 129/131] maint/CICD ~ add dependencies between CI job steps (using 'needs') - the build and test steps won't run until/unless Dependency and MSRV checks pass - code coverage won't run until/unless the build steps all pass ## [why] This helps make more efficient use of CI resources and can help more easily visualize build issues from the resultant GHA dashboard flow diagram. --- .github/workflows/CICD.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 3c9be1319..e4d74a690 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -343,6 +343,7 @@ jobs: build_makefile: name: Build/Makefile + needs: [ min_version, deps ] runs-on: ${{ matrix.job.os }} strategy: fail-fast: false @@ -373,6 +374,7 @@ jobs: build: name: Build + needs: [ min_version, deps ] runs-on: ${{ matrix.job.os }} strategy: fail-fast: false @@ -635,6 +637,7 @@ jobs: test_busybox: name: Tests/BusyBox test suite + needs: [ min_version, deps ] runs-on: ${{ matrix.job.os }} strategy: fail-fast: false @@ -667,6 +670,7 @@ jobs: test_freebsd: name: Tests/FreeBSD test suite + needs: [ min_version, deps ] runs-on: ${{ matrix.job.os }} strategy: fail-fast: false @@ -733,6 +737,7 @@ jobs: coverage: name: Code Coverage + needs: build runs-on: ${{ matrix.job.os }} strategy: fail-fast: true From 7e06ddaa92c65ecfbdc04e7349da74f776c35f17 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Fri, 19 Nov 2021 21:39:35 -0600 Subject: [PATCH 130/131] fix/tee ~ repair 'unstable_name_collisions' compiler warning --- src/uu/tee/src/tee.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/tee/src/tee.rs b/src/uu/tee/src/tee.rs index d2fb015bf..e977699ea 100644 --- a/src/uu/tee/src/tee.rs +++ b/src/uu/tee/src/tee.rs @@ -164,7 +164,7 @@ impl MultiWriter { impl Write for MultiWriter { fn write(&mut self, buf: &[u8]) -> Result { - self.writers.retain_mut(|writer| { + RetainMut::retain_mut(&mut self.writers, |writer| { let result = writer.write_all(buf); match result { Err(f) => { @@ -178,7 +178,7 @@ impl Write for MultiWriter { } fn flush(&mut self) -> Result<()> { - self.writers.retain_mut(|writer| { + RetainMut::retain_mut(&mut self.writers, |writer| { let result = writer.flush(); match result { Err(f) => { From 81a1fde9f43c83722f16da6798103fbaa7d842fa Mon Sep 17 00:00:00 2001 From: Smicry Date: Sun, 21 Nov 2021 12:37:56 +0800 Subject: [PATCH 131/131] tail use UResult --- src/uu/tail/src/tail.rs | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/uu/tail/src/tail.rs b/src/uu/tail/src/tail.rs index d83f02724..655abcecf 100644 --- a/src/uu/tail/src/tail.rs +++ b/src/uu/tail/src/tail.rs @@ -191,7 +191,7 @@ fn uu_tail(settings: &Settings) -> UResult<()> { if use_stdin { let mut reader = BufReader::new(stdin()); - unbounded_tail(&mut reader, settings); + unbounded_tail(&mut reader, settings)?; // Don't follow stdin since there are no checks for pipes/FIFOs // @@ -230,7 +230,7 @@ fn uu_tail(settings: &Settings) -> UResult<()> { } } else { let mut reader = BufReader::new(file); - unbounded_tail(&mut reader, settings); + unbounded_tail(&mut reader, settings)?; if settings.follow { readers.push((Box::new(reader), filename)); } @@ -239,7 +239,7 @@ fn uu_tail(settings: &Settings) -> UResult<()> { } if settings.follow { - follow(&mut readers[..], settings); + follow(&mut readers[..], settings)?; } Ok(()) @@ -342,10 +342,9 @@ pub fn uu_app() -> App<'static, 'static> { ) } -fn follow(readers: &mut [(T, &String)], settings: &Settings) { - assert!(settings.follow); - if readers.is_empty() { - return; +fn follow(readers: &mut [(T, &String)], settings: &Settings) -> UResult<()> { + if readers.is_empty() || !settings.follow { + return Ok(()); } let mut last = readers.len() - 1; @@ -372,7 +371,7 @@ fn follow(readers: &mut [(T, &String)], settings: &Settings) { } print!("{}", datum); } - Err(err) => panic!("{}", err), + Err(err) => return Err(USimpleError::new(1, err.to_string())), } } } @@ -381,6 +380,7 @@ fn follow(readers: &mut [(T, &String)], settings: &Settings) { break; } } + Ok(()) } /// Iterate over bytes in the file, in reverse, until we find the @@ -475,7 +475,7 @@ where } } -fn unbounded_tail(reader: &mut BufReader, settings: &Settings) { +fn unbounded_tail(reader: &mut BufReader, settings: &Settings) -> UResult<()> { // Read through each line/char and store them in a ringbuffer that always // contains count lines/chars. When reaching the end of file, output the // data in the ringbuf. @@ -487,11 +487,13 @@ fn unbounded_tail(reader: &mut BufReader, settings: &Settings) { } FilterMode::Bytes(count) => { for byte in unbounded_tail_collect(reader.bytes(), count, settings.beginning) { - let mut stdout = stdout(); - print_byte(&mut stdout, byte); + if let Err(err) = stdout().write(&[byte]) { + return Err(USimpleError::new(1, err.to_string())); + } } } } + Ok(()) } fn is_seekable(file: &mut T) -> bool { @@ -500,13 +502,6 @@ fn is_seekable(file: &mut T) -> bool { && file.seek(SeekFrom::Start(0)).is_ok() } -#[inline] -fn print_byte(stdout: &mut T, ch: u8) { - if let Err(err) = stdout.write(&[ch]) { - crash!(1, "{}", err); - } -} - fn parse_num(src: &str) -> Result<(usize, bool), ParseSizeError> { let mut size_string = src.trim(); let mut starting_with = false;