1- use std:: borrow:: Cow ;
21use std:: {
2+ borrow:: Cow ,
33 fs:: OpenOptions ,
44 io:: Write ,
55 path:: { Path , PathBuf } ,
@@ -286,12 +286,8 @@ 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- use std:: os:: unix:: fs:: PermissionsExt ;
290289 let mut perm = std:: fs:: symlink_metadata ( path) ?. permissions ( ) ;
291- let mut mode = perm. mode ( ) ;
292- mode &= 0o777 ; // Clear non-rwx bits (setuid, setgid, sticky).
293- mode |= ( mode & 0o444 ) >> 2 ; // Let readers also execute.
294- perm. set_mode ( mode) ;
290+ set_mode_executable ( & mut perm) ;
295291 std:: fs:: set_permissions ( path, perm) ?;
296292 }
297293 // NOTE: we don't call `file.sync_all()` here knowing that some filesystems don't handle this well.
@@ -300,3 +296,76 @@ pub(crate) fn finalize_entry(
300296 file. close ( ) ?;
301297 Ok ( ( ) )
302298}
299+
300+ #[ cfg( unix) ]
301+ fn set_mode_executable ( perm : & mut std:: fs:: Permissions ) {
302+ use std:: os:: unix:: fs:: PermissionsExt ;
303+ let mut mode = perm. mode ( ) ;
304+ mode &= 0o777 ; // Clear non-rwx bits (setuid, setgid, sticky).
305+ mode |= ( mode & 0o444 ) >> 2 ; // Let readers also execute.
306+ perm. set_mode ( mode) ;
307+ }
308+
309+ #[ cfg( test) ]
310+ mod tests {
311+ #[ test]
312+ #[ cfg( unix) ]
313+ fn set_mode_executable ( ) {
314+ 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 ) ,
359+ ] ;
360+ for ( old, expected) in cases {
361+ 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 ( ) ;
365+ assert_eq ! (
366+ actual, expected,
367+ "{old:06o} should become {expected:04o} but became {actual:04o}"
368+ ) ;
369+ }
370+ }
371+ }
0 commit comments