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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/uu/test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ doctest = false
clap = { workspace = true }
fluent = { workspace = true }
thiserror = { workspace = true }
uucore = { workspace = true, features = ["fs", "wide"] }
uucore = { workspace = true, features = ["fs", "i18n-collator", "wide"] }

[target.'cfg(not(any(windows, target_os = "wasi")))'.dependencies]
rustix = { workspace = true, features = ["process"] }
rustix = { workspace = true, features = ["fs", "process"] }

[target.'cfg(not(windows))'.dependencies]
libc = { workspace = true }
Expand Down
6 changes: 4 additions & 2 deletions src/uu/test/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,15 @@ test-after-help = Exit with the status determined by EXPRESSION.

# Error messages
test-error-missing-closing-bracket = missing '{"]"}'
test-error-expected = expected { $value }
test-error-expected-value = expected value
test-error-expected = { $value } expected
test-error-missing-argument = missing argument after { $argument }
test-error-extra-argument = extra argument { $argument }
test-error-unknown-operator = unknown operator { $operator }
test-error-invalid-integer = invalid integer { $value }
test-error-unary-operator-expected = { $operator }: unary operator expected
test-error-binary-operator-expected = { $operator }: binary operator expected
test-error-expected-found = { $expected } expected, found { $found }
test-error-does-not-accept-length = { $operator } does not accept -l

# Diagnostic labels, used when errors are rendered with a source snippet
test-diag-label-unary-operator-expected = this needs an expression on both sides
Expand Down
4 changes: 3 additions & 1 deletion src/uu/test/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,14 @@ test-after-help = Quitter avec le statut déterminé par EXPRESSION.
# Messages d'erreur
test-error-missing-closing-bracket = '{"]"}' manquant
test-error-expected = { $value } attendu
test-error-expected-value = valeur attendue
test-error-missing-argument = argument manquant après { $argument }
test-error-extra-argument = argument supplémentaire { $argument }
test-error-unknown-operator = opérateur inconnu { $operator }
test-error-invalid-integer = entier invalide { $value }
test-error-unary-operator-expected = { $operator } : opérateur unaire attendu
test-error-binary-operator-expected = { $operator } : opérateur binaire attendu
test-error-expected-found = { $expected } attendu, trouvé { $found }
test-error-does-not-accept-length = { $operator } n'accepte pas -l

# Étiquettes de diagnostic, utilisées quand les erreurs sont rendues avec un extrait
test-diag-label-unary-operator-expected = nécessite une expression de chaque côté
Expand Down
7 changes: 4 additions & 3 deletions src/uu/test/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ pub fn render(args: &[OsString], err: &ParseError) -> bool {
// Labelled only where a label would add to the message, per the convention
// in `uucore::diagnostics`.
let (label, help) = match &err.kind {
ParseErrorKind::Expected(_) => (None, None),
ParseErrorKind::Expected(_)
| ParseErrorKind::ExpectedFound(_, _)
| ParseErrorKind::BinaryOperatorExpected(_)
| ParseErrorKind::DoesNotAcceptLength(_) => (None, None),
ParseErrorKind::ExtraArgument(_) => (
Some(translate!("diagnostics-label-expression-complete")),
Some(translate!("test-diag-help-extra-argument")),
Expand Down Expand Up @@ -63,8 +66,6 @@ pub fn render(args: &[OsString], err: &ParseError) -> bool {
"name" => uucore::util_name()
)),
),
// Never carries a position, so it is filtered out above.
ParseErrorKind::ExpectedValue => return false,
};

snapshot.render(
Expand Down
8 changes: 6 additions & 2 deletions src/uu/test/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ use uucore::translate;
/// Represents an error encountered while parsing a test expression
#[derive(Error, Debug)]
pub enum ParseErrorKind {
#[error("{}", translate!("test-error-expected-value"))]
ExpectedValue,
#[error("{}", translate!("test-error-expected", "value" => .0))]
Expected(String),
#[error("{}", translate!("test-error-extra-argument", "argument" => .0))]
Expand All @@ -27,6 +25,12 @@ pub enum ParseErrorKind {
InvalidFileDescriptor(String),
#[error("{}", translate!("test-error-unary-operator-expected", "operator" => .0))]
UnaryOperatorExpected(String),
#[error("{}", translate!("test-error-binary-operator-expected", "operator" => .0))]
BinaryOperatorExpected(String),
#[error("{}", translate!("test-error-expected-found", "expected" => .0, "found" => .1))]
ExpectedFound(String, String),
#[error("{}", translate!("test-error-does-not-accept-length", "operator" => .0))]
DoesNotAcceptLength(String),
}

/// Where in the original argument list an error occurred.
Expand Down
86 changes: 86 additions & 0 deletions src/uu/test/src/faccessat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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.

// spell-checker:ignore (vars) egid euid accessat EACCESS OPNOTSUPP NOSYS XOTH XGRP XUSR
use rustix::fs::Access;
use rustix::io;
#[cfg(not(any(windows, target_os = "wasi")))]
use rustix::process::{getegid, geteuid};
use std::ffi::OsStr;
use std::fs::OpenOptions;

/// Check `-r`, `-w` and `-x` with the process's effective credentials.
///
/// Let the OS account for supplementary groups, ACLs and privileged users.
#[cfg(not(any(windows, target_os = "wasi")))]
pub(crate) fn effective_access(path: &OsStr, access: Access) -> bool {
use rustix::fs::{AtFlags, CWD, accessat};

#[cfg(target_os = "android")]
let flags = AtFlags::empty();

#[cfg(not(target_os = "android"))]
let flags = AtFlags::EACCESS;

match accessat(CWD, path, access, flags) {
Ok(()) => true,
Err(io::Errno::NOSYS) | Err(io::Errno::OPNOTSUPP) => {
effective_access_fallback(path, access)
}
Err(_) => false,
}
}
fn effective_access_fallback(path: &OsStr, access: Access) -> bool {
use rustix::fs::Mode;
use rustix::fs::stat;
use rustix::process::getgroups;
if access.contains(Access::EXEC_OK) {
let Ok(st) = stat(path) else {
return false;
};

let mode = Mode::from_raw_mode(st.st_mode);
let euid = geteuid().as_raw();
let egid = getegid().as_raw();

let executable = if euid == 0 {
mode.intersects(Mode::XUSR | Mode::XGRP | Mode::XOTH)
} else if st.st_uid == euid {
mode.contains(Mode::XUSR)
} else {
let in_group = st.st_gid == egid
|| getgroups()
.is_ok_and(|groups| groups.iter().any(|gid| gid.as_raw() == st.st_gid));

if in_group {
mode.contains(Mode::XGRP)
} else {
mode.contains(Mode::XOTH)
}
};

if !executable {
return false;
}
}

if access.intersects(Access::READ_OK | Access::WRITE_OK) {
let mut options = OpenOptions::new();

options
.read(access.contains(Access::READ_OK))
.write(access.contains(Access::WRITE_OK));

if options.open(path).is_err() {
return false;
}
}

if access == Access::EXISTS {
return stat(path).is_ok();
}

true
}
Loading
Loading