Skip to content

Commit

Permalink
cp: copy attributes after making subdir
Browse files Browse the repository at this point in the history
  • Loading branch information
rm-dr committed Nov 24, 2024
1 parent 4bc9e7b commit 8e98620
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 10 deletions.
48 changes: 39 additions & 9 deletions src/uu/cp/src/copydir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::collections::{HashMap, HashSet};
use std::env;
use std::fs;
use std::io;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf, StripPrefixError};

use indicatif::ProgressBar;
Expand All @@ -27,7 +28,7 @@ use walkdir::{DirEntry, WalkDir};

use crate::{
aligned_ancestors, context_for, copy_attributes, copy_file, copy_link, CopyResult, Error,
Options,
Options, Preserve,
};

/// Ensure a Windows path starts with a `\\?`.
Expand Down Expand Up @@ -246,7 +247,7 @@ fn copy_direntry(
if target_is_file {
return Err("cannot overwrite non-directory with directory".into());
} else {
build_dir(options, &local_to_target, false)?;
build_dir(&local_to_target, false, options, Some(&source_absolute))?;
if options.verbose {
println!("{}", context_for(&source_relative, &local_to_target));
}
Expand Down Expand Up @@ -371,7 +372,7 @@ pub(crate) fn copy_directory(
let tmp = if options.parents {
if let Some(parent) = root.parent() {
let new_target = target.join(parent);
build_dir(options, &new_target, true)?;
build_dir(&new_target, true, options, None)?;
if options.verbose {
// For example, if copying file `a/b/c` and its parents
// to directory `d/`, then print
Expand Down Expand Up @@ -469,15 +470,22 @@ pub fn path_has_prefix(p1: &Path, p2: &Path) -> io::Result<bool> {
/// Builds a directory at the specified path with the given options.
///
/// # Notes
/// - It excludes certain permissions if ownership or special mode bits could
/// potentially change.
/// - If `copy_attributes_from` is `Some`, the new directory's attributes will be
/// copied from the provided file. Otherwise, the new directory will have the default
/// attributes for the current user.
/// - This method excludes certain permissions if ownership or special mode bits could
/// potentially change. (See `test_dir_perm_race_with_preserve_mode_and_ownership``)
/// - The `recursive` flag determines whether parent directories should be created
/// if they do not already exist.
// we need to allow unused_variable since `options` might be unused in non unix systems
#[allow(unused_variables)]
fn build_dir(options: &Options, path: &PathBuf, recursive: bool) -> CopyResult<()> {
fn build_dir(
path: &PathBuf,
recursive: bool,
options: &Options,
copy_attributes_from: Option<&Path>,
) -> CopyResult<()> {
let mut builder = fs::DirBuilder::new();
builder.recursive(recursive);

// To prevent unauthorized access before the folder is ready,
// exclude certain permissions if ownership or special mode bits
// could potentially change.
Expand All @@ -494,11 +502,33 @@ fn build_dir(options: &Options, path: &PathBuf, recursive: bool) -> CopyResult<(
} else {
0
} as u32;
excluded_perms |= uucore::mode::get_umask();

let umask = if copy_attributes_from.is_some()
&& matches!(options.attributes.mode, Preserve::Yes { .. })
{
!fs::symlink_metadata(copy_attributes_from.unwrap())?
.permissions()
.mode()
} else {
uucore::mode::get_umask()
};

excluded_perms |= umask;
let mode = !excluded_perms & 0o777; //use only the last three octet bits
std::os::unix::fs::DirBuilderExt::mode(&mut builder, mode);
}

builder.create(path)?;

if let Some(f) = copy_attributes_from {
// We intentionally exclude certain permissions above,
// do not overwrite them.
// All other attributes should be copied, though.
let mut filtered_permissions = options.attributes.clone();

Check failure on line 527 in src/uu/cp/src/copydir.rs

View workflow job for this annotation

GitHub Actions / Style and Lint (ubuntu-24.04, unix)

ERROR: `cargo clippy`: using `clone` on type `Attributes` which implements the `Copy` trait (file:'src/uu/cp/src/copydir.rs', line:527)
filtered_permissions.mode = Preserve::No { explicit: true };
copy_attributes(f, path, &filtered_permissions)?;
}

Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion src/uu/cp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ pub enum CopyMode {
/// For full compatibility with GNU, these options should also combine. We
/// currently only do a best effort imitation of that behavior, because it is
/// difficult to achieve in clap, especially with `--no-preserve`.
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Attributes {
#[cfg(unix)]
pub ownership: Preserve,
Expand Down

0 comments on commit 8e98620

Please sign in to comment.