From 2c42623b6e3bf4e53d3aae3034d75351d63b6712 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Thu, 28 Dec 2023 10:59:33 +0100 Subject: [PATCH 01/17] cp: backup dest symlink linking to source --- src/uu/cp/src/cp.rs | 1 + tests/by-util/test_cp.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 30cb3f59ffe..a5fb66d2926 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1521,6 +1521,7 @@ fn is_forbidden_copy_to_same_file( options.dereference(source_in_command_line) || !source.is_symlink(); paths_refer_to_same_file(source, dest, dereference_to_compare) && !(options.force() && options.backup != BackupMode::NoBackup) + && !(dest.is_symlink() && options.backup != BackupMode::NoBackup) } /// Back up, remove, or leave intact the destination file, depending on the options. diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index a1638584b8c..63cfa2155fa 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -701,6 +701,25 @@ fn test_cp_arg_backup_with_dest_a_symlink() { assert_eq!(original, at.resolve_link(backup)); } +#[test] +fn test_cp_arg_backup_with_dest_a_symlink_to_source() { + let (at, mut ucmd) = at_and_ucmd!(); + let source = "source"; + let source_content = "content"; + let symlink = "symlink"; + let backup = "symlink~"; + + at.write(source, source_content); + at.symlink_file(source, symlink); + + ucmd.arg("-b").arg(source).arg(symlink).succeeds(); + + assert!(!at.symlink_exists(symlink)); + assert_eq!(source_content, at.read(symlink)); + assert!(at.symlink_exists(backup)); + assert_eq!(source, at.resolve_link(backup)); +} + #[test] fn test_cp_arg_backup_with_other_args() { let (at, mut ucmd) = at_and_ucmd!(); From 902a128ea8a3cce5b520afef3c7c357728b93a97 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sun, 31 Dec 2023 14:17:16 +0100 Subject: [PATCH 02/17] cp: rename function is_forbidden_copy_to_same_file -> is_forbidden_to_copy_to_same_file --- src/uu/cp/src/cp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index a5fb66d2926..91507f31958 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1509,7 +1509,7 @@ fn backup_dest(dest: &Path, backup_path: &Path) -> CopyResult { /// /// Copying to the same file is only allowed if both `--backup` and /// `--force` are specified and the file is a regular file. -fn is_forbidden_copy_to_same_file( +fn is_forbidden_to_copy_to_same_file( source: &Path, dest: &Path, options: &Options, @@ -1533,7 +1533,7 @@ fn handle_existing_dest( ) -> CopyResult<()> { // Disallow copying a file to itself, unless `--force` and // `--backup` are both specified. - if is_forbidden_copy_to_same_file(source, dest, options, source_in_command_line) { + if is_forbidden_to_copy_to_same_file(source, dest, options, source_in_command_line) { return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into()); } From 7ddeba4b98615dea328d2d3ab8dd2be8ad02bfe4 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 1 Jan 2024 14:30:27 +0100 Subject: [PATCH 03/17] cp: show no "same file" error for --link a a --- src/uu/cp/src/cp.rs | 2 ++ tests/by-util/test_cp.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 332bb578512..d58eec13d2d 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -150,6 +150,7 @@ pub enum TargetType { } /// Copy action to perform +#[derive(PartialEq)] pub enum CopyMode { Link, SymLink, @@ -1714,6 +1715,7 @@ fn copy_file( && !options.force() && options.backup == BackupMode::NoBackup && source != dest + || (source == dest && options.copy_mode == CopyMode::Link) { return Ok(()); } diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index d166243ed6e..884e71a3c6a 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -566,6 +566,22 @@ fn test_cp_arg_link_with_dest_hardlink_to_source() { assert!(at.file_exists(hardlink)); } +#[test] +#[cfg(target_os = "linux")] +fn test_cp_arg_link_with_same_file() { + use std::os::linux::fs::MetadataExt; + + let (at, mut ucmd) = at_and_ucmd!(); + let file = "file"; + + at.touch(file); + + ucmd.args(&["--link", file, file]).succeeds(); + + assert_eq!(at.metadata(file).st_nlink(), 1); + assert!(at.file_exists(file)); +} + #[test] fn test_cp_arg_symlink() { let (at, mut ucmd) = at_and_ucmd!(); From 1c6bf6991c012e3fd391199e98a6feec27bbcba4 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 8 Jan 2024 10:49:16 +0100 Subject: [PATCH 04/17] nl: don't exit if input is directory --- src/uu/nl/src/nl.rs | 16 +++++++++++----- tests/by-util/test_nl.rs | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/uu/nl/src/nl.rs b/src/uu/nl/src/nl.rs index 7d7688650aa..c7e72f6e2e2 100644 --- a/src/uu/nl/src/nl.rs +++ b/src/uu/nl/src/nl.rs @@ -7,8 +7,8 @@ use clap::{crate_version, Arg, ArgAction, Command}; use std::fs::File; use std::io::{stdin, BufRead, BufReader, Read}; use std::path::Path; -use uucore::error::{FromIo, UResult, USimpleError}; -use uucore::{format_usage, help_about, help_section, help_usage}; +use uucore::error::{set_exit_code, FromIo, UResult, USimpleError}; +use uucore::{format_usage, help_about, help_section, help_usage, show_error}; mod helper; @@ -205,9 +205,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { nl(&mut buffer, &mut stats, &settings)?; } else { let path = Path::new(file); - let reader = File::open(path).map_err_context(|| file.to_string())?; - let mut buffer = BufReader::new(reader); - nl(&mut buffer, &mut stats, &settings)?; + + if path.is_dir() { + show_error!("{}: Is a directory", path.display()); + set_exit_code(1); + } else { + let reader = File::open(path).map_err_context(|| file.to_string())?; + let mut buffer = BufReader::new(reader); + nl(&mut buffer, &mut stats, &settings)?; + } } } diff --git a/tests/by-util/test_nl.rs b/tests/by-util/test_nl.rs index 78c8975a849..a00e37a4767 100644 --- a/tests/by-util/test_nl.rs +++ b/tests/by-util/test_nl.rs @@ -634,3 +634,20 @@ fn test_empty_section_delimiter() { .stdout_is(" 1\ta\n \n 2\tb\n"); } } + +#[test] +fn test_directory_as_input() { + let (at, mut ucmd) = at_and_ucmd!(); + let dir = "dir"; + let file = "file"; + let content = "aaa"; + + at.mkdir(dir); + at.write(file, content); + + ucmd.arg(dir) + .arg(file) + .fails() + .stderr_is(format!("nl: {dir}: Is a directory\n")) + .stdout_contains(content); +} From 49154669a620df00c267fd0c520af6001bf73f79 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 9 Jan 2024 15:17:15 +0100 Subject: [PATCH 05/17] pathchk: simplify and rename test --- tests/by-util/test_pathchk.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/by-util/test_pathchk.rs b/tests/by-util/test_pathchk.rs index d66ecb9efb4..f5d84b5762a 100644 --- a/tests/by-util/test_pathchk.rs +++ b/tests/by-util/test_pathchk.rs @@ -4,6 +4,14 @@ // file that was distributed with this source code. use crate::common::util::TestScenario; +#[test] +fn test_no_args() { + new_ucmd!() + .fails() + .no_stdout() + .stderr_contains("pathchk: missing operand"); +} + #[test] fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails().code_is(1); @@ -164,10 +172,3 @@ fn test_posix_all() { // fail on empty path new_ucmd!().args(&["-p", "-P", ""]).fails().no_stdout(); } - -#[test] -fn test_args_parsing() { - // fail on no args - let empty_args: [String; 0] = []; - new_ucmd!().args(&empty_args).fails().no_stdout(); -} From f400a07dc6e6fed71efb38a5b84d0d981e355cd9 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 9 Jan 2024 15:19:22 +0100 Subject: [PATCH 06/17] pathchk: remove useless comments --- tests/by-util/test_pathchk.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/by-util/test_pathchk.rs b/tests/by-util/test_pathchk.rs index f5d84b5762a..d09c8a2e1e4 100644 --- a/tests/by-util/test_pathchk.rs +++ b/tests/by-util/test_pathchk.rs @@ -19,8 +19,6 @@ fn test_invalid_arg() { #[test] fn test_default_mode() { - // test the default mode - // accept some reasonable default new_ucmd!().args(&["dir/file"]).succeeds().no_stdout(); @@ -56,8 +54,6 @@ fn test_default_mode() { #[test] fn test_posix_mode() { - // test the posix mode - // accept some reasonable default new_ucmd!().args(&["-p", "dir/file"]).succeeds().no_stdout(); @@ -82,8 +78,6 @@ fn test_posix_mode() { #[test] fn test_posix_special() { - // test the posix special mode - // accept some reasonable default new_ucmd!().args(&["-P", "dir/file"]).succeeds().no_stdout(); @@ -123,8 +117,6 @@ fn test_posix_special() { #[test] fn test_posix_all() { - // test the posix special mode - // accept some reasonable default new_ucmd!() .args(&["-p", "-P", "dir/file"]) From e0abb76c0f5be632b60460ccdce69547baa06c60 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 16 Dec 2023 14:56:13 +0100 Subject: [PATCH 07/17] Bump redox_syscall to 0.4.1 redox_syscall from 0.3.5 & 0.4.0 -> 0.4.1 filetime from 0.2.22 -> 0.2.23 parking_lot_core from 0.9.8 -> 0.9.9 --- Cargo.lock | 31 +++++++++++-------------------- deny.toml | 2 -- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e4a271dfe0..936d2c1f245 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -813,14 +813,14 @@ checksum = "31a7a908b8f32538a2143e59a6e4e2508988832d5d4d6f7c156b3cbc762643a5" [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall", + "windows-sys 0.52.0", ] [[package]] @@ -1455,13 +1455,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall", "smallvec", "windows-targets 0.48.0", ] @@ -1689,18 +1689,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded0bce2d41cc3c57aefa284708ced249a64acb01745dbbe72bd78610bfd644c" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] @@ -2051,7 +2042,7 @@ checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.4.0", + "redox_syscall", "rustix 0.38.28", "windows-sys 0.48.0", ] @@ -3014,7 +3005,7 @@ version = "0.0.23" dependencies = [ "clap", "libc", - "redox_syscall 0.4.0", + "redox_syscall", "uucore", ] diff --git a/deny.toml b/deny.toml index 986c2b26970..d7c04ad2d5b 100644 --- a/deny.toml +++ b/deny.toml @@ -102,8 +102,6 @@ skip = [ { name = "syn", version = "1.0.109" }, # various crates { name = "bitflags", version = "1.3.2" }, - # various crates - { name = "redox_syscall", version = "0.3.5" }, # clap_builder, textwrap { name = "terminal_size", version = "0.2.6" }, ] From aeee56b3c3bd7ed0f93d875d322b0f9eba4ae95d Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sun, 14 Jan 2024 15:23:24 +0100 Subject: [PATCH 08/17] head: fix clippy warnings in tests --- tests/by-util/test_head.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/by-util/test_head.rs b/tests/by-util/test_head.rs index cc17cdf950a..0b0e98aa122 100644 --- a/tests/by-util/test_head.rs +++ b/tests/by-util/test_head.rs @@ -391,7 +391,7 @@ fn test_read_backwards_bytes_proc_fs_version() { let args = ["-c", "-1", "/proc/version"]; let result = ts.ucmd().args(&args).succeeds(); - assert!(result.stdout().len() > 0); + assert!(!result.stdout().is_empty()); } #[cfg(all( @@ -406,7 +406,7 @@ fn test_read_backwards_bytes_proc_fs_modules() { let args = ["-c", "-1", "/proc/modules"]; let result = ts.ucmd().args(&args).succeeds(); - assert!(result.stdout().len() > 0); + assert!(!result.stdout().is_empty()); } #[cfg(all( @@ -421,7 +421,7 @@ fn test_read_backwards_lines_proc_fs_modules() { let args = ["--lines", "-1", "/proc/modules"]; let result = ts.ucmd().args(&args).succeeds(); - assert!(result.stdout().len() > 0); + assert!(!result.stdout().is_empty()); } #[cfg(all( From e01d5f75f786b5f6953f5d714de92a588e0d5bc1 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 14 Jan 2024 15:57:22 +0100 Subject: [PATCH 09/17] ls: if acl are used, show the + in the perms (#5816) * ls: if acl are used, show the + in the perms Tested by tests/mkdir/p-acl.sh * CICD.yml: fix small formatting issue --------- Co-authored-by: Daniel Hofstetter --- .github/workflows/CICD.yml | 20 ++++++++++++++++---- Cargo.lock | 1 + src/uu/ls/Cargo.toml | 3 +++ src/uu/ls/src/ls.rs | 28 ++++++++++++++++++++++++++-- tests/by-util/test_ls.rs | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 89022bbef35..20945609230 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -612,11 +612,23 @@ jobs: 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-*) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; - *-redox*) sudo apt-get -y update ; sudo apt-get -y install fuse3 libfuse-dev ;; + arm-unknown-linux-gnueabihf) + sudo apt-get -y update + sudo apt-get -y install gcc-arm-linux-gnueabihf + ;; + aarch64-unknown-linux-*) + sudo apt-get -y update + sudo apt-get -y install gcc-aarch64-linux-gnu + ;; + *-redox*) + sudo apt-get -y update + sudo apt-get -y install fuse3 libfuse-dev + ;; # Update binutils if MinGW due to https://github.com/rust-lang/rust/issues/112368 - x86_64-pc-windows-gnu) C:/msys64/usr/bin/pacman.exe -Syu --needed mingw-w64-x86_64-gcc --noconfirm ; echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH ;; + x86_64-pc-windows-gnu) + C:/msys64/usr/bin/pacman.exe -Syu --needed mingw-w64-x86_64-gcc --noconfirm + echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH + ;; esac case '${{ matrix.job.os }}' in macos-latest) brew install coreutils ;; # needed for testing diff --git a/Cargo.lock b/Cargo.lock index b6a25913477..56e448fa794 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2616,6 +2616,7 @@ dependencies = [ "unicode-width", "uucore", "uutils_term_grid", + "xattr", ] [[package]] diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index d11eeb27ce3..38312eefc5e 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -34,6 +34,9 @@ once_cell = { workspace = true } selinux = { workspace = true, optional = true } hostname = { workspace = true } +[target.'cfg(unix)'.dependencies] +xattr = { workspace = true } + [[bin]] name = "ls" path = "src/main.rs" diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 0e9b2572289..1c89cd3536d 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm +// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm getxattr use clap::{ builder::{NonEmptyStringValueParser, ValueParser}, @@ -36,6 +36,7 @@ use std::{ }; use term_grid::{Cell, Direction, Filling, Grid, GridOptions}; use unicode_width::UnicodeWidthStr; + #[cfg(any( target_os = "linux", target_os = "macos", @@ -2620,6 +2621,18 @@ fn display_grid( Ok(()) } +#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] +fn file_has_acl>(file: P) -> bool { + // don't use exacl here, it is doing more getxattr call then needed + match xattr::list(file) { + Ok(acl) => { + // if we have extra attributes, we have an acl + acl.count() > 0 + } + Err(_) => false, + } +} + /// This writes to the BufWriter out a single string of the output of `ls -l`. /// /// It writes the following keys, in order: @@ -2663,9 +2676,14 @@ fn display_item_long( output_display += " "; } if let Some(md) = item.get_metadata(out) { + #[cfg(any(not(unix), target_os = "android", target_os = "macos"))] + // TODO: See how Mac should work here + let is_acl_set = false; + #[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] + let is_acl_set = file_has_acl(item.display_name.as_os_str()); write!( output_display, - "{}{} {}", + "{}{}{} {}", display_permissions(md, true), if item.security_context.len() > 1 { // GNU `ls` uses a "." character to indicate a file with a security context, @@ -2674,6 +2692,12 @@ fn display_item_long( } else { "" }, + if is_acl_set { + // if acl has been set, we display a "+" at the end of the file permissions + "+" + } else { + "" + }, pad_left(&display_symlink_count(md), padding.link_count) ) .unwrap(); diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 0162b017078..1262c2ab9ec 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -4293,3 +4293,39 @@ fn test_term_colorterm() { "exe" ); } + +#[cfg(all(unix, not(target_os = "macos")))] +#[test] +fn test_acl_display() { + use std::process::Command; + + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + let path = "a42"; + at.mkdir(path); + + let path = at.plus_as_string(path); + // calling the command directly. xattr requires some dev packages to be installed + // and it adds a complex dependency just for a test + match Command::new("setfacl") + .args(["-d", "-m", "group::rwx", &path]) + .status() + .map(|status| status.code()) + { + Ok(Some(0)) => {} + Ok(_) => { + println!("test skipped: setfacl failed"); + return; + } + Err(e) => { + println!("test skipped: setfacl failed with {}", e); + return; + } + } + + scene + .ucmd() + .args(&["-lda", &path]) + .succeeds() + .stdout_contains("+"); +} From 4f33a375cda8c515db6eb81cd54007fe850dd2cb Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 5 Jan 2024 20:55:54 +0100 Subject: [PATCH 10/17] factor: handle the '< .' arg --- src/uu/factor/src/cli.rs | 15 ++++++++++++--- tests/by-util/test_factor.rs | 5 +++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/uu/factor/src/cli.rs b/src/uu/factor/src/cli.rs index 63a0632a3b7..d01ca625c7b 100644 --- a/src/uu/factor/src/cli.rs +++ b/src/uu/factor/src/cli.rs @@ -73,9 +73,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let stdin = stdin(); let lines = stdin.lock().lines(); for line in lines { - for number in line.unwrap().split_whitespace() { - print_factors_str(number, &mut w, print_exponents) - .map_err_context(|| "write error".into())?; + match line { + Ok(line) => { + for number in line.split_whitespace() { + print_factors_str(number, &mut w, print_exponents) + .map_err_context(|| "write error".into())?; + } + } + Err(e) => { + set_exit_code(1); + show_error!("error reading input: {}", e); + return Ok(()); + } } } } diff --git a/tests/by-util/test_factor.rs b/tests/by-util/test_factor.rs index 3326a1ace71..57a2dae0998 100644 --- a/tests/by-util/test_factor.rs +++ b/tests/by-util/test_factor.rs @@ -1258,3 +1258,8 @@ const PRIMES50: &[u64] = &[ 1125899906841623, 1125899906841613, ]; + +#[test] +fn fails_on_directory() { + new_ucmd!().pipe_in(".").fails(); +} From 8d24036f5cf769f6a10672e6060c615599269f2a Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 5 Jan 2024 21:15:26 +0100 Subject: [PATCH 11/17] basenc: handle '--base32 .' arg --- src/uu/base32/src/base_common.rs | 3 ++- src/uucore/src/lib/features/encoding.rs | 7 +++++-- tests/by-util/test_basenc.rs | 13 +++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index 2112a40ead7..68c40287db7 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -6,7 +6,7 @@ use std::io::{stdout, Read, Write}; use uucore::display::Quotable; -use uucore::encoding::{wrap_print, Data, Format}; +use uucore::encoding::{wrap_print, Data, EncodeError, Format}; use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; use uucore::format_usage; @@ -174,6 +174,7 @@ pub fn handle_input( wrap_print(&data, &s); Ok(()) } + Err(EncodeError::InvalidInput) => Err(USimpleError::new(1, "error: invalid input")), Err(_) => Err(USimpleError::new( 1, "error: invalid input (length must be multiple of 4 characters)", diff --git a/src/uucore/src/lib/features/encoding.rs b/src/uucore/src/lib/features/encoding.rs index 14fdbb38ee7..db218d5f061 100644 --- a/src/uucore/src/lib/features/encoding.rs +++ b/src/uucore/src/lib/features/encoding.rs @@ -27,6 +27,7 @@ pub enum DecodeError { pub enum EncodeError { Z85InputLenNotMultipleOf4, + InvalidInput, } pub type DecodeResult = Result, DecodeError>; @@ -148,8 +149,10 @@ impl Data { pub fn encode(&mut self) -> Result { let mut buf: Vec = vec![]; - self.input.read_to_end(&mut buf).unwrap(); - encode(self.format, buf.as_slice()) + match self.input.read_to_end(&mut buf) { + Ok(_) => encode(self.format, buf.as_slice()), + Err(_) => Err(EncodeError::InvalidInput), + } } } diff --git a/tests/by-util/test_basenc.rs b/tests/by-util/test_basenc.rs index 6c71b63f79c..13a8967031b 100644 --- a/tests/by-util/test_basenc.rs +++ b/tests/by-util/test_basenc.rs @@ -18,3 +18,16 @@ fn test_z85_not_padded() { .fails() .stderr_only("basenc: error: invalid input (length must be multiple of 4 characters)\n"); } + +#[test] +fn test_invalid_input() { + let error_message = if cfg!(windows) { + "basenc: .: Permission denied" + } else { + "basenc: error: invalid input\n" + }; + new_ucmd!() + .args(&["--base32", "."]) + .fails() + .stderr_only(error_message); +} From 3d356d47b3f6ada89b4946983d904f578f5263e2 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 5 Jan 2024 21:44:58 +0100 Subject: [PATCH 12/17] expand: avoid an infinite loop --- src/uu/expand/src/expand.rs | 11 +++++++++-- tests/by-util/test_expand.rs | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/uu/expand/src/expand.rs b/src/uu/expand/src/expand.rs index eb9766eb4f6..1efb36c654c 100644 --- a/src/uu/expand/src/expand.rs +++ b/src/uu/expand/src/expand.rs @@ -11,11 +11,12 @@ use std::fmt; use std::fs::File; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write}; use std::num::IntErrorKind; +use std::path::Path; use std::str::from_utf8; use unicode_width::UnicodeWidthChar; use uucore::display::Quotable; -use uucore::error::{FromIo, UError, UResult}; -use uucore::{format_usage, help_about, help_usage}; +use uucore::error::{set_exit_code, FromIo, UError, UResult}; +use uucore::{format_usage, help_about, help_usage, show_error}; const ABOUT: &str = help_about!("expand.md"); const USAGE: &str = help_usage!("expand.md"); @@ -465,6 +466,12 @@ fn expand(options: &Options) -> UResult<()> { let mut buf = Vec::new(); for file in &options.files { + if Path::new(file).is_dir() { + show_error!("{}: Is a directory", file); + set_exit_code(1); + continue; + } + let mut fh = open(file)?; while match fh.read_until(b'\n', &mut buf) { diff --git a/tests/by-util/test_expand.rs b/tests/by-util/test_expand.rs index 1e26b32732c..c420f5ad5b9 100644 --- a/tests/by-util/test_expand.rs +++ b/tests/by-util/test_expand.rs @@ -409,3 +409,11 @@ int main() { ", ); } + +#[test] +fn test_expand_directory() { + new_ucmd!() + .args(&["."]) + .fails() + .stderr_contains("expand: .: Is a directory"); +} From 7914f22cfe13167d14f38b01d4a3b8aa6f5b309e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 03:14:26 +0000 Subject: [PATCH 13/17] chore(deps): update rust crate xattr to 1.3.1 --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0d6ffbf5da..55ec59e2d0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1589,7 +1589,7 @@ dependencies = [ "hex", "lazy_static", "procfs-core", - "rustix 0.38.28", + "rustix 0.38.30", ] [[package]] @@ -1822,9 +1822,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags 2.4.0", "errno", @@ -2043,7 +2043,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall", - "rustix 0.38.28", + "rustix 0.38.30", "windows-sys 0.52.0", ] @@ -2063,7 +2063,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.38.28", + "rustix 0.38.30", "windows-sys 0.48.0", ] @@ -3566,13 +3566,13 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "xattr" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", "linux-raw-sys 0.4.12", - "rustix 0.38.28", + "rustix 0.38.30", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c6912251560..17efcb6a747 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -331,7 +331,7 @@ utf-8 = "0.7.6" walkdir = "2.4" winapi-util = "0.1.6" windows-sys = { version = "0.48.0", default-features = false } -xattr = "1.2.0" +xattr = "1.3.1" zip = { version = "0.6.6", default-features = false, features = ["deflate"] } hex = "0.4.3" From 076b905513b1ed27cae640ba6cb5d0db42ef5935 Mon Sep 17 00:00:00 2001 From: Biplab Mochan Gartia <45629823+biplab5464@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:07:56 +0530 Subject: [PATCH 14/17] cksum: stops when one of given files doesn't exist #5809 (#5820) * cksum: stops when one of given files doesn't exist #5809 * removed printld cksum: stops when one of given files doesn't exist #5809 * formatting the code cksum: stops when one of given files doesn't exist #5809 * formatting the code cksum: stops when one of given files doesn't exist #5809 * set exist status cksum: stops when one of given files doesn't exist #5809 * code quality formatting issue cksum: stops when one of given files doesn't exist #5809 * tertsdiepraam idea cksum: stops when one of given files doesn't exist #5809 * one new test case and deleted duplicate show cksum: stops when one of given files doesn't exist #5809 * formatting the test cases cksum: stops when one of given files doesn't exist #5809 * Revert "formatting the test cases cksum: stops when one of given files doesn't exist #5809" This reverts commit 8c382f1e8fa4e9ba3fc0f4fc05ee2fb58ef9dbfd. * reverting the changes and committing again due to a mistake cksum: stops when one of given files doesn't exist #5809 --------- Co-authored-by: biplab5464 --- src/uu/cksum/src/cksum.rs | 9 +++++++-- tests/by-util/test_cksum.rs | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 458177e8a7c..36dfbbe1e3d 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -168,8 +168,13 @@ where } else if filename.is_dir() { Box::new(BufReader::new(io::empty())) as Box } else { - file_buf = - File::open(filename).map_err_context(|| filename.to_str().unwrap().to_string())?; + file_buf = match File::open(filename) { + Ok(file) => file, + Err(err) => { + show!(err.map_err_context(|| filename.to_string_lossy().to_string())); + continue; + } + }; Box::new(file_buf) as Box }); let (sum, sz) = digest_read(&mut options.digest, &mut file, options.output_bits) diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index 80cfc749931..464de947479 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -80,6 +80,22 @@ fn test_nonexisting_file() { .stderr_contains(format!("cksum: {file_name}: No such file or directory")); } +#[test] +fn test_one_nonexisting_file() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.touch("abc.txt"); + at.touch("xyz.txt"); + + ucmd.arg("abc.txt") + .arg("asdf.txt") + .arg("xyz.txt") + .fails() + .stdout_contains_line("4294967295 0 xyz.txt") + .stderr_contains("asdf.txt: No such file or directory") + .stdout_contains_line("4294967295 0 abc.txt"); +} + // Make sure crc is correct for files larger than 32 bytes // but <128 bytes (1 fold pclmul) // spell-checker:disable-line #[test] From e91540fc0792ab563fe2a35a52299024902b88cf Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 15 Jan 2024 10:27:59 +0100 Subject: [PATCH 15/17] uucore: fix clippy warning from if_not_else lint --- src/uucore/src/lib/features/format/num_format.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uucore/src/lib/features/format/num_format.rs b/src/uucore/src/lib/features/format/num_format.rs index 325da3ce6e3..ea5d6a75316 100644 --- a/src/uucore/src/lib/features/format/num_format.rs +++ b/src/uucore/src/lib/features/format/num_format.rs @@ -146,10 +146,10 @@ impl Formatter for UnsignedInt { // We also need to take into account that 0 should not be 00 // Since this is an unsigned int, we do not need to take the minus // sign into account. - if x != 0 { - format!("0{x:o}") - } else { + if x == 0 { format!("{x:o}") + } else { + format!("0{x:o}") } } UnsignedIntVariant::Hexadecimal(Case::Lowercase, Prefix::No) => { From b116a97fdcd8533f296f0c2e0f1a94e3b287d0e5 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 15 Jan 2024 10:43:20 +0100 Subject: [PATCH 16/17] add missing \n --- tests/by-util/test_basenc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_basenc.rs b/tests/by-util/test_basenc.rs index 13a8967031b..c9e15ef1f61 100644 --- a/tests/by-util/test_basenc.rs +++ b/tests/by-util/test_basenc.rs @@ -22,7 +22,7 @@ fn test_z85_not_padded() { #[test] fn test_invalid_input() { let error_message = if cfg!(windows) { - "basenc: .: Permission denied" + "basenc: .: Permission denied\n" } else { "basenc: error: invalid input\n" }; From fff83995fb2c10fe1b477f4e3879fa0dbd5e247d Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 15 Jan 2024 10:59:04 +0100 Subject: [PATCH 17/17] cp: --preserve should keep xattr (#5834) * cp: --preserve should keep xattr Should help with tests/cp/acl.sh * Update tests/by-util/test_cp.rs Co-authored-by: Daniel Hofstetter * Update tests/by-util/test_cp.rs Co-authored-by: Daniel Hofstetter * Update tests/by-util/test_cp.rs Co-authored-by: Daniel Hofstetter --------- Co-authored-by: Daniel Hofstetter --- Cargo.lock | 1 + Cargo.toml | 1 + src/uu/cp/src/cp.rs | 5 ++-- tests/by-util/test_cp.rs | 58 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55ec59e2d0a..978c7fc3fae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -507,6 +507,7 @@ dependencies = [ "uucore", "uuhelp_parser", "walkdir", + "xattr", "zip", ] diff --git a/Cargo.toml b/Cargo.toml index 17efcb6a747..a8e9c399b5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -500,6 +500,7 @@ rlimit = "0.10.1" [target.'cfg(unix)'.dev-dependencies] nix = { workspace = true, features = ["process", "signal", "user"] } rand_pcg = "0.3" +xattr = { workspace = true } [build-dependencies] phf_codegen = { workspace = true } diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 52d59ff6198..28e9b678471 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -826,6 +826,7 @@ impl Attributes { ownership: Preserve::Yes { required: true }, mode: Preserve::Yes { required: true }, timestamps: Preserve::Yes { required: true }, + xattr: Preserve::Yes { required: true }, ..Self::NONE }; @@ -1441,7 +1442,7 @@ pub(crate) fn copy_attributes( })?; handle_preserve(&attributes.xattr, || -> CopyResult<()> { - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "android")))] { let xattrs = xattr::list(source)?; for attr in xattrs { @@ -1450,7 +1451,7 @@ pub(crate) fn copy_attributes( } } } - #[cfg(not(unix))] + #[cfg(not(all(unix, not(target_os = "android"))))] { // The documentation for GNU cp states: // diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 5d78c5996c0..1271909ecb9 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs outfile uufs +// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs outfile uufs xattrs use crate::common::util::TestScenario; #[cfg(not(windows))] @@ -56,6 +56,8 @@ static TEST_MOUNT_MOUNTPOINT: &str = "mount"; static TEST_MOUNT_OTHER_FILESYSTEM_FILE: &str = "mount/DO_NOT_copy_me.txt"; #[cfg(unix)] static TEST_NONEXISTENT_FILE: &str = "nonexistent_file.txt"; +#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] +use xattr; /// Assert that mode, ownership, and permissions of two metadata objects match. #[cfg(all(not(windows), not(target_os = "freebsd")))] @@ -3736,3 +3738,57 @@ fn test_cp_no_such() { .fails() .stderr_is("cp: 'no-such/' is not a directory\n"); } + +#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] +fn compare_xattrs>(path1: P, path2: P) -> bool { + let get_sorted_xattrs = |path: P| { + xattr::list(path) + .map(|attrs| { + let mut attrs = attrs.collect::>(); + attrs.sort(); + attrs + }) + .unwrap_or_else(|_| Vec::new()) + }; + + get_sorted_xattrs(path1) == get_sorted_xattrs(path2) +} + +#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))] +#[test] +fn test_acl_preserve() { + use std::process::Command; + + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + let path1 = "a"; + let path2 = "b"; + let file = "a/file"; + let file_target = "b/file"; + at.mkdir(path1); + at.mkdir(path2); + at.touch(file); + + let path = at.plus_as_string(file); + // calling the command directly. xattr requires some dev packages to be installed + // and it adds a complex dependency just for a test + match Command::new("setfacl") + .args(["-m", "group::rwx", &path1]) + .status() + .map(|status| status.code()) + { + Ok(Some(0)) => {} + Ok(_) => { + println!("test skipped: setfacl failed"); + return; + } + Err(e) => { + println!("test skipped: setfacl failed with {}", e); + return; + } + } + + scene.ucmd().args(&["-p", &path, path2]).succeeds(); + + assert!(compare_xattrs(&file, &file_target)); +}