Skip to content

Commit 83c9df8

Browse files
committed
WIP
1 parent cc46be9 commit 83c9df8

File tree

4 files changed

+47
-27
lines changed

4 files changed

+47
-27
lines changed

src/uu/cp/src/copydir.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ impl<'a> Context<'a> {
166166
/// }
167167
/// ];
168168
/// ```
169+
#[derive(Debug)]
169170
struct Entry {
170171
/// The absolute path to file or directory to copy.
171172
source_absolute: PathBuf,
@@ -178,6 +179,9 @@ struct Entry {
178179

179180
/// Whether the destination is a file.
180181
target_is_file: bool,
182+
183+
/// Whether we created the destination dir
184+
target_is_created: bool,
181185
}
182186

183187
impl Entry {
@@ -231,6 +235,7 @@ impl Entry {
231235
source_relative,
232236
local_to_target,
233237
target_is_file,
238+
target_is_created: false,
234239
})
235240
}
236241
}
@@ -239,7 +244,7 @@ impl Entry {
239244
/// Copy a single entry during a directory traversal.
240245
fn copy_direntry(
241246
progress_bar: Option<&ProgressBar>,
242-
entry: &Entry,
247+
entry: &mut Entry,
243248
entry_is_symlink: bool,
244249
entry_is_dir_no_follow: bool,
245250
options: &Options,
@@ -270,6 +275,7 @@ fn copy_direntry(
270275
options,
271276
Some(&entry.source_absolute),
272277
)?;
278+
entry.target_is_created = true;
273279
if options.verbose {
274280
println!(
275281
"{}",
@@ -421,7 +427,7 @@ pub(crate) fn copy_directory(
421427
let mut last_iter: Option<DirEntry> = None;
422428

423429
// Keep track of all directories we've created that need permission fixes
424-
let mut dirs_needing_permissions: Vec<(PathBuf, PathBuf)> = Vec::new();
430+
let mut dirs_needing_permissions: Vec<(PathBuf, PathBuf, bool)> = Vec::new();
425431

426432
// Traverse the contents of the directory, copying each one.
427433
for direntry_result in WalkDir::new(root)
@@ -440,11 +446,11 @@ pub(crate) fn copy_directory(
440446
}
441447
Err(_) => (direntry_type.is_symlink(), direntry_type.is_dir()),
442448
};
443-
let entry = Entry::new(&context, direntry_path, options.no_target_dir)?;
449+
let mut entry = Entry::new(&context, direntry_path, options.no_target_dir)?;
444450

445451
copy_direntry(
446452
progress_bar,
447-
&entry,
453+
&mut entry,
448454
entry_is_symlink,
449455
entry_is_dir_no_follow,
450456
options,
@@ -470,8 +476,11 @@ pub(crate) fn copy_directory(
470476
entry_is_dir_no_follow || (options.dereference && direntry_path.is_dir());
471477
if is_dir_for_permissions {
472478
// Add this directory to our list for permission fixing later
473-
dirs_needing_permissions
474-
.push((entry.source_absolute.clone(), entry.local_to_target.clone()));
479+
dirs_needing_permissions.push((
480+
entry.source_absolute.clone(),
481+
entry.local_to_target.clone(),
482+
entry.target_is_created,
483+
));
475484

476485
// If true, last_iter is not a parent of this iter.
477486
// The means we just exited a directory.
@@ -504,6 +513,7 @@ pub(crate) fn copy_directory(
504513
&entry.source_absolute,
505514
&entry.local_to_target,
506515
options,
516+
entry.target_is_created,
507517
)?;
508518
}
509519
}
@@ -519,8 +529,8 @@ pub(crate) fn copy_directory(
519529

520530
// Fix permissions for all directories we created
521531
// This ensures that even sibling directories get their permissions fixed
522-
for (source_path, dest_path) in dirs_needing_permissions {
523-
copy_attributes(&source_path, &dest_path, options)?;
532+
for (source_path, dest_path, is_dir_created) in dirs_needing_permissions {
533+
copy_attributes(&source_path, &dest_path, options, is_dir_created)?;
524534
}
525535

526536
// Also fix permissions for parent directories,
@@ -529,7 +539,7 @@ pub(crate) fn copy_directory(
529539
let dest = target.join(root.file_name().unwrap());
530540
for (x, y) in aligned_ancestors(root, dest.as_path()) {
531541
if let Ok(src) = canonicalize(x, MissingHandling::Normal, ResolveMode::Physical) {
532-
copy_attributes(&src, y, options)?;
542+
copy_attributes(&src, y, options, false)?;
533543
}
534544
}
535545
}

src/uu/cp/src/cp.rs

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ pub enum SparseMode {
194194
}
195195

196196
/// The expected file type of copy target
197-
#[derive(Copy, Clone)]
197+
#[derive(Copy, Clone, Debug)]
198198
pub enum TargetType {
199199
Directory,
200200
File,
@@ -1506,7 +1506,7 @@ fn copy_source(
15061506
if options.parents {
15071507
for (x, y) in aligned_ancestors(source, dest.as_path()) {
15081508
if let Ok(src) = canonicalize(x, MissingHandling::Normal, ResolveMode::Physical) {
1509-
copy_attributes(&src, y, options)?;
1509+
copy_attributes(&src, y, options, false)?;
15101510
}
15111511
}
15121512
}
@@ -1655,11 +1655,22 @@ fn copy_extended_attrs(source: &Path, dest: &Path) -> CopyResult<()> {
16551655
}
16561656

16571657
/// Copy the specified attributes from one path to another.
1658-
pub(crate) fn copy_attributes(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
1658+
pub(crate) fn copy_attributes(
1659+
source: &Path,
1660+
dest: &Path,
1661+
options: &Options,
1662+
is_dir_created: bool,
1663+
) -> CopyResult<()> {
16591664
let context = &*format!("{} -> {}", source.quote(), dest.quote());
16601665
let source_metadata =
16611666
fs::symlink_metadata(source).map_err(|e| CpError::IoErrContext(e, context.to_owned()))?;
16621667

1668+
let mode = if dest.is_dir() && is_dir_created {
1669+
Preserve::Yes { required: false }
1670+
} else {
1671+
options.attributes.mode
1672+
};
1673+
16631674
// Ownership must be changed first to avoid interfering with mode change.
16641675
#[cfg(unix)]
16651676
handle_preserve(&options.attributes.ownership, || -> CopyResult<()> {
@@ -1697,22 +1708,22 @@ pub(crate) fn copy_attributes(source: &Path, dest: &Path, options: &Options) ->
16971708
Ok(())
16981709
})?;
16991710

1700-
handle_preserve(&options.attributes.mode, || -> CopyResult<()> {
1711+
handle_preserve(&mode, || -> CopyResult<()> {
17011712
// The `chmod()` system call that underlies the
17021713
// `fs::set_permissions()` call is unable to change the
17031714
// permissions of a symbolic link. In that case, we just
17041715
// do nothing, since every symbolic link has the same
17051716
// permissions.
17061717
if !dest.is_symlink() {
1707-
// let dest_permissions = calculate_dest_permissions(
1708-
// fs::metadata(dest).ok().as_ref(),
1709-
// dest,
1710-
// &source_metadata,
1711-
// options,
1712-
// context,
1713-
// )?;
1714-
let dest_permissions = source_metadata.permissions();
1715-
fs::set_permissions(dest, dest_permissions)
1718+
let mut perms = source_metadata.permissions();
1719+
if is_dir_created {
1720+
let mode = handle_no_preserve_mode(options, perms.mode());
1721+
use uucore::mode::get_umask;
1722+
let mode = mode & !get_umask();
1723+
perms.set_mode(mode);
1724+
}
1725+
1726+
fs::set_permissions(dest, perms)
17161727
.map_err(|e| CpError::IoErrContext(e, context.to_owned()))?;
17171728
// FIXME: Implement this for windows as well
17181729
#[cfg(feature = "feat_acl")]
@@ -2516,7 +2527,7 @@ fn copy_file(
25162527
if options.dereference(source_in_command_line) {
25172528
if let Ok(src) = canonicalize(source, MissingHandling::Normal, ResolveMode::Physical) {
25182529
if src.exists() {
2519-
copy_attributes(&src, dest, options)?;
2530+
copy_attributes(&src, dest, options, false)?;
25202531
}
25212532
}
25222533
} else if source_is_stream && !source.exists() {
@@ -2525,7 +2536,7 @@ fn copy_file(
25252536
// attributes. However, this is already handled in the stream
25262537
// copy function (see `copy_stream` under platform/linux.rs).
25272538
} else {
2528-
copy_attributes(source, dest, options)?;
2539+
copy_attributes(source, dest, options, false)?;
25292540
}
25302541

25312542
#[cfg(feature = "selinux")]
@@ -2701,7 +2712,7 @@ fn copy_link(
27012712
delete_path(dest, options)?;
27022713
}
27032714
symlink_file(&link, dest, symlinked_files)?;
2704-
copy_attributes(source, dest, options)
2715+
copy_attributes(source, dest, options, false)
27052716
}
27062717

27072718
/// Generate an error message if `target` is not the correct `target_type`

src/uucore/src/lib/features/fs.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,6 @@ pub fn display_permissions_unix(mode: mode_t, display_file_type: bool) -> String
542542
/// std::fs::create_dir("foo/."); fails in pure Rust
543543
pub fn dir_strip_dot_for_creation(path: &Path) -> PathBuf {
544544
let path_str = path.to_string_lossy();
545-
546545
if path_str.ends_with("/.") || path_str.ends_with("/./") {
547546
// Do a simple dance to strip the "/."
548547
Path::new(&path).components().collect()

tests/by-util/test_cp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use std::os::unix::fs::{FileTypeExt, MetadataExt};
2525
use std::os::windows::fs::symlink_file;
2626
#[cfg(not(windows))]
2727
use std::path::Path;
28-
#[cfg(target_os = "linux")]
28+
2929
use std::path::PathBuf;
3030

3131
#[cfg(any(target_os = "linux", target_os = "android"))]

0 commit comments

Comments
 (0)