Skip to content

Commit 6234f87

Browse files
committed
fix(cp): permissions mode and umask aware
1 parent 57f69ad commit 6234f87

3 files changed

Lines changed: 494 additions & 137 deletions

File tree

src/uu/cp/src/copydir.rs

Lines changed: 22 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ fn copy_direntry(
267267
copied_destinations: &HashSet<PathBuf>,
268268
copied_files: &mut HashMap<FileInformation, PathBuf>,
269269
created_parent_dirs: &mut HashSet<PathBuf>,
270+
#[cfg(unix)] orig_umask: u32,
270271
) -> CopyResult<bool> {
271272
let source_is_symlink = entry_is_symlink;
272273
let source_is_dir = if source_is_symlink && !options.dereference {
@@ -283,12 +284,7 @@ fn copy_direntry(
283284
return if entry.target_is_file {
284285
Err(translate!("cp-error-cannot-overwrite-non-directory-with-directory").into())
285286
} else {
286-
build_dir(
287-
&entry.local_to_target,
288-
false,
289-
options,
290-
Some(&entry.source_absolute),
291-
)?;
287+
build_dir(&entry.local_to_target, false)?;
292288
if options.verbose {
293289
println!(
294290
"{}",
@@ -311,6 +307,8 @@ fn copy_direntry(
311307
copied_files,
312308
created_parent_dirs,
313309
false,
310+
#[cfg(unix)]
311+
orig_umask,
314312
)
315313
{
316314
if preserve_hard_links {
@@ -364,6 +362,7 @@ pub(crate) fn copy_directory(
364362
copied_files: &mut HashMap<FileInformation, PathBuf>,
365363
created_parent_dirs: &mut HashSet<PathBuf>,
366364
source_in_command_line: bool,
365+
#[cfg(unix)] orig_umask: u32,
367366
) -> CopyResult<()> {
368367
// if no-dereference is enabled and this is a symlink, copy it as a file
369368
if !options.dereference(source_in_command_line) && root.is_symlink() {
@@ -377,6 +376,8 @@ pub(crate) fn copy_directory(
377376
copied_files,
378377
created_parent_dirs,
379378
source_in_command_line,
379+
#[cfg(unix)]
380+
orig_umask,
380381
);
381382
}
382383

@@ -402,7 +403,7 @@ pub(crate) fn copy_directory(
402403
let tmp = if options.parents {
403404
if let Some(parent) = root.parent() {
404405
let new_target = target.join(parent);
405-
build_dir(&new_target, true, options, None)?;
406+
build_dir(&new_target, true)?;
406407
if options.verbose {
407408
// For example, if copying file `a/b/c` and its parents
408409
// to directory `d/`, then print
@@ -472,6 +473,8 @@ pub(crate) fn copy_directory(
472473
copied_destinations,
473474
copied_files,
474475
created_parent_dirs,
476+
#[cfg(unix)]
477+
orig_umask,
475478
)?;
476479

477480
// We omit certain permissions when creating directories
@@ -496,6 +499,8 @@ pub(crate) fn copy_directory(
496499
&options.attributes,
497500
false,
498501
options.set_selinux_context,
502+
#[cfg(unix)]
503+
orig_umask,
499504
)?;
500505
continue;
501506
}
@@ -539,6 +544,8 @@ pub(crate) fn copy_directory(
539544
&options.attributes,
540545
false,
541546
options.set_selinux_context,
547+
#[cfg(unix)]
548+
orig_umask,
542549
)?;
543550
}
544551
}
@@ -561,6 +568,8 @@ pub(crate) fn copy_directory(
561568
&options.attributes,
562569
dir.was_created,
563570
options.set_selinux_context,
571+
#[cfg(unix)]
572+
orig_umask,
564573
)?;
565574

566575
#[cfg(all(feature = "selinux", target_os = "linux"))]
@@ -581,6 +590,8 @@ pub(crate) fn copy_directory(
581590
&options.attributes,
582591
false,
583592
options.set_selinux_context,
593+
#[cfg(unix)]
594+
orig_umask,
584595
)?;
585596

586597
#[cfg(all(feature = "selinux", target_os = "linux"))]
@@ -620,59 +631,12 @@ pub fn path_has_prefix(p1: &Path, p2: &Path) -> io::Result<bool> {
620631

621632
/// Builds a directory at the specified path with the given options.
622633
///
623-
/// # Notes
624-
/// - If `copy_attributes_from` is `Some`, the new directory's attributes will be
625-
/// copied from the provided file. Otherwise, the new directory will have the default
626-
/// attributes for the current user.
627-
/// - This method excludes certain permissions if ownership or special mode bits could
628-
/// potentially change. (See `test_dir_perm_race_with_preserve_mode_and_ownership`)
629-
/// - The `recursive` flag determines whether parent directories should be created
630-
/// if they do not already exist.
631-
// we need to allow unused_variable since `options` might be unused in non unix systems
632-
#[allow(unused_variables)]
633-
fn build_dir(
634-
path: &PathBuf,
635-
recursive: bool,
636-
options: &Options,
637-
copy_attributes_from: Option<&Path>,
638-
) -> CopyResult<()> {
634+
/// The `recursive` flag determines whether parent directories should be created if they do not
635+
/// already exist.
636+
#[inline]
637+
fn build_dir(path: &PathBuf, recursive: bool) -> CopyResult<()> {
639638
let mut builder = fs::DirBuilder::new();
640639
builder.recursive(recursive);
641-
642-
// To prevent unauthorized access before the folder is ready,
643-
// exclude certain permissions if ownership or special mode bits
644-
// could potentially change.
645-
#[cfg(unix)]
646-
{
647-
use crate::Preserve;
648-
use std::os::unix::fs::PermissionsExt;
649-
650-
// we need to allow trivial casts here because some systems like linux have u32 constants in
651-
// in libc while others don't.
652-
#[allow(clippy::unnecessary_cast)]
653-
let mut excluded_perms = if matches!(options.attributes.ownership, Preserve::Yes { .. }) {
654-
libc::S_IRWXG | libc::S_IRWXO // exclude rwx for group and other
655-
} else if matches!(options.attributes.mode, Preserve::Yes { .. }) {
656-
libc::S_IWGRP | libc::S_IWOTH //exclude w for group and other
657-
} else {
658-
0
659-
} as u32;
660-
661-
let umask = if let (Some(from), Preserve::Yes { .. }) =
662-
(copy_attributes_from, options.attributes.mode)
663-
{
664-
!fs::symlink_metadata(from)?.permissions().mode()
665-
} else {
666-
uucore::mode::get_umask()
667-
};
668-
669-
excluded_perms |= umask;
670-
// Always keep the owner write bit so we can copy files into the directory.
671-
// The correct final permissions are applied afterward by dirs_needing_permissions.
672-
let mode = (!excluded_perms & 0o777) | 0o200; // mask to permission bits, always keep owner write
673-
std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode);
674-
}
675-
676640
builder.create(path)?;
677641
Ok(())
678642
}

0 commit comments

Comments
 (0)