2
2
3
3
use errno:: { self , Errno } ;
4
4
use { Error , Result , NixPath } ;
5
- use fcntl:: { fcntl, FdFlag , OFlag } ;
5
+ use fcntl:: { AtFlags , at_rawfd , fcntl, FdFlag , OFlag } ;
6
6
use fcntl:: FcntlArg :: F_SETFD ;
7
7
use libc:: { self , c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t,
8
8
uid_t, gid_t, mode_t} ;
@@ -557,6 +557,16 @@ pub fn getcwd() -> Result<PathBuf> {
557
557
}
558
558
}
559
559
560
+ /// Computes the raw UID and GID values to pass to a `*chown` call.
561
+ fn chown_raw_ids ( owner : Option < Uid > , group : Option < Gid > ) -> ( libc:: uid_t , libc:: gid_t ) {
562
+ // According to the POSIX specification, -1 is used to indicate that owner and group
563
+ // are not to be changed. Since uid_t and gid_t are unsigned types, we have to wrap
564
+ // around to get -1.
565
+ let uid = owner. map ( Into :: into) . unwrap_or ( ( 0 as uid_t ) . wrapping_sub ( 1 ) ) ;
566
+ let gid = group. map ( Into :: into) . unwrap_or ( ( 0 as gid_t ) . wrapping_sub ( 1 ) ) ;
567
+ ( uid, gid)
568
+ }
569
+
560
570
/// Change the ownership of the file at `path` to be owned by the specified
561
571
/// `owner` (user) and `group` (see
562
572
/// [chown(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html)).
@@ -567,17 +577,62 @@ pub fn getcwd() -> Result<PathBuf> {
567
577
#[ inline]
568
578
pub fn chown < P : ?Sized + NixPath > ( path : & P , owner : Option < Uid > , group : Option < Gid > ) -> Result < ( ) > {
569
579
let res = try!( path. with_nix_path ( |cstr| {
570
- // According to the POSIX specification, -1 is used to indicate that
571
- // owner and group, respectively, are not to be changed. Since uid_t and
572
- // gid_t are unsigned types, we use wrapping_sub to get '-1'.
573
- unsafe { libc:: chown ( cstr. as_ptr ( ) ,
574
- owner. map ( Into :: into) . unwrap_or ( ( 0 as uid_t ) . wrapping_sub ( 1 ) ) ,
575
- group. map ( Into :: into) . unwrap_or ( ( 0 as gid_t ) . wrapping_sub ( 1 ) ) ) }
580
+ let ( uid, gid) = chown_raw_ids ( owner, group) ;
581
+ unsafe { libc:: chown ( cstr. as_ptr ( ) , uid, gid) }
576
582
} ) ) ;
577
583
578
584
Errno :: result ( res) . map ( drop)
579
585
}
580
586
587
+ /// Flags for `fchownat` function.
588
+ #[ derive( Clone , Copy , Debug ) ]
589
+ pub enum FchownatFlags {
590
+ FollowSymlink ,
591
+ NoFollowSymlink ,
592
+ }
593
+
594
+ /// Change the ownership of the file at `path` to be owned by the specified
595
+ /// `owner` (user) and `group`.
596
+ ///
597
+ /// The owner/group for the provided path name will not be modified if `None` is
598
+ /// provided for that argument. Ownership change will be attempted for the path
599
+ /// only if `Some` owner/group is provided.
600
+ ///
601
+ /// The file to be changed is determined relative to the directory associated
602
+ /// with the file descriptor `dirfd` or the current working directory
603
+ /// if `dirfd` is `None`.
604
+ ///
605
+ /// If `flag` is `FchownatFlags::NoFollowSymlink` and `path` names a symbolic link,
606
+ /// then the mode of the symbolic link is changed.
607
+ ///
608
+ /// `fchownat(None, path, mode, FchownatFlags::NoFollowSymlink)` is identical to
609
+ /// a call `libc::lchown(path, mode)`. That's why `lchmod` is unimplemented in
610
+ /// the `nix` crate.
611
+ ///
612
+ /// # References
613
+ ///
614
+ /// [fchownat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html).
615
+ pub fn fchownat < P : ?Sized + NixPath > (
616
+ dirfd : Option < RawFd > ,
617
+ path : & P ,
618
+ owner : Option < Uid > ,
619
+ group : Option < Gid > ,
620
+ flag : FchownatFlags ,
621
+ ) -> Result < ( ) > {
622
+ let atflag =
623
+ match flag {
624
+ FchownatFlags :: FollowSymlink => AtFlags :: empty ( ) ,
625
+ FchownatFlags :: NoFollowSymlink => AtFlags :: AT_SYMLINK_NOFOLLOW ,
626
+ } ;
627
+ let res = path. with_nix_path ( |cstr| unsafe {
628
+ let ( uid, gid) = chown_raw_ids ( owner, group) ;
629
+ libc:: fchownat ( at_rawfd ( dirfd) , cstr. as_ptr ( ) , uid, gid,
630
+ atflag. bits ( ) as libc:: c_int )
631
+ } ) ?;
632
+
633
+ Errno :: result ( res) . map ( |_| ( ) )
634
+ }
635
+
581
636
fn to_exec_array ( args : & [ CString ] ) -> Vec < * const c_char > {
582
637
let mut args_p: Vec < * const c_char > = args. iter ( ) . map ( |s| s. as_ptr ( ) ) . collect ( ) ;
583
638
args_p. push ( ptr:: null ( ) ) ;
0 commit comments