Skip to content

Commit 4dc6dbf

Browse files
committed
More tests #3
1 parent 2dec6f5 commit 4dc6dbf

File tree

3 files changed

+218
-7
lines changed

3 files changed

+218
-7
lines changed

tree/chgrp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
8282

8383
// Enable `no_derereference` if `-R` is enabled without either `-H` or `-L`
8484
if args.delegate.recurse && !(args.delegate.follow_cli || args.delegate.follow_symlinks) {
85-
args.delegate.no_derereference = true;
85+
args.delegate.no_dereference = true;
8686
}
8787

8888
// initialize translations

tree/common/change_ownership.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub struct ChangeOwnershipArgs {
2020

2121
/// Change symbolic links, rather than the files they point to
2222
#[arg(short = 'h', long, default_value_t = false)]
23-
pub no_derereference: bool,
23+
pub no_dereference: bool,
2424

2525
/// Follow command line symlinks during -R recursion
2626
#[arg(short = 'H', overrides_with_all = ["follow_cli", "follow_symlinks", "follow_none"])]
@@ -31,7 +31,7 @@ pub struct ChangeOwnershipArgs {
3131
pub follow_symlinks: bool,
3232

3333
/// Never follow symlinks during -R recursion
34-
#[arg(short = 'P', overrides_with_all = ["follow_cli", "follow_symlinks", "follow_none"], default_value_t = true)]
34+
#[arg(short = 'P', overrides_with_all = ["follow_cli", "follow_symlinks", "follow_none"])]
3535
pub follow_none: bool,
3636

3737
/// Recursively change groups of directories and their contents
@@ -52,7 +52,9 @@ where
5252
G: Fn(io::Error, ftw::DisplayablePath), // separately to use two different closures
5353
{
5454
let recurse = args.recurse;
55-
let no_derereference = args.no_derereference;
55+
let no_dereference = args.no_dereference;
56+
let follow_none = args.follow_none;
57+
let follow_symlinks = args.follow_symlinks;
5658

5759
let terminate = RefCell::new(false);
5860

@@ -79,14 +81,15 @@ where
7981
uid,
8082
gid,
8183
// Default is to change the file that the symbolic link points to unless the
82-
// -h flag is specified.
83-
if no_derereference {
84+
// -h flag or -P flag is specified.
85+
if no_dereference || follow_none {
8486
libc::AT_SYMLINK_NOFOLLOW
8587
} else {
8688
0
8789
},
8890
)
8991
};
92+
9093
if ret != 0 {
9194
chown_err_handler(io::Error::last_os_error(), entry.path());
9295
*terminate.borrow_mut() = true;

tree/tests/chown/mod.rs

Lines changed: 209 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ fn test_chown_symlink() {
398398
// Tests the -R flag
399399
#[test]
400400
fn test_chown_recursive_basic() {
401-
let test_dir = &format!("{}/test_chown_nonexistent", env!("CARGO_TARGET_TMPDIR"));
401+
let test_dir = &format!("{}/test_chown_recursive_basic", env!("CARGO_TARGET_TMPDIR"));
402402
let f = &format!("{test_dir}/f");
403403
let f_g = &format!("{test_dir}/f/g");
404404
let f_g_h = &format!("{test_dir}/f/g/h");
@@ -420,15 +420,223 @@ fn test_chown_recursive_basic() {
420420
0,
421421
);
422422

423+
// Recursively change the owner and group of f, f/g and f/g/h
423424
assert_eq!(target_group, fs::metadata(f).unwrap().gid());
424425
assert_eq!(target_group, fs::metadata(f_g).unwrap().gid());
425426
assert_eq!(target_group, fs::metadata(f_g_h).unwrap().gid());
427+
if is_root() {
428+
let target_owner = target_owner.unwrap();
429+
assert_eq!(target_owner, fs::metadata(f).unwrap().uid());
430+
assert_eq!(target_owner, fs::metadata(f_g).unwrap().uid());
431+
assert_eq!(target_owner, fs::metadata(f_g_h).unwrap().uid());
432+
}
433+
434+
fs::remove_dir_all(test_dir).unwrap();
435+
}
436+
437+
// Tests the -P flag
438+
#[test]
439+
fn test_chown_recursive_follow_none() {
440+
let test_dir = &format!(
441+
"{}/test_chown_recursive_follow_none",
442+
env!("CARGO_TARGET_TMPDIR")
443+
);
444+
let f = &format!("{test_dir}/f");
445+
let f_g = &format!("{test_dir}/f/g");
446+
let f_g_h = &format!("{test_dir}/f/g/h");
447+
let g = &format!("{test_dir}/g");
448+
let g_symlink = &format!("{test_dir}/g/symlink");
449+
450+
fs::create_dir(test_dir).unwrap();
451+
fs::create_dir_all(f_g).unwrap();
452+
fs::File::create(f_g_h).unwrap();
453+
fs::create_dir_all(g).unwrap();
454+
unix::fs::symlink(f, g_symlink).unwrap();
455+
456+
let original_uid = unsafe { libc::geteuid() };
457+
let original_gid = unsafe { libc::getegid() };
458+
459+
let (target_owner, target_group) = select_target_ownergroup();
460+
let target_owner_str = match target_owner {
461+
Some(uid) => uid.to_string(),
462+
None => String::from(""),
463+
};
464+
465+
// chown -RP owner:group g
466+
// Only g and g/symlink should be changed. The symlink to f should not be followed.
467+
chown_test(
468+
&["-RP", &format!("{target_owner_str}:{target_group}"), g],
469+
"",
470+
"",
471+
0,
472+
);
473+
474+
// f, f/g and f/g/h must be unchanged
475+
assert_eq!(original_gid, fs::metadata(f).unwrap().gid());
476+
assert_eq!(original_gid, fs::metadata(f_g).unwrap().gid());
477+
assert_eq!(original_gid, fs::metadata(f_g_h).unwrap().gid());
478+
if is_root() {
479+
assert_eq!(original_uid, fs::metadata(f).unwrap().uid());
480+
assert_eq!(original_uid, fs::metadata(f_g).unwrap().uid());
481+
assert_eq!(original_uid, fs::metadata(f_g_h).unwrap().uid());
482+
}
483+
484+
// g and g/symlink should be changed
485+
assert_eq!(target_group, fs::metadata(g).unwrap().gid());
486+
assert_eq!(target_group, fs::symlink_metadata(g_symlink).unwrap().gid());
487+
if is_root() {
488+
let target_owner = target_owner.unwrap();
489+
assert_eq!(target_owner, fs::metadata(g).unwrap().uid());
490+
assert_eq!(target_owner, fs::symlink_metadata(g_symlink).unwrap().uid());
491+
}
492+
493+
fs::remove_dir_all(test_dir).unwrap();
494+
}
495+
496+
// Tests the -L flag
497+
#[test]
498+
fn test_chown_recursive_follow_symlinks() {
499+
let test_dir = &format!(
500+
"{}/test_chown_recursive_follow_symlinks",
501+
env!("CARGO_TARGET_TMPDIR")
502+
);
503+
let f = &format!("{test_dir}/f");
504+
let f_g = &format!("{test_dir}/f/g");
505+
let f_g_h = &format!("{test_dir}/f/g/h");
506+
let g = &format!("{test_dir}/g");
507+
let g_symlink = &format!("{test_dir}/g/symlink");
508+
509+
fs::create_dir(test_dir).unwrap();
510+
fs::create_dir_all(f_g).unwrap();
511+
fs::File::create(f_g_h).unwrap();
512+
fs::create_dir_all(g).unwrap();
513+
unix::fs::symlink(f, g_symlink).unwrap();
514+
515+
let original_uid = unsafe { libc::geteuid() };
516+
let original_gid = unsafe { libc::getegid() };
517+
518+
let (target_owner, target_group) = select_target_ownergroup();
519+
let target_owner_str = match target_owner {
520+
Some(uid) => uid.to_string(),
521+
None => String::from(""),
522+
};
523+
524+
// chown -RL owner:group g
525+
// Follow the symlink to f and change everything. `g/symlink` remains unchanged because f was
526+
// changed in its place.
527+
chown_test(
528+
&["-RL", &format!("{target_owner_str}:{target_group}"), g],
529+
"",
530+
"",
531+
0,
532+
);
426533

534+
// g/symlink should be unchanged
535+
assert_eq!(original_gid, fs::symlink_metadata(g_symlink).unwrap().gid());
536+
if is_root() {
537+
assert_eq!(original_uid, fs::symlink_metadata(g_symlink).unwrap().uid());
538+
}
539+
540+
// f, g, f/g and f/g/h should be changed
541+
assert_eq!(target_group, fs::metadata(f).unwrap().gid());
542+
assert_eq!(target_group, fs::metadata(f_g).unwrap().gid());
543+
assert_eq!(target_group, fs::metadata(f_g_h).unwrap().gid());
544+
assert_eq!(target_group, fs::metadata(g).unwrap().gid());
427545
if is_root() {
428546
let target_owner = target_owner.unwrap();
429547
assert_eq!(target_owner, fs::metadata(f).unwrap().uid());
430548
assert_eq!(target_owner, fs::metadata(f_g).unwrap().uid());
431549
assert_eq!(target_owner, fs::metadata(f_g_h).unwrap().uid());
550+
assert_eq!(target_owner, fs::metadata(g).unwrap().uid());
551+
}
552+
553+
// Recreate the files
554+
fs::remove_dir_all(test_dir).unwrap();
555+
fs::create_dir(test_dir).unwrap();
556+
fs::create_dir_all(f_g).unwrap();
557+
fs::File::create(f_g_h).unwrap();
558+
fs::create_dir_all(g).unwrap();
559+
unix::fs::symlink(f, g_symlink).unwrap();
560+
561+
// chown -RLh owner:group g
562+
// Follow the symlink to f and change everything. `f` is the one unchanged when -h is used
563+
chown_test(
564+
&["-RLh", &format!("{target_owner_str}:{target_group}"), g],
565+
"",
566+
"",
567+
0,
568+
);
569+
570+
// f should be unchanged
571+
assert_eq!(original_gid, fs::metadata(f).unwrap().gid());
572+
if is_root() {
573+
assert_eq!(original_uid, fs::metadata(f).unwrap().uid());
574+
}
575+
576+
// f, g, f/g and f/g/h should be changed
577+
assert_eq!(target_group, fs::symlink_metadata(g_symlink).unwrap().gid());
578+
assert_eq!(target_group, fs::metadata(f_g).unwrap().gid());
579+
assert_eq!(target_group, fs::metadata(f_g_h).unwrap().gid());
580+
assert_eq!(target_group, fs::metadata(g).unwrap().gid());
581+
if is_root() {
582+
let target_owner = target_owner.unwrap();
583+
assert_eq!(target_owner, fs::symlink_metadata(g_symlink).unwrap().uid());
584+
assert_eq!(target_owner, fs::metadata(f_g).unwrap().uid());
585+
assert_eq!(target_owner, fs::metadata(f_g_h).unwrap().uid());
586+
assert_eq!(target_owner, fs::metadata(g).unwrap().uid());
587+
}
588+
589+
fs::remove_dir_all(test_dir).unwrap();
590+
}
591+
592+
// Changing ownership should propagate through hard links
593+
#[test]
594+
fn test_chown_hardlink() {
595+
let test_dir = &format!("{}/test_chown_hardlink", env!("CARGO_TARGET_TMPDIR"));
596+
let f = &format!("{test_dir}/f");
597+
let hardlink = &format!("{test_dir}/hardlink");
598+
599+
fs::create_dir(test_dir).unwrap();
600+
fs::File::create(f).unwrap();
601+
fs::hard_link(f, hardlink).unwrap();
602+
603+
let (target_owner, target_group) = select_target_ownergroup();
604+
let target_owner_str = match target_owner {
605+
Some(uid) => uid.to_string(),
606+
None => String::from(""),
607+
};
608+
609+
chown_test(
610+
&[&format!("{target_owner_str}:{target_group}"), f],
611+
"",
612+
"",
613+
0,
614+
);
615+
616+
// `hardlink` should be changed
617+
assert_eq!(target_group, fs::metadata(hardlink).unwrap().gid());
618+
if is_root() {
619+
assert_eq!(target_owner.unwrap(), fs::metadata(hardlink).unwrap().uid());
620+
}
621+
622+
// Recreate f and the hard link
623+
fs::remove_file(f).unwrap();
624+
fs::remove_file(hardlink).unwrap();
625+
fs::File::create(f).unwrap();
626+
fs::hard_link(f, hardlink).unwrap();
627+
628+
// Test changing ownership using the hard link
629+
chown_test(
630+
&[&format!("{target_owner_str}:{target_group}"), hardlink],
631+
"",
632+
"",
633+
0,
634+
);
635+
636+
// `f` should be changed
637+
assert_eq!(target_group, fs::metadata(f).unwrap().gid());
638+
if is_root() {
639+
assert_eq!(target_owner.unwrap(), fs::metadata(f).unwrap().uid());
432640
}
433641

434642
fs::remove_dir_all(test_dir).unwrap();

0 commit comments

Comments
 (0)