@@ -646,7 +646,7 @@ fn standard(mut paths: Vec<OsString>, b: &Behavior) -> UResult<()> {
646646 && sources. len ( ) == 1
647647 && !is_potential_directory_path ( & target)
648648 {
649- if let Ok ( dir_fd) = DirFd :: open_no_follow ( to_create) {
649+ if let Ok ( dir_fd) = DirFd :: open ( to_create, false ) {
650650 if let Some ( filename) = target. file_name ( ) {
651651 target_parent_fd = Some ( dir_fd) ;
652652 target_filename = Some ( filename. to_os_string ( ) ) ;
@@ -672,6 +672,8 @@ fn standard(mut paths: Vec<OsString>, b: &Behavior) -> UResult<()> {
672672
673673 #[ cfg( unix) ]
674674 {
675+ // Use DEFAULT_MODE (0o755) for created directories - this matches GNU install
676+ // behavior. The actual mode will be modified by umask at the kernel level.
675677 match create_dir_all_safe ( to_create, DEFAULT_MODE ) {
676678 Ok ( dir_fd) => {
677679 if b. target_dir . is_none ( )
@@ -768,47 +770,7 @@ fn standard(mut paths: Vec<OsString>, b: &Behavior) -> UResult<()> {
768770
769771 copy_file_safe ( source, parent_fd, filename. as_os_str ( ) ) ?;
770772
771- #[ cfg( not( windows) ) ]
772- if b. strip {
773- strip_file ( & target, b) ?;
774- }
775-
776- set_ownership_and_permissions ( & target, b) ?;
777-
778- if b. preserve_timestamps {
779- preserve_timestamps ( source, & target) ?;
780- }
781-
782- #[ cfg( all( feature = "selinux" , target_os = "linux" ) ) ]
783- if !b. unprivileged {
784- if b. preserve_context {
785- uucore:: selinux:: preserve_security_context ( source, & target)
786- . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
787- } else if b. default_context {
788- set_selinux_default_context ( & target)
789- . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
790- } else if b. context . is_some ( ) {
791- let context = get_context_for_selinux ( b) ;
792- set_selinux_security_context ( & target, context)
793- . map_err ( |e| InstallError :: SelinuxContextFailed ( e. to_string ( ) ) ) ?;
794- }
795- }
796-
797- if b. verbose {
798- print ! (
799- "{}" ,
800- translate!( "install-verbose-copy" , "from" => source. quote( ) , "to" => target. quote( ) )
801- ) ;
802- match backup_path {
803- Some ( path) => println ! (
804- " {}" ,
805- translate!( "install-verbose-backup" , "backup" => path. quote( ) )
806- ) ,
807- None => println ! ( ) ,
808- }
809- }
810-
811- Ok ( ( ) )
773+ finalize_installed_file ( source, & target, b, backup_path)
812774 } else {
813775 copy ( source, & target, b)
814776 }
@@ -964,8 +926,7 @@ fn copy_file_safe(from: &Path, to_parent_fd: &DirFd, to_filename: &std::ffi::OsS
964926
965927 let mut src = File :: open ( from) ?;
966928 let mut dst = to_parent_fd. open_file_at ( to_filename) ?;
967- std:: io:: copy ( & mut src, & mut dst) ?;
968- dst. sync_all ( ) ?;
929+ copy_stream ( & mut src, & mut dst) ?;
969930
970931 Ok ( ( ) )
971932}
@@ -997,9 +958,7 @@ fn copy_file(from: &Path, to: &Path) -> UResult<()> {
997958 . into ( ) ) ;
998959 }
999960
1000- // Remove existing file at destination to allow overwriting
1001- // Note: create_new() below provides TOCTOU protection; if something
1002- // appears at this path between the remove and create, it will fail safely
961+ // Remove existing file (create_new below provides TOCTOU protection)
1003962 if let Err ( e) = fs:: remove_file ( to) {
1004963 if e. kind ( ) != std:: io:: ErrorKind :: NotFound {
1005964 show_error ! (
@@ -1010,7 +969,6 @@ fn copy_file(from: &Path, to: &Path) -> UResult<()> {
1010969 }
1011970
1012971 let mut handle = File :: open ( from) ?;
1013- // create_new provides TOCTOU protection
1014972 let mut dest = OpenOptions :: new ( ) . write ( true ) . create_new ( true ) . open ( to) ?;
1015973
1016974 copy_stream ( & mut handle, & mut dest) . map_err ( |err| {
@@ -1113,28 +1071,13 @@ fn preserve_timestamps(from: &Path, to: &Path) -> UResult<()> {
11131071 Ok ( ( ) )
11141072}
11151073
1116- /// Copy one file to a new location, changing metadata.
1117- ///
1118- /// Returns a Result type with the Err variant containing the error message.
1119- ///
1120- /// # Parameters
1121- ///
1122- /// _from_ must exist as a non-directory.
1123- /// _to_ must be a non-existent file, whose parent directory exists.
1124- ///
1125- /// # Errors
1126- ///
1127- /// If the copy system call fails, we print a verbose error and return an empty error value.
1128- ///
1129- fn copy ( from : & Path , to : & Path , b : & Behavior ) -> UResult < ( ) > {
1130- if b. compare && !need_copy ( from, to, b) {
1131- return Ok ( ( ) ) ;
1132- }
1133- // Declare the path here as we may need it for the verbose output below.
1134- let backup_path = perform_backup ( to, b) ?;
1135-
1136- copy_file ( from, to) ?;
1137-
1074+ /// Apply post-copy operations: strip, ownership, permissions, timestamps, SELinux, and verbose output.
1075+ fn finalize_installed_file (
1076+ from : & Path ,
1077+ to : & Path ,
1078+ b : & Behavior ,
1079+ backup_path : Option < PathBuf > ,
1080+ ) -> UResult < ( ) > {
11381081 #[ cfg( not( windows) ) ]
11391082 if b. strip {
11401083 strip_file ( to, b) ?;
@@ -1178,6 +1121,31 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
11781121 Ok ( ( ) )
11791122}
11801123
1124+ /// Copy one file to a new location, changing metadata.
1125+ ///
1126+ /// Returns a Result type with the Err variant containing the error message.
1127+ ///
1128+ /// # Parameters
1129+ ///
1130+ /// _from_ must exist as a non-directory.
1131+ /// _to_ must be a non-existent file, whose parent directory exists.
1132+ ///
1133+ /// # Errors
1134+ ///
1135+ /// If the copy system call fails, we print a verbose error and return an empty error value.
1136+ ///
1137+ fn copy ( from : & Path , to : & Path , b : & Behavior ) -> UResult < ( ) > {
1138+ if b. compare && !need_copy ( from, to, b) {
1139+ return Ok ( ( ) ) ;
1140+ }
1141+ // Declare the path here as we may need it for the verbose output below.
1142+ let backup_path = perform_backup ( to, b) ?;
1143+
1144+ copy_file ( from, to) ?;
1145+
1146+ finalize_installed_file ( from, to, b, backup_path)
1147+ }
1148+
11811149#[ cfg( all( feature = "selinux" , target_os = "linux" ) ) ]
11821150fn get_context_for_selinux ( b : & Behavior ) -> Option < & String > {
11831151 if b. default_context {
0 commit comments