Skip to content

Commit 0c41299

Browse files
authored
fix(install): prevent symlink race condition in install -D (fixes #10013) (#10140)
1 parent 5dcdba1 commit 0c41299

8 files changed

Lines changed: 762 additions & 110 deletions

File tree

.vscode/cspell.dictionaries/jargon.wordlist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ noxfer
116116
ofile
117117
oflag
118118
oflags
119+
openat
119120
pdeathsig
120121
peekable
121122
performant

src/uu/chmod/src/chmod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use uucore::mode;
1919
use uucore::perms::{TraverseSymlinks, configure_symlink_and_recursion};
2020

2121
#[cfg(all(unix, not(target_os = "redox")))]
22-
use uucore::safe_traversal::DirFd;
22+
use uucore::safe_traversal::{DirFd, SymlinkBehavior};
2323
use uucore::{format_usage, show, show_error};
2424

2525
use uucore::translate;
@@ -473,7 +473,7 @@ impl Chmoder {
473473

474474
// If the path is a directory (or we should follow symlinks), recurse into it using safe traversal
475475
if (!file_path.is_symlink() || should_follow_symlink) && file_path.is_dir() {
476-
match DirFd::open(file_path) {
476+
match DirFd::open(file_path, SymlinkBehavior::Follow) {
477477
Ok(dir_fd) => {
478478
r = self.safe_traverse_dir(&dir_fd, file_path).and(r);
479479
}
@@ -502,7 +502,7 @@ impl Chmoder {
502502
for entry_name in entries {
503503
let entry_path = dir_path.join(&entry_name);
504504

505-
let dir_meta = dir_fd.metadata_at(&entry_name, should_follow_symlink);
505+
let dir_meta = dir_fd.metadata_at(&entry_name, should_follow_symlink.into());
506506
let Ok(meta) = dir_meta else {
507507
// Handle permission denied with proper file path context
508508
let e = dir_meta.unwrap_err();
@@ -527,7 +527,7 @@ impl Chmoder {
527527

528528
// Recurse into subdirectories using the existing directory fd
529529
if meta.is_dir() {
530-
match dir_fd.open_subdir(&entry_name) {
530+
match dir_fd.open_subdir(&entry_name, SymlinkBehavior::Follow) {
531531
Ok(child_dir_fd) => {
532532
r = self.safe_traverse_dir(&child_dir_fd, &entry_path).and(r);
533533
}
@@ -591,7 +591,7 @@ impl Chmoder {
591591

592592
// Use safe traversal to change the mode
593593
let follow_symlinks = self.dereference;
594-
if let Err(_e) = dir_fd.chmod_at(entry_name, new_mode, follow_symlinks) {
594+
if let Err(_e) = dir_fd.chmod_at(entry_name, new_mode, follow_symlinks.into()) {
595595
if self.verbose {
596596
println!(
597597
"failed to change mode of {} to {new_mode:o}",

src/uu/du/src/du.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use uucore::error::{FromIo, UError, UResult, USimpleError, set_exit_code};
2626
use uucore::fsext::{MetadataTimeField, metadata_get_time};
2727
use uucore::line_ending::LineEnding;
2828
#[cfg(all(unix, not(target_os = "redox")))]
29-
use uucore::safe_traversal::DirFd;
29+
use uucore::safe_traversal::{DirFd, SymlinkBehavior};
3030
use uucore::translate;
3131

3232
use uucore::parser::parse_glob;
@@ -313,7 +313,7 @@ fn safe_du(
313313
let mut my_stat = if let Some(parent_fd) = parent_fd {
314314
// We have a parent fd, this is a subdirectory - use openat
315315
let dir_name = path.file_name().unwrap_or(path.as_os_str());
316-
match parent_fd.metadata_at(dir_name, false) {
316+
match parent_fd.metadata_at(dir_name, SymlinkBehavior::NoFollow) {
317317
Ok(safe_metadata) => {
318318
// Create Stat from safe metadata
319319
let file_info = safe_metadata.file_info();
@@ -368,7 +368,7 @@ fn safe_du(
368368
Ok(s) => s,
369369
Err(_e) => {
370370
// Try using our new DirFd method for the root directory
371-
match DirFd::open(path) {
371+
match DirFd::open(path, SymlinkBehavior::Follow) {
372372
Ok(dir_fd) => match Stat::new_from_dirfd(&dir_fd, path) {
373373
Ok(s) => s,
374374
Err(e) => {
@@ -406,8 +406,11 @@ fn safe_du(
406406

407407
// Open the directory using DirFd
408408
let open_result = match parent_fd {
409-
Some(parent) => parent.open_subdir(path.file_name().unwrap_or(path.as_os_str())),
410-
None => DirFd::open(path),
409+
Some(parent) => parent.open_subdir(
410+
path.file_name().unwrap_or(path.as_os_str()),
411+
SymlinkBehavior::Follow,
412+
),
413+
None => DirFd::open(path, SymlinkBehavior::Follow),
411414
};
412415

413416
let dir_fd = match open_result {
@@ -435,7 +438,7 @@ fn safe_du(
435438
let entry_path = path.join(&entry_name);
436439

437440
// First get the lstat (without following symlinks) to check if it's a symlink
438-
let lstat = match dir_fd.stat_at(&entry_name, false) {
441+
let lstat = match dir_fd.stat_at(&entry_name, SymlinkBehavior::NoFollow) {
439442
Ok(stat) => stat,
440443
Err(e) => {
441444
print_tx.send(Err(e.map_err_context(

0 commit comments

Comments
 (0)