Skip to content

Commit ee3198b

Browse files
authored
Merge branch 'main' into fix-touch-dev-full
2 parents 57bbc7f + ef8fcf6 commit ee3198b

File tree

20 files changed

+332
-91
lines changed

20 files changed

+332
-91
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ are some tips for writing good issues:
8787
- What platform are you on?
8888
- Provide a way to reliably reproduce the issue.
8989
- Be as specific as possible!
90+
- Please provide the output with LANG=C, except for locale-related bugs.
9091

9192
### Writing Documentation
9293

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ feat_selinux = [
6565
"selinux",
6666
"stat/selinux",
6767
]
68+
# "feat_smack" == enable support for SMACK Security Context (by using `--features feat_smack`)
69+
# NOTE:
70+
# * Running a uutils compiled with `feat_smack` requires a SMACK enabled Kernel at run time.
71+
feat_smack = ["ls/smack"]
6872
##
6973
## feature sets
7074
## (common/core and Tier1) feature sets

README.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,6 @@ options might be missing or different behavior might be experienced.
3232
We provide prebuilt binaries at https://github.com/uutils/coreutils/releases/latest .
3333
It is recommended to install from main branch if you install from source.
3434

35-
To install it:
36-
37-
```shell
38-
cargo install --git https://github.com/uutils/coreutils coreutils
39-
# cargo install --git https://github.com/uutils/coreutils uu_true # for one util only
40-
~/.cargo/bin/coreutils
41-
```
42-
4335
</div>
4436

4537
<!-- markdownlint-disable-next-line MD026 -->

src/uu/date/src/locale.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,23 @@ cfg_langinfo! {
2929
use std::ffi::CStr;
3030
use std::sync::OnceLock;
3131
use nix::libc;
32+
33+
#[cfg(test)]
34+
use std::sync::Mutex;
3235
}
3336

3437
cfg_langinfo! {
3538
/// Cached locale date/time format string
3639
static DEFAULT_FORMAT_CACHE: OnceLock<&'static str> = OnceLock::new();
3740

41+
/// Mutex to serialize setlocale() calls during tests.
42+
///
43+
/// setlocale() is process-global, so parallel tests that call it can
44+
/// interfere with each other. This mutex ensures only one test accesses
45+
/// locale functions at a time.
46+
#[cfg(test)]
47+
static LOCALE_MUTEX: Mutex<()> = Mutex::new(());
48+
3849
/// Returns the default date format string for the current locale.
3950
///
4051
/// The format respects locale preferences for time display (12-hour vs 24-hour),
@@ -55,6 +66,11 @@ cfg_langinfo! {
5566

5667
/// Retrieves the date/time format string from the system locale
5768
fn get_locale_format_string() -> Option<String> {
69+
// In tests, acquire mutex to prevent race conditions with setlocale()
70+
// which is process-global and not thread-safe
71+
#[cfg(test)]
72+
let _lock = LOCALE_MUTEX.lock().unwrap();
73+
5874
unsafe {
5975
// Set locale from environment variables
6076
libc::setlocale(libc::LC_TIME, c"".as_ptr());
@@ -158,11 +174,24 @@ mod tests {
158174

159175
#[test]
160176
fn test_c_locale_format() {
161-
// Save original locale
177+
// Acquire mutex to prevent interference with other tests
178+
let _lock = LOCALE_MUTEX.lock().unwrap();
179+
180+
// Save original locale (both environment and process locale)
162181
let original_lc_all = std::env::var("LC_ALL").ok();
163182
let original_lc_time = std::env::var("LC_TIME").ok();
164183
let original_lang = std::env::var("LANG").ok();
165184

185+
// Save current process locale
186+
let original_process_locale = unsafe {
187+
let ptr = libc::setlocale(libc::LC_TIME, std::ptr::null());
188+
if ptr.is_null() {
189+
None
190+
} else {
191+
CStr::from_ptr(ptr).to_str().ok().map(|s| s.to_string())
192+
}
193+
};
194+
166195
unsafe {
167196
// Set C locale
168197
std::env::set_var("LC_ALL", "C");
@@ -177,7 +206,7 @@ mod tests {
177206
if d_t_fmt_ptr.is_null() {
178207
None
179208
} else {
180-
std::ffi::CStr::from_ptr(d_t_fmt_ptr).to_str().ok()
209+
CStr::from_ptr(d_t_fmt_ptr).to_str().ok()
181210
}
182211
};
183212

@@ -190,7 +219,7 @@ mod tests {
190219
assert!(uses_24_hour, "C locale should use 24-hour format, got: {locale_format}");
191220
}
192221

193-
// Restore original locale
222+
// Restore original environment variables
194223
unsafe {
195224
if let Some(val) = original_lc_all {
196225
std::env::set_var("LC_ALL", val);
@@ -208,6 +237,17 @@ mod tests {
208237
std::env::remove_var("LANG");
209238
}
210239
}
240+
241+
// Restore original process locale
242+
unsafe {
243+
if let Some(locale) = original_process_locale {
244+
let c_locale = std::ffi::CString::new(locale).unwrap();
245+
libc::setlocale(libc::LC_TIME, c_locale.as_ptr());
246+
} else {
247+
// Restore from environment
248+
libc::setlocale(libc::LC_TIME, c"".as_ptr());
249+
}
250+
}
211251
}
212252

213253
#[test]

src/uu/env/src/env.rs

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -701,9 +701,24 @@ impl EnvAppData {
701701
#[cfg(unix)]
702702
{
703703
let mut signal_action_log = SignalActionLog::default();
704-
apply_default_signal(&opts.default_signal, &mut signal_action_log)?;
705-
apply_ignore_signal(&opts.ignore_signal, &mut signal_action_log)?;
706-
apply_block_signal(&opts.block_signal, &mut signal_action_log)?;
704+
apply_signal_action(
705+
&opts.default_signal,
706+
&mut signal_action_log,
707+
SignalActionKind::Default,
708+
reset_signal,
709+
)?;
710+
apply_signal_action(
711+
&opts.ignore_signal,
712+
&mut signal_action_log,
713+
SignalActionKind::Ignore,
714+
ignore_signal,
715+
)?;
716+
apply_signal_action(
717+
&opts.block_signal,
718+
&mut signal_action_log,
719+
SignalActionKind::Block,
720+
block_signal,
721+
)?;
707722
if opts.list_signal_handling {
708723
list_signal_handling(&signal_action_log);
709724
}
@@ -990,19 +1005,29 @@ fn apply_specified_env_vars(opts: &Options<'_>) {
9901005
}
9911006

9921007
#[cfg(unix)]
993-
fn apply_default_signal(request: &SignalRequest, log: &mut SignalActionLog) -> UResult<()> {
1008+
fn apply_signal_action<F>(
1009+
request: &SignalRequest,
1010+
log: &mut SignalActionLog,
1011+
action_kind: SignalActionKind,
1012+
signal_fn: F,
1013+
) -> UResult<()>
1014+
where
1015+
F: Fn(Signal) -> UResult<()>,
1016+
{
9941017
request.for_each_signal(|sig_value, explicit| {
9951018
// On some platforms ALL_SIGNALS may contain values that are not valid in libc.
9961019
// Skip those invalid ones and continue (GNU env also ignores undefined signals).
9971020
let Ok(sig) = signal_from_value(sig_value) else {
9981021
return Ok(());
9991022
};
1000-
reset_signal(sig)?;
1001-
log.record(sig_value, SignalActionKind::Default, explicit);
1023+
signal_fn(sig)?;
1024+
log.record(sig_value, action_kind, explicit);
10021025

10031026
// Set environment variable to communicate to Rust child processes
10041027
// that SIGPIPE should be default (not ignored)
1005-
if sig_value == nix::libc::SIGPIPE as usize {
1028+
if matches!(action_kind, SignalActionKind::Default)
1029+
&& sig_value == nix::libc::SIGPIPE as usize
1030+
{
10061031
unsafe {
10071032
std::env::set_var("RUST_SIGPIPE", "default");
10081033
}
@@ -1012,30 +1037,6 @@ fn apply_default_signal(request: &SignalRequest, log: &mut SignalActionLog) -> U
10121037
})
10131038
}
10141039

1015-
#[cfg(unix)]
1016-
fn apply_ignore_signal(request: &SignalRequest, log: &mut SignalActionLog) -> UResult<()> {
1017-
request.for_each_signal(|sig_value, explicit| {
1018-
let Ok(sig) = signal_from_value(sig_value) else {
1019-
return Ok(());
1020-
};
1021-
ignore_signal(sig)?;
1022-
log.record(sig_value, SignalActionKind::Ignore, explicit);
1023-
Ok(())
1024-
})
1025-
}
1026-
1027-
#[cfg(unix)]
1028-
fn apply_block_signal(request: &SignalRequest, log: &mut SignalActionLog) -> UResult<()> {
1029-
request.for_each_signal(|sig_value, explicit| {
1030-
let Ok(sig) = signal_from_value(sig_value) else {
1031-
return Ok(());
1032-
};
1033-
block_signal(sig)?;
1034-
log.record(sig_value, SignalActionKind::Block, explicit);
1035-
Ok(())
1036-
})
1037-
}
1038-
10391040
#[cfg(unix)]
10401041
fn ignore_signal(sig: Signal) -> UResult<()> {
10411042
// SAFETY: This is safe because we write the handler for each signal only once, and therefore "the current handler is the default", as the documentation requires it.

src/uu/ls/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ harness = false
6060

6161
[features]
6262
feat_selinux = ["selinux", "uucore/selinux"]
63+
smack = ["uucore/smack"]

src/uu/ls/locales/en-US.ftl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,13 @@ ls-invalid-columns-width = ignoring invalid width in environment variable COLUMN
124124
ls-invalid-ignore-pattern = Invalid pattern for ignore: {$pattern}
125125
ls-invalid-hide-pattern = Invalid pattern for hide: {$pattern}
126126
ls-total = total {$size}
127+
128+
# Security context warnings
129+
ls-warning-failed-to-get-security-context = failed to get security context of: {$path}
130+
ls-warning-getting-security-context = getting security context of: {$path}: {$error}
131+
132+
# SMACK error messages (used by uucore::smack when called from ls)
133+
smack-error-not-enabled = SMACK is not enabled on this system
134+
smack-error-label-retrieval-failure = failed to get SMACK label: { $error }
135+
smack-error-label-set-failure = failed to set SMACK label to '{ $context }': { $error }
136+
smack-error-no-label-set = no SMACK label set

src/uu/ls/src/ls.rs

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,10 @@ pub struct Config {
365365
time_format_recent: String, // Time format for recent dates
366366
time_format_older: Option<String>, // Time format for older dates (optional, if not present, time_format_recent is used)
367367
context: bool,
368+
#[cfg(all(feature = "selinux", target_os = "linux"))]
368369
selinux_supported: bool,
370+
#[cfg(all(feature = "smack", target_os = "linux"))]
371+
smack_supported: bool,
369372
group_directories_first: bool,
370373
line_ending: LineEnding,
371374
dired: bool,
@@ -1157,16 +1160,10 @@ impl Config {
11571160
time_format_recent,
11581161
time_format_older,
11591162
context,
1160-
selinux_supported: {
1161-
#[cfg(all(feature = "selinux", target_os = "linux"))]
1162-
{
1163-
uucore::selinux::is_selinux_enabled()
1164-
}
1165-
#[cfg(not(all(feature = "selinux", target_os = "linux")))]
1166-
{
1167-
false
1168-
}
1169-
},
1163+
#[cfg(all(feature = "selinux", target_os = "linux"))]
1164+
selinux_supported: uucore::selinux::is_selinux_enabled(),
1165+
#[cfg(all(feature = "smack", target_os = "linux"))]
1166+
smack_supported: uucore::smack::is_smack_enabled(),
11701167
group_directories_first: options.get_flag(options::GROUP_DIRECTORIES_FIRST),
11711168
line_ending: LineEnding::from_zero_flag(options.get_flag(options::ZERO)),
11721169
dired,
@@ -3387,37 +3384,59 @@ fn get_security_context<'a>(
33873384
}
33883385
}
33893386

3387+
#[cfg(all(feature = "selinux", target_os = "linux"))]
33903388
if config.selinux_supported {
3391-
#[cfg(all(feature = "selinux", target_os = "linux"))]
3392-
{
3393-
match selinux::SecurityContext::of_path(path, must_dereference, false) {
3394-
Err(_r) => {
3395-
// TODO: show the actual reason why it failed
3396-
show_warning!("failed to get security context of: {}", path.quote());
3397-
return Cow::Borrowed(SUBSTITUTE_STRING);
3398-
}
3399-
Ok(None) => return Cow::Borrowed(SUBSTITUTE_STRING),
3400-
Ok(Some(context)) => {
3401-
let context = context.as_bytes();
3389+
match selinux::SecurityContext::of_path(path, must_dereference, false) {
3390+
Err(_r) => {
3391+
// TODO: show the actual reason why it failed
3392+
show_warning!(
3393+
"{}",
3394+
translate!(
3395+
"ls-warning-failed-to-get-security-context",
3396+
"path" => path.quote().to_string()
3397+
)
3398+
);
3399+
return Cow::Borrowed(SUBSTITUTE_STRING);
3400+
}
3401+
Ok(None) => return Cow::Borrowed(SUBSTITUTE_STRING),
3402+
Ok(Some(context)) => {
3403+
let context = context.as_bytes();
34023404

3403-
let context = context.strip_suffix(&[0]).unwrap_or(context);
3405+
let context = context.strip_suffix(&[0]).unwrap_or(context);
34043406

3405-
let res: String = String::from_utf8(context.to_vec()).unwrap_or_else(|e| {
3406-
show_warning!(
3407-
"getting security context of: {}: {}",
3408-
path.quote(),
3409-
e.to_string()
3410-
);
3407+
let res: String = String::from_utf8(context.to_vec()).unwrap_or_else(|e| {
3408+
show_warning!(
3409+
"{}",
3410+
translate!(
3411+
"ls-warning-getting-security-context",
3412+
"path" => path.quote().to_string(),
3413+
"error" => e.to_string()
3414+
)
3415+
);
34113416

3412-
String::from_utf8_lossy(context).to_string()
3413-
});
3417+
String::from_utf8_lossy(context).to_string()
3418+
});
34143419

3415-
return Cow::Owned(res);
3416-
}
3420+
return Cow::Owned(res);
34173421
}
34183422
}
34193423
}
34203424

3425+
#[cfg(all(feature = "smack", target_os = "linux"))]
3426+
if config.smack_supported {
3427+
// For SMACK, use the path to get the label
3428+
// If must_dereference is true, we follow the symlink
3429+
let target_path = if must_dereference {
3430+
std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
3431+
} else {
3432+
path.to_path_buf()
3433+
};
3434+
3435+
return uucore::smack::get_smack_label_for_path(&target_path)
3436+
.map(Cow::Owned)
3437+
.unwrap_or(Cow::Borrowed(SUBSTITUTE_STRING));
3438+
}
3439+
34213440
Cow::Borrowed(SUBSTITUTE_STRING)
34223441
}
34233442

src/uu/ptx/locales/en-US.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ ptx-error-dumb-format = There is no dumb format with GNU extensions disabled
2828
ptx-error-not-implemented = { $feature } not implemented yet
2929
ptx-error-write-failed = write failed
3030
ptx-error-extra-operand = extra operand { $operand }
31+
ptx-error-empty-regexp = A regular expression cannot match a length zero string
32+
ptx-error-invalid-regexp = Invalid regexp: { $error }

0 commit comments

Comments
 (0)