11use std:: {
22 borrow:: Cow ,
3- fs:: OpenOptions ,
3+ fs:: { OpenOptions , Permissions } ,
44 io:: Write ,
55 path:: { Path , PathBuf } ,
66} ;
@@ -286,9 +286,10 @@ pub(crate) fn finalize_entry(
286286 // For possibly existing, overwritten files, we must change the file mode explicitly.
287287 #[ cfg( unix) ]
288288 if let Some ( path) = set_executable_after_creation {
289- let mut perm = std:: fs:: symlink_metadata ( path) ?. permissions ( ) ;
290- set_mode_executable ( & mut perm) ;
291- std:: fs:: set_permissions ( path, perm) ?;
289+ let old_perm = std:: fs:: symlink_metadata ( path) ?. permissions ( ) ;
290+ if let Some ( new_perm) = set_mode_executable ( old_perm) {
291+ std:: fs:: set_permissions ( path, new_perm) ?;
292+ }
292293 }
293294 // NOTE: we don't call `file.sync_all()` here knowing that some filesystems don't handle this well.
294295 // revisit this once there is a bug to fix.
@@ -298,73 +299,100 @@ pub(crate) fn finalize_entry(
298299}
299300
300301#[ cfg( unix) ]
301- fn set_mode_executable ( perm : & mut std :: fs :: Permissions ) {
302+ fn set_mode_executable ( mut perm : Permissions ) -> Option < Permissions > {
302303 use std:: os:: unix:: fs:: PermissionsExt ;
303304 let mut mode = perm. mode ( ) ;
305+ if mode & 0o170000 != 0o100000 {
306+ return None ; // Stop if we don't have a regular file anymore.
307+ }
304308 mode &= 0o777 ; // Clear non-rwx bits (setuid, setgid, sticky).
305309 mode |= ( mode & 0o444 ) >> 2 ; // Let readers also execute.
306310 perm. set_mode ( mode) ;
311+ Some ( perm)
307312}
308313
309- #[ cfg( test) ]
314+ #[ cfg( all ( test, unix ) ) ]
310315mod tests {
316+ fn pretty ( maybe_mode : Option < u32 > ) -> String {
317+ match maybe_mode {
318+ Some ( mode) => format ! ( "Some({mode:04o})" ) ,
319+ None => "None" . into ( ) ,
320+ }
321+ }
322+
311323 #[ test]
312- #[ cfg( unix) ]
313324 fn set_mode_executable ( ) {
314325 let cases = [
315- // Common cases.
316- ( 0o100755 , 0o755 ) ,
317- ( 0o100644 , 0o755 ) ,
318- ( 0o100750 , 0o750 ) ,
319- ( 0o100640 , 0o750 ) ,
320- ( 0o100700 , 0o700 ) ,
321- ( 0o100600 , 0o700 ) ,
322- ( 0o100775 , 0o775 ) ,
323- ( 0o100664 , 0o775 ) ,
324- ( 0o100770 , 0o770 ) ,
325- ( 0o100660 , 0o770 ) ,
326- ( 0o100764 , 0o775 ) ,
327- ( 0o100760 , 0o770 ) ,
328- // Some less common cases.
329- ( 0o100674 , 0o775 ) ,
330- ( 0o100670 , 0o770 ) ,
331- ( 0o100000 , 0o000 ) ,
332- ( 0o100400 , 0o500 ) ,
333- ( 0o100440 , 0o550 ) ,
334- ( 0o100444 , 0o555 ) ,
335- ( 0o100462 , 0o572 ) ,
336- ( 0o100242 , 0o252 ) ,
337- ( 0o100167 , 0o177 ) ,
338- // Some cases with set-user-ID, set-group-ID, and sticky bits.
339- ( 0o104755 , 0o755 ) ,
340- ( 0o104644 , 0o755 ) ,
341- ( 0o102755 , 0o755 ) ,
342- ( 0o102644 , 0o755 ) ,
343- ( 0o101755 , 0o755 ) ,
344- ( 0o101644 , 0o755 ) ,
345- ( 0o106755 , 0o755 ) ,
346- ( 0o106644 , 0o755 ) ,
347- ( 0o104750 , 0o750 ) ,
348- ( 0o104640 , 0o750 ) ,
349- ( 0o102750 , 0o750 ) ,
350- ( 0o102640 , 0o750 ) ,
351- ( 0o101750 , 0o750 ) ,
352- ( 0o101640 , 0o750 ) ,
353- ( 0o106750 , 0o750 ) ,
354- ( 0o106640 , 0o750 ) ,
355- ( 0o107644 , 0o755 ) ,
356- ( 0o107000 , 0o000 ) ,
357- ( 0o106400 , 0o500 ) ,
358- ( 0o102462 , 0o572 ) ,
326+ // Common cases:
327+ ( 0o100755 , Some ( 0o755 ) ) ,
328+ ( 0o100644 , Some ( 0o755 ) ) ,
329+ ( 0o100750 , Some ( 0o750 ) ) ,
330+ ( 0o100640 , Some ( 0o750 ) ) ,
331+ ( 0o100700 , Some ( 0o700 ) ) ,
332+ ( 0o100600 , Some ( 0o700 ) ) ,
333+ ( 0o100775 , Some ( 0o775 ) ) ,
334+ ( 0o100664 , Some ( 0o775 ) ) ,
335+ ( 0o100770 , Some ( 0o770 ) ) ,
336+ ( 0o100660 , Some ( 0o770 ) ) ,
337+ ( 0o100764 , Some ( 0o775 ) ) ,
338+ ( 0o100760 , Some ( 0o770 ) ) ,
339+ // Less common:
340+ ( 0o100674 , Some ( 0o775 ) ) ,
341+ ( 0o100670 , Some ( 0o770 ) ) ,
342+ ( 0o100000 , Some ( 0o000 ) ) ,
343+ ( 0o100400 , Some ( 0o500 ) ) ,
344+ ( 0o100440 , Some ( 0o550 ) ) ,
345+ ( 0o100444 , Some ( 0o555 ) ) ,
346+ ( 0o100462 , Some ( 0o572 ) ) ,
347+ ( 0o100242 , Some ( 0o252 ) ) ,
348+ ( 0o100167 , Some ( 0o177 ) ) ,
349+ // With set-user-ID, set-group-ID, and sticky bits:
350+ ( 0o104755 , Some ( 0o755 ) ) ,
351+ ( 0o104644 , Some ( 0o755 ) ) ,
352+ ( 0o102755 , Some ( 0o755 ) ) ,
353+ ( 0o102644 , Some ( 0o755 ) ) ,
354+ ( 0o101755 , Some ( 0o755 ) ) ,
355+ ( 0o101644 , Some ( 0o755 ) ) ,
356+ ( 0o106755 , Some ( 0o755 ) ) ,
357+ ( 0o106644 , Some ( 0o755 ) ) ,
358+ ( 0o104750 , Some ( 0o750 ) ) ,
359+ ( 0o104640 , Some ( 0o750 ) ) ,
360+ ( 0o102750 , Some ( 0o750 ) ) ,
361+ ( 0o102640 , Some ( 0o750 ) ) ,
362+ ( 0o101750 , Some ( 0o750 ) ) ,
363+ ( 0o101640 , Some ( 0o750 ) ) ,
364+ ( 0o106750 , Some ( 0o750 ) ) ,
365+ ( 0o106640 , Some ( 0o750 ) ) ,
366+ ( 0o107644 , Some ( 0o755 ) ) ,
367+ ( 0o107000 , Some ( 0o000 ) ) ,
368+ ( 0o106400 , Some ( 0o500 ) ) ,
369+ ( 0o102462 , Some ( 0o572 ) ) ,
370+ // Where it was replaced with a directory due to a race:
371+ ( 0o040755 , None ) ,
372+ ( 0o040644 , None ) ,
373+ ( 0o040600 , None ) ,
374+ ( 0o041755 , None ) ,
375+ ( 0o041644 , None ) ,
376+ ( 0o046644 , None ) ,
377+ // Where it was replaced with a symlink due to a race:
378+ ( 0o120777 , None ) ,
379+ ( 0o120644 , None ) ,
380+ // Where it was replaced with some other non-regular file due to a race:
381+ ( 0o140644 , None ) ,
382+ ( 0o060644 , None ) ,
383+ ( 0o020644 , None ) ,
384+ ( 0o010644 , None ) ,
359385 ] ;
360- for ( old , expected) in cases {
386+ for ( old_mode , expected) in cases {
361387 use std:: os:: unix:: fs:: PermissionsExt ;
362- let mut perm = std:: fs:: Permissions :: from_mode ( old) ;
363- super :: set_mode_executable ( & mut perm) ;
364- let actual = perm. mode ( ) ;
388+ let old_perm = std:: fs:: Permissions :: from_mode ( old_mode) ;
389+ let actual = super :: set_mode_executable ( old_perm) . map ( |perm| perm. mode ( ) ) ;
365390 assert_eq ! (
366- actual, expected,
367- "{old:06o} should become {expected:04o} but became {actual:04o}"
391+ actual,
392+ expected,
393+ "{old_mode:06o} should become {}, became {}" ,
394+ pretty( expected) ,
395+ pretty( actual)
368396 ) ;
369397 }
370398 }
0 commit comments