2
2
//
3
3
// For the full copyright and license information, please view the LICENSE
4
4
// file that was distributed with this source code.
5
- // spell-checker:ignore TODO canonicalizes direntry pathbuf symlinked
5
+ // spell-checker:ignore TODO canonicalizes direntry pathbuf symlinked IRWXO IRWXG
6
6
//! Recursively copy the contents of a directory.
7
7
//!
8
8
//! See the [`copy_directory`] function for more information.
@@ -27,7 +27,7 @@ use walkdir::{DirEntry, WalkDir};
27
27
28
28
use crate :: {
29
29
aligned_ancestors, context_for, copy_attributes, copy_file, copy_link, CopyResult , Error ,
30
- Options ,
30
+ Options , Preserve ,
31
31
} ;
32
32
33
33
/// Ensure a Windows path starts with a `\\?`.
@@ -246,12 +246,7 @@ fn copy_direntry(
246
246
if target_is_file {
247
247
return Err ( "cannot overwrite non-directory with directory" . into ( ) ) ;
248
248
} else {
249
- // TODO Since the calling code is traversing from the root
250
- // of the directory structure, I don't think
251
- // `create_dir_all()` will have any benefit over
252
- // `create_dir()`, since all the ancestor directories
253
- // should have already been created.
254
- fs:: create_dir_all ( & local_to_target) ?;
249
+ build_dir ( options, & local_to_target, false ) ?;
255
250
if options. verbose {
256
251
println ! ( "{}" , context_for( & source_relative, & local_to_target) ) ;
257
252
}
@@ -376,8 +371,7 @@ pub(crate) fn copy_directory(
376
371
let tmp = if options. parents {
377
372
if let Some ( parent) = root. parent ( ) {
378
373
let new_target = target. join ( parent) ;
379
- std:: fs:: create_dir_all ( & new_target) ?;
380
-
374
+ build_dir ( options, & new_target, true ) ?;
381
375
if options. verbose {
382
376
// For example, if copying file `a/b/c` and its parents
383
377
// to directory `d/`, then print
@@ -470,6 +464,39 @@ pub fn path_has_prefix(p1: &Path, p2: &Path) -> io::Result<bool> {
470
464
Ok ( pathbuf1. starts_with ( pathbuf2) )
471
465
}
472
466
467
+ /// Builds a directory at the specified path with the given options.
468
+ ///
469
+ /// # Notes
470
+ /// - It excludes certain permissions if ownership or special mode bits could
471
+ /// potentially change.
472
+ /// - The `recursive` flag determines whether parent directories should be created
473
+ /// if they do not already exist.
474
+ fn build_dir ( options : & Options , path : & PathBuf , recursive : bool ) -> CopyResult < ( ) > {
475
+ let mut builder = fs:: DirBuilder :: new ( ) ;
476
+ builder. recursive ( recursive) ;
477
+ // To prevent unauthorized access before the folder is ready,
478
+ // exclude certain permissions if ownership or special mode bits
479
+ // could potentially change.
480
+ #[ cfg( unix) ]
481
+ {
482
+ // we need to allow trivial casts here because some systems like linux have u32 constants in
483
+ // in libc while others don't.
484
+ #[ allow( clippy:: unnecessary_cast) ]
485
+ let mut excluded_perms = if matches ! ( options. attributes. ownership, Preserve :: Yes { .. } ) {
486
+ libc:: S_IRWXG | libc:: S_IRWXO // exclude rwx for group and other
487
+ } else if matches ! ( options. attributes. mode, Preserve :: Yes { .. } ) {
488
+ libc:: S_IWGRP | libc:: S_IWOTH //exclude w for group and other
489
+ } else {
490
+ 0
491
+ } as u32 ;
492
+ excluded_perms |= uucore:: mode:: get_umask ( ) ;
493
+ let mode = !excluded_perms & 0o777 ; //use only the last three octet bits
494
+ std:: os:: unix:: fs:: DirBuilderExt :: mode ( & mut builder, mode) ;
495
+ }
496
+ builder. create ( path) ?;
497
+ Ok ( ( ) )
498
+ }
499
+
473
500
#[ cfg( test) ]
474
501
mod tests {
475
502
use super :: ends_with_slash_dot;
0 commit comments