@@ -115,6 +115,9 @@ pub trait OpenatDirExt {
115
115
/// following the link.
116
116
fn update_timestamps < P : openat:: AsPath > ( & self , path : P ) -> io:: Result < ( ) > ;
117
117
118
+ /// Update permissions for the given path (see `fchmodat(2)`).
119
+ fn set_mode < P : openat:: AsPath > ( & self , path : P , mode : libc:: mode_t ) -> io:: Result < ( ) > ;
120
+
118
121
/// Copy a regular file. The semantics here are intended to match `std::fs::copy()`.
119
122
/// If the target exists, it will be overwritten. The mode bits (permissions) will match, but
120
123
/// owner/group will be derived from the current process. Extended attributes are not
@@ -379,6 +382,27 @@ impl OpenatDirExt for openat::Dir {
379
382
. map_err( map_nix_error) )
380
383
}
381
384
385
+ fn set_mode < P : openat:: AsPath > ( & self , p : P , mode : libc:: mode_t ) -> io:: Result < ( ) > {
386
+ use nix:: sys:: stat:: { fchmodat, FchmodatFlags , Mode } ;
387
+
388
+ let path = p
389
+ . to_path ( )
390
+ . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: Other , "null byte in path" ) ) ?;
391
+ let perms = Mode :: from_bits_truncate ( mode) ;
392
+
393
+ // NOTE(lucab): `AT_SYMLINK_NOFOLLOW` exists but it is not really implemented
394
+ // and short-circuits to `ENOTSUP`.
395
+ fchmodat (
396
+ Some ( self . as_raw_fd ( ) ) ,
397
+ path. as_ref ( ) ,
398
+ perms,
399
+ FchmodatFlags :: FollowSymlink ,
400
+ )
401
+ . map_err ( map_nix_error) ?;
402
+
403
+ Ok ( ( ) )
404
+ }
405
+
382
406
fn new_file_writer < ' a > ( & ' a self , mode : libc:: mode_t ) -> io:: Result < FileWriter > {
383
407
let ( tmpf, name) = if let Some ( tmpf) = self . new_unnamed_file ( mode) . ok ( ) {
384
408
( tmpf, None )
@@ -859,6 +883,15 @@ mod tests {
859
883
}
860
884
}
861
885
886
+ #[ test]
887
+ fn test_syncfs ( ) {
888
+ let td = tempfile:: tempdir ( ) . unwrap ( ) ;
889
+ let d = openat:: Dir :: open ( td. path ( ) ) . unwrap ( ) ;
890
+ d. ensure_dir_all ( "foo/bar" , 0o755 ) . unwrap ( ) ;
891
+ d. syncfs ( ) . unwrap ( ) ;
892
+ assert_eq ! ( d. exists( "foo/bar" ) . unwrap( ) , true ) ;
893
+ }
894
+
862
895
#[ test]
863
896
fn test_update_timestamps ( ) {
864
897
let td = tempfile:: tempdir ( ) . unwrap ( ) ;
@@ -881,6 +914,25 @@ mod tests {
881
914
) ;
882
915
}
883
916
917
+ #[ test]
918
+ fn test_fchmodat ( ) {
919
+ let td = tempfile:: tempdir ( ) . unwrap ( ) ;
920
+ let d = openat:: Dir :: open ( td. path ( ) ) . unwrap ( ) ;
921
+ d. ensure_dir ( "foo" , 0o777 ) . unwrap ( ) ;
922
+ d. set_mode ( "foo" , 0o750 ) . unwrap ( ) ;
923
+ d. syncfs ( ) . unwrap ( ) ;
924
+ assert_eq ! (
925
+ d. metadata( "foo" ) . unwrap( ) . stat( ) . st_mode & !libc:: S_IFMT ,
926
+ 0o750
927
+ ) ;
928
+ d. set_mode ( "foo" , 0o700 ) . unwrap ( ) ;
929
+ d. syncfs ( ) . unwrap ( ) ;
930
+ assert_eq ! (
931
+ d. metadata( "foo" ) . unwrap( ) . stat( ) . st_mode & !libc:: S_IFMT ,
932
+ 0o700
933
+ ) ;
934
+ }
935
+
884
936
fn find_test_file ( tempdir : & Path ) -> Result < PathBuf > {
885
937
for p in [ "/proc/self/exe" , "/usr/bin/bash" ] . iter ( ) {
886
938
let p = Path :: new ( p) ;
@@ -893,15 +945,6 @@ mod tests {
893
945
Ok ( fallback)
894
946
}
895
947
896
- #[ test]
897
- fn test_syncfs ( ) {
898
- let td = tempfile:: tempdir ( ) . unwrap ( ) ;
899
- let d = openat:: Dir :: open ( td. path ( ) ) . unwrap ( ) ;
900
- d. ensure_dir_all ( "foo/bar" , 0o755 ) . unwrap ( ) ;
901
- d. syncfs ( ) . unwrap ( ) ;
902
- assert_eq ! ( d. exists( "foo/bar" ) . unwrap( ) , true ) ;
903
- }
904
-
905
948
#[ test]
906
949
fn copy_fallback ( ) -> Result < ( ) > {
907
950
use std:: io:: Read ;
0 commit comments