@@ -116,6 +116,8 @@ pub trait OpenatDirExt {
116
116
fn update_timestamps < P : openat:: AsPath > ( & self , path : P ) -> io:: Result < ( ) > ;
117
117
118
118
/// Update permissions for the given path (see `fchmodat(2)`).
119
+ ///
120
+ /// If the entry at `path` is a symlink, no action is performed.
119
121
fn set_mode < P : openat:: AsPath > ( & self , path : P , mode : libc:: mode_t ) -> io:: Result < ( ) > ;
120
122
121
123
/// Copy a regular file. The semantics here are intended to match `std::fs::copy()`.
@@ -384,14 +386,23 @@ impl OpenatDirExt for openat::Dir {
384
386
385
387
fn set_mode < P : openat:: AsPath > ( & self , p : P , mode : libc:: mode_t ) -> io:: Result < ( ) > {
386
388
use nix:: sys:: stat:: { fchmodat, FchmodatFlags , Mode } ;
389
+ use openat:: SimpleType ;
387
390
388
391
let path = p
389
392
. to_path ( )
390
393
. ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: Other , "null byte in path" ) ) ?;
391
- let perms = Mode :: from_bits_truncate ( mode) ;
392
394
393
- // NOTE(lucab): `AT_SYMLINK_NOFOLLOW` exists but it is not really implemented
394
- // and short-circuits to `ENOTSUP`.
395
+ {
396
+ // NOTE(lucab): `AT_SYMLINK_NOFOLLOW` used to short-circuit to `ENOTSUP`
397
+ // in older glibc versions, so we don't use it. Instead we try to detect
398
+ // any symlink, and skip it.
399
+ let entry_meta = self . metadata ( path. as_ref ( ) ) ?;
400
+ if entry_meta. simple_type ( ) == SimpleType :: Symlink {
401
+ return Ok ( ( ) ) ;
402
+ } ;
403
+ }
404
+
405
+ let perms = Mode :: from_bits_truncate ( mode) ;
395
406
fchmodat (
396
407
Some ( self . as_raw_fd ( ) ) ,
397
408
path. as_ref ( ) ,
@@ -931,6 +942,18 @@ mod tests {
931
942
d. metadata( "foo" ) . unwrap( ) . stat( ) . st_mode & !libc:: S_IFMT ,
932
943
0o700
933
944
) ;
945
+
946
+ d. symlink ( "bar" , "foo" ) . unwrap ( ) ;
947
+ d. set_mode ( "bar" , 0o000 ) . unwrap ( ) ;
948
+ d. syncfs ( ) . unwrap ( ) ;
949
+ assert_ne ! (
950
+ d. metadata( "bar" ) . unwrap( ) . stat( ) . st_mode & !libc:: S_IFMT ,
951
+ 0o000
952
+ ) ;
953
+ assert_ne ! (
954
+ d. metadata( "foo" ) . unwrap( ) . stat( ) . st_mode & !libc:: S_IFMT ,
955
+ 0o000
956
+ ) ;
934
957
}
935
958
936
959
fn find_test_file ( tempdir : & Path ) -> Result < PathBuf > {
0 commit comments