Skip to content

Commit 80a0bd2

Browse files
authored
Merge pull request #215 from JRF63/issue199
Fix #199
2 parents 7c928a3 + c2f7ac0 commit 80a0bd2

File tree

2 files changed

+61
-7
lines changed

2 files changed

+61
-7
lines changed

tree/common/mod.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,25 @@ where
175175
}
176176

177177
unsafe {
178+
// Creates the target directory with the same file permission bits as the source,
179+
// modified by the umask of the process. Copying the permission bits without the
180+
// umask is postponed to the `postprocess_dir` closure on the call to
181+
// `traverse_directory` inside `copy_file`.
178182
let ret = libc::mkdirat(
179183
target_dirfd,
180184
target_filename,
181-
source_md.mode() as libc::mode_t,
185+
// OR'ed with S_IRWXU according to the spec
186+
source_md.mode() as libc::mode_t | libc::S_IRWXU,
182187
);
188+
183189
if ret != 0 {
184-
return Err(io::Error::last_os_error());
190+
let e = io::Error::last_os_error();
191+
let err_str = gettext!(
192+
"cannot create directory '{}': {}",
193+
target.display(),
194+
error_string(&e)
195+
);
196+
return Err(io::Error::other(err_str));
185197
}
186198
}
187199
}
@@ -515,14 +527,25 @@ where
515527
// not allow atomically creating a directory then opening it:
516528
//
517529
// https://stackoverflow.com/questions/45818628/whats-the-expected-behavior-of-openname-o-creato-directory-mode/48693137#48693137
518-
let new_target_dirfd = ftw::FileDescriptor::open_at(
530+
let new_target_dirfd = match ftw::FileDescriptor::open_at(
519531
target_dirfd,
520532
target_filename_cstr.as_ptr(),
521533
libc::O_RDONLY,
522-
)
523-
.map_err(|_| ())?;
524-
target_dirfd_stack_borrowed.push(new_target_dirfd);
534+
) {
535+
Ok(fd) => fd,
536+
Err(e) => {
537+
let err_str = gettext!(
538+
"cannot open directory '{}': {}",
539+
target.display(),
540+
error_string(&e)
541+
);
542+
*last_error.borrow_mut() = Some(io::Error::other(err_str));
543+
*terminate_borrowed = true;
544+
return Ok(false);
545+
}
546+
};
525547

548+
target_dirfd_stack_borrowed.push(new_target_dirfd);
526549
target_dir_path_borrowed.push(target_filename);
527550

528551
true

tree/tests/cp/mod.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ fn test_cp_fail_perm() {
342342
1,
343343
);
344344

345-
for f in [test_dir, d, d_a] {
345+
for f in [test_dir, d, d_a, dd] {
346346
fs::set_permissions(f, fs::Permissions::from_mode(0o777)).unwrap();
347347
}
348348
fs::remove_dir_all(test_dir).unwrap();
@@ -939,3 +939,34 @@ fn test_cp_special_bits() {
939939

940940
fs::remove_dir_all(test_dir).unwrap();
941941
}
942+
943+
// Replicates failure to copy D/D on `test_cp_fail_perm` due to not OR'ing with S_IRWXU:
944+
// https://github.com/rustcoreutils/posixutils-rs/issues/199
945+
#[test]
946+
fn test_cp_issue199() {
947+
let test_dir = &format!("{}/test_cp_issue199", env!("CARGO_TARGET_TMPDIR"));
948+
let d = &format!("{test_dir}/D");
949+
let dd = &format!("{test_dir}/DD");
950+
let d_d = &format!("{test_dir}/D/D");
951+
952+
let setgid = libc::S_ISGID as u32;
953+
let setuid = libc::S_ISUID as u32;
954+
955+
fs::create_dir(test_dir).unwrap();
956+
957+
let mode = fs::symlink_metadata(test_dir).unwrap().mode();
958+
fs::set_permissions(test_dir, fs::Permissions::from_mode(mode & !setgid)).unwrap();
959+
960+
fs::create_dir(d).unwrap();
961+
fs::create_dir(d_d).unwrap();
962+
963+
let nonperm_bits = fs::symlink_metadata(d).unwrap().mode() & !(setgid | setuid) & !0o777;
964+
fs::set_permissions(d, fs::Permissions::from_mode(0o500 | nonperm_bits)).unwrap();
965+
966+
cp_test(&["-pR", d, dd], "", "", 0);
967+
968+
fs::set_permissions(d, fs::Permissions::from_mode(0o777)).unwrap();
969+
fs::set_permissions(dd, fs::Permissions::from_mode(0o777)).unwrap();
970+
971+
fs::remove_dir_all(test_dir).unwrap();
972+
}

0 commit comments

Comments
 (0)