Skip to content

Commit ca01f55

Browse files
committed
fix(cp): permissions mode and umask aware
1 parent 107dc44 commit ca01f55

3 files changed

Lines changed: 532 additions & 141 deletions

File tree

src/uu/cp/src/copydir.rs

Lines changed: 34 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ fn copy_direntry(
264264
copied_destinations: &HashSet<PathBuf>,
265265
copied_files: &mut HashMap<FileInformation, PathBuf>,
266266
created_parent_dirs: &mut HashSet<PathBuf>,
267+
#[cfg(unix)] orig_umask: u32,
267268
) -> CopyResult<bool> {
268269
let source_is_symlink = entry_is_symlink;
269270
let source_is_dir = if source_is_symlink && !options.dereference {
@@ -280,12 +281,7 @@ fn copy_direntry(
280281
return if entry.target_is_file {
281282
Err(translate!("cp-error-cannot-overwrite-non-directory-with-directory").into())
282283
} else {
283-
build_dir(
284-
&entry.local_to_target,
285-
false,
286-
options,
287-
Some(&entry.source_absolute),
288-
)?;
284+
build_dir(&entry.local_to_target, false)?;
289285
if options.verbose {
290286
println!(
291287
"{}",
@@ -308,6 +304,8 @@ fn copy_direntry(
308304
copied_files,
309305
created_parent_dirs,
310306
false,
307+
#[cfg(unix)]
308+
orig_umask,
311309
) {
312310
if preserve_hard_links {
313311
if !source_is_symlink {
@@ -361,6 +359,7 @@ pub(crate) fn copy_directory(
361359
copied_files: &mut HashMap<FileInformation, PathBuf>,
362360
created_parent_dirs: &mut HashSet<PathBuf>,
363361
source_in_command_line: bool,
362+
#[cfg(unix)] orig_umask: u32,
364363
) -> CopyResult<()> {
365364
// if no-dereference is enabled and this is a symlink, copy it as a file
366365
if !options.dereference(source_in_command_line) && root.is_symlink() {
@@ -374,6 +373,8 @@ pub(crate) fn copy_directory(
374373
copied_files,
375374
created_parent_dirs,
376375
source_in_command_line,
376+
#[cfg(unix)]
377+
orig_umask,
377378
);
378379
}
379380

@@ -399,7 +400,7 @@ pub(crate) fn copy_directory(
399400
let tmp = if options.parents {
400401
if let Some(parent) = root.parent() {
401402
let new_target = target.join(parent);
402-
build_dir(&new_target, true, options, None)?;
403+
build_dir(&new_target, true)?;
403404
if options.verbose {
404405
// For example, if copying file `a/b/c` and its parents
405406
// to directory `d/`, then print
@@ -469,6 +470,8 @@ pub(crate) fn copy_directory(
469470
copied_destinations,
470471
copied_files,
471472
created_parent_dirs,
473+
#[cfg(unix)]
474+
orig_umask,
472475
)?;
473476

474477
// We omit certain permissions when creating directories
@@ -492,6 +495,8 @@ pub(crate) fn copy_directory(
492495
&entry.local_to_target,
493496
&options.attributes,
494497
false,
498+
#[cfg(unix)]
499+
orig_umask,
495500
)?;
496501
continue;
497502
}
@@ -534,6 +539,8 @@ pub(crate) fn copy_directory(
534539
&entry.local_to_target,
535540
&options.attributes,
536541
false,
542+
#[cfg(unix)]
543+
orig_umask,
537544
)?;
538545
}
539546
}
@@ -550,7 +557,14 @@ pub(crate) fn copy_directory(
550557
// Fix permissions for all directories we created
551558
// This ensures that even sibling directories get their permissions fixed
552559
for dir in dirs_needing_permissions {
553-
copy_attributes(&dir.source, &dir.dest, &options.attributes, dir.was_created)?;
560+
copy_attributes(
561+
&dir.source,
562+
&dir.dest,
563+
&options.attributes,
564+
dir.was_created,
565+
#[cfg(unix)]
566+
orig_umask,
567+
)?;
554568
}
555569

556570
// Also fix permissions for parent directories,
@@ -559,7 +573,14 @@ pub(crate) fn copy_directory(
559573
let dest = target.join(root.file_name().unwrap());
560574
for (x, y) in aligned_ancestors(root, dest.as_path()) {
561575
if let Ok(src) = canonicalize(x, MissingHandling::Normal, ResolveMode::Physical) {
562-
copy_attributes(&src, y, &options.attributes, false)?;
576+
copy_attributes(
577+
&src,
578+
y,
579+
&options.attributes,
580+
false,
581+
#[cfg(unix)]
582+
orig_umask,
583+
)?;
563584
}
564585
}
565586
}
@@ -593,57 +614,12 @@ pub fn path_has_prefix(p1: &Path, p2: &Path) -> io::Result<bool> {
593614

594615
/// Builds a directory at the specified path with the given options.
595616
///
596-
/// # Notes
597-
/// - If `copy_attributes_from` is `Some`, the new directory's attributes will be
598-
/// copied from the provided file. Otherwise, the new directory will have the default
599-
/// attributes for the current user.
600-
/// - This method excludes certain permissions if ownership or special mode bits could
601-
/// potentially change. (See `test_dir_perm_race_with_preserve_mode_and_ownership`)
602-
/// - The `recursive` flag determines whether parent directories should be created
603-
/// if they do not already exist.
604-
// we need to allow unused_variable since `options` might be unused in non unix systems
605-
#[allow(unused_variables)]
606-
fn build_dir(
607-
path: &PathBuf,
608-
recursive: bool,
609-
options: &Options,
610-
copy_attributes_from: Option<&Path>,
611-
) -> CopyResult<()> {
617+
/// The `recursive` flag determines whether parent directories should be created if they do not
618+
/// already exist.
619+
#[inline]
620+
fn build_dir(path: &PathBuf, recursive: bool) -> CopyResult<()> {
612621
let mut builder = fs::DirBuilder::new();
613622
builder.recursive(recursive);
614-
615-
// To prevent unauthorized access before the folder is ready,
616-
// exclude certain permissions if ownership or special mode bits
617-
// could potentially change.
618-
#[cfg(unix)]
619-
{
620-
use crate::Preserve;
621-
use std::os::unix::fs::PermissionsExt;
622-
623-
// we need to allow trivial casts here because some systems like linux have u32 constants in
624-
// in libc while others don't.
625-
#[allow(clippy::unnecessary_cast)]
626-
let mut excluded_perms = if matches!(options.attributes.ownership, Preserve::Yes { .. }) {
627-
libc::S_IRWXG | libc::S_IRWXO // exclude rwx for group and other
628-
} else if matches!(options.attributes.mode, Preserve::Yes { .. }) {
629-
libc::S_IWGRP | libc::S_IWOTH //exclude w for group and other
630-
} else {
631-
0
632-
} as u32;
633-
634-
let umask = if let (Some(from), Preserve::Yes { .. }) =
635-
(copy_attributes_from, options.attributes.mode)
636-
{
637-
!fs::symlink_metadata(from)?.permissions().mode()
638-
} else {
639-
uucore::mode::get_umask()
640-
};
641-
642-
excluded_perms |= umask;
643-
let mode = !excluded_perms & 0o777; //use only the last three octet bits
644-
std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode);
645-
}
646-
647623
builder.create(path)?;
648624
Ok(())
649625
}

0 commit comments

Comments
 (0)