@@ -6,14 +6,13 @@ use fcntl::{fcntl, OFlag, O_CLOEXEC, FD_CLOEXEC};
66use fcntl:: FcntlArg :: F_SETFD ;
77use libc:: { self , c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t,
88 uid_t, gid_t, mode_t} ;
9- use std:: mem;
9+ use std:: { fmt , mem, ptr } ;
1010use std:: ffi:: { CString , CStr , OsString , OsStr } ;
1111use std:: os:: unix:: ffi:: { OsStringExt , OsStrExt } ;
1212use std:: os:: unix:: io:: RawFd ;
1313use std:: path:: { PathBuf } ;
1414use void:: Void ;
1515use sys:: stat:: Mode ;
16- use std:: fmt;
1716
1817#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
1918pub use self :: pivot_root:: * ;
@@ -464,7 +463,7 @@ pub fn mkdir<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> {
464463/// fn main() {
465464/// let tmp_dir = TempDir::new("test_fifo").unwrap();
466465/// let fifo_path = tmp_dir.path().join("foo.pipe");
467- ///
466+ ///
468467/// // create new fifo and give read, write and execute rights to the owner
469468/// match unistd::mkfifo(&fifo_path, stat::S_IRWXU) {
470469/// Ok(_) => println!("created {:?}", fifo_path),
@@ -554,9 +553,6 @@ pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<Uid>, group: Option<Gi
554553}
555554
556555fn to_exec_array ( args : & [ CString ] ) -> Vec < * const c_char > {
557- use std:: ptr;
558- use libc:: c_char;
559-
560556 let mut args_p: Vec < * const c_char > = args. iter ( ) . map ( |s| s. as_ptr ( ) ) . collect ( ) ;
561557 args_p. push ( ptr:: null ( ) ) ;
562558 args_p
@@ -804,7 +800,7 @@ pub enum Whence {
804800 SeekCur = libc:: SEEK_CUR ,
805801 /// Specify an offset relative to the end of the file.
806802 SeekEnd = libc:: SEEK_END ,
807- /// Specify an offset relative to the next location in the file greater than or
803+ /// Specify an offset relative to the next location in the file greater than or
808804 /// equal to offset that contains some data. If offset points to
809805 /// some data, then the file offset is set to offset.
810806 #[ cfg( any( target_os = "dragonfly" , target_os = "freebsd" ,
@@ -813,7 +809,7 @@ pub enum Whence {
813809 target_arch = "mips64" ) ) ) ) ) ]
814810 SeekData = libc:: SEEK_DATA ,
815811 /// Specify an offset relative to the next hole in the file greater than
816- /// or equal to offset. If offset points into the middle of a hole, then
812+ /// or equal to offset. If offset points into the middle of a hole, then
817813 /// the file offset should be set to offset. If there is no hole past offset,
818814 /// then the file offset should be adjusted to the end of the file (i.e., there
819815 /// is an implicit hole at the end of any file).
@@ -1047,6 +1043,206 @@ pub fn setgid(gid: Gid) -> Result<()> {
10471043 Errno :: result ( res) . map ( drop)
10481044}
10491045
1046+ /// Get the list of supplementary group IDs of the calling process.
1047+ ///
1048+ /// [Further reading](http://pubs.opengroup.org/onlinepubs/009695399/functions/getgroups.html)
1049+ ///
1050+ /// **Note:** This function is not available for Apple platforms. On those
1051+ /// platforms, checking group membership should be achieved via communication
1052+ /// with the `opendirectoryd` service.
1053+ #[ cfg( not( any( target_os = "ios" , target_os = "macos" ) ) ) ]
1054+ pub fn getgroups ( ) -> Result < Vec < Gid > > {
1055+ // First get the number of groups so we can size our Vec
1056+ let ret = unsafe { libc:: getgroups ( 0 , ptr:: null_mut ( ) ) } ;
1057+
1058+ // Now actually get the groups. We try multiple times in case the number of
1059+ // groups has changed since the first call to getgroups() and the buffer is
1060+ // now too small.
1061+ let mut groups = Vec :: < Gid > :: with_capacity ( Errno :: result ( ret) ? as usize ) ;
1062+ loop {
1063+ // FIXME: On the platforms we currently support, the `Gid` struct has
1064+ // the same representation in memory as a bare `gid_t`. This is not
1065+ // necessarily the case on all Rust platforms, though. See RFC 1785.
1066+ let ret = unsafe {
1067+ libc:: getgroups ( groups. capacity ( ) as c_int , groups. as_mut_ptr ( ) as * mut gid_t )
1068+ } ;
1069+
1070+ match Errno :: result ( ret) {
1071+ Ok ( s) => {
1072+ unsafe { groups. set_len ( s as usize ) } ;
1073+ return Ok ( groups) ;
1074+ } ,
1075+ Err ( Error :: Sys ( Errno :: EINVAL ) ) => {
1076+ // EINVAL indicates that the buffer size was too small. Trigger
1077+ // the internal buffer resizing logic of `Vec` by requiring
1078+ // more space than the current capacity.
1079+ let cap = groups. capacity ( ) ;
1080+ unsafe { groups. set_len ( cap) } ;
1081+ groups. reserve ( 1 ) ;
1082+ } ,
1083+ Err ( e) => return Err ( e)
1084+ }
1085+ }
1086+ }
1087+
1088+ /// Set the list of supplementary group IDs for the calling process.
1089+ ///
1090+ /// [Further reading](http://man7.org/linux/man-pages/man2/getgroups.2.html)
1091+ ///
1092+ /// **Note:** This function is not available for Apple platforms. On those
1093+ /// platforms, group membership management should be achieved via communication
1094+ /// with the `opendirectoryd` service.
1095+ ///
1096+ /// # Examples
1097+ ///
1098+ /// `setgroups` can be used when dropping privileges from the root user to a
1099+ /// specific user and group. For example, given the user `www-data` with UID
1100+ /// `33` and the group `backup` with the GID `34`, one could switch the user as
1101+ /// follows:
1102+ /// ```
1103+ /// let uid = Uid::from_raw(33);
1104+ /// let gid = Gid::from_raw(34);
1105+ /// setgroups(&[gid])?;
1106+ /// setgid(gid)?;
1107+ /// setuid(uid)?;
1108+ /// ```
1109+ #[ cfg( not( any( target_os = "ios" , target_os = "macos" ) ) ) ]
1110+ pub fn setgroups ( groups : & [ Gid ] ) -> Result < ( ) > {
1111+ cfg_if ! {
1112+ if #[ cfg( any( target_os = "dragonfly" ,
1113+ target_os = "freebsd" ,
1114+ target_os = "ios" ,
1115+ target_os = "macos" ,
1116+ target_os = "netbsd" ,
1117+ target_os = "openbsd" ) ) ] {
1118+ type setgroups_ngroups_t = c_int;
1119+ } else {
1120+ type setgroups_ngroups_t = size_t;
1121+ }
1122+ }
1123+ // FIXME: On the platforms we currently support, the `Gid` struct has the
1124+ // same representation in memory as a bare `gid_t`. This is not necessarily
1125+ // the case on all Rust platforms, though. See RFC 1785.
1126+ let res = unsafe {
1127+ libc:: setgroups ( groups. len ( ) as setgroups_ngroups_t , groups. as_ptr ( ) as * const gid_t )
1128+ } ;
1129+
1130+ Errno :: result ( res) . map ( |_| ( ) )
1131+ }
1132+
1133+ /// Calculate the supplementary group access list.
1134+ ///
1135+ /// Gets the group IDs of all groups that `user` is a member of. The additional
1136+ /// group `group` is also added to the list.
1137+ ///
1138+ /// [Further reading](http://man7.org/linux/man-pages/man3/getgrouplist.3.html)
1139+ ///
1140+ /// **Note:** This function is not available for Apple platforms. On those
1141+ /// platforms, checking group membership should be achieved via communication
1142+ /// with the `opendirectoryd` service.
1143+ ///
1144+ /// # Errors
1145+ ///
1146+ /// Although the `getgrouplist()` call does not return any specific
1147+ /// errors on any known platforms, this implementation will return a system
1148+ /// error of `EINVAL` if the number of groups to be fetched exceeds the
1149+ /// `NGROUPS_MAX` sysconf value. This mimics the behaviour of `getgroups()`
1150+ /// and `setgroups()`. Additionally, while some implementations will return a
1151+ /// partial list of groups when `NGROUPS_MAX` is exceeded, this implementation
1152+ /// will only ever return the complete list or else an error.
1153+ #[ cfg( not( any( target_os = "ios" , target_os = "macos" ) ) ) ]
1154+ pub fn getgrouplist ( user : & CStr , group : Gid ) -> Result < Vec < Gid > > {
1155+ let ngroups_max = match sysconf ( SysconfVar :: NGROUPS_MAX ) {
1156+ Ok ( Some ( n) ) => n as c_int ,
1157+ Ok ( None ) | Err ( _) => <c_int >:: max_value ( ) ,
1158+ } ;
1159+ use std:: cmp:: min;
1160+ let mut ngroups = min ( ngroups_max, 8 ) ;
1161+ let mut groups = Vec :: < Gid > :: with_capacity ( ngroups as usize ) ;
1162+ cfg_if ! {
1163+ if #[ cfg( any( target_os = "ios" , target_os = "macos" ) ) ] {
1164+ type getgrouplist_group_t = c_int;
1165+ } else {
1166+ type getgrouplist_group_t = gid_t;
1167+ }
1168+ }
1169+ let gid: gid_t = group. into ( ) ;
1170+ loop {
1171+ let ret = unsafe {
1172+ libc:: getgrouplist ( user. as_ptr ( ) ,
1173+ gid as getgrouplist_group_t ,
1174+ groups. as_mut_ptr ( ) as * mut getgrouplist_group_t ,
1175+ & mut ngroups)
1176+ } ;
1177+
1178+ // BSD systems only return 0 or -1, Linux returns ngroups on success.
1179+ if ret >= 0 {
1180+ unsafe { groups. set_len ( ngroups as usize ) } ;
1181+ return Ok ( groups) ;
1182+ } else if ret == -1 {
1183+ // Returns -1 if ngroups is too small, but does not set errno.
1184+ // BSD systems will still fill the groups buffer with as many
1185+ // groups as possible, but Linux manpages do not mention this
1186+ // behavior.
1187+
1188+ let cap = groups. capacity ( ) ;
1189+ if cap >= ngroups_max as usize {
1190+ // We already have the largest capacity we can, give up
1191+ return Err ( Error :: invalid_argument ( ) ) ;
1192+ }
1193+
1194+ // Reserve space for at least ngroups
1195+ groups. reserve ( ngroups as usize ) ;
1196+
1197+ // Even if the buffer gets resized to bigger than ngroups_max,
1198+ // don't ever ask for more than ngroups_max groups
1199+ ngroups = min ( ngroups_max, groups. capacity ( ) as c_int ) ;
1200+ }
1201+ }
1202+ }
1203+
1204+ /// Initialize the supplementary group access list.
1205+ ///
1206+ /// Sets the supplementary group IDs for the calling process using all groups
1207+ /// that `user` is a member of. The additional group `group` is also added to
1208+ /// the list.
1209+ ///
1210+ /// [Further reading](http://man7.org/linux/man-pages/man3/initgroups.3.html)
1211+ ///
1212+ /// **Note:** This function is not available for Apple platforms. On those
1213+ /// platforms, group membership management should be achieved via communication
1214+ /// with the `opendirectoryd` service.
1215+ ///
1216+ /// # Examples
1217+ ///
1218+ /// `initgroups` can be used when dropping privileges from the root user to
1219+ /// another user. For example, given the user `www-data`, we could look up the
1220+ /// UID and GID for the user in the system's password database (usually found
1221+ /// in `/etc/passwd`). If the `www-data` user's UID and GID were `33` and `33`,
1222+ /// respectively, one could switch the user as follows:
1223+ /// ```
1224+ /// let user = CString::new("www-data").unwrap();
1225+ /// let uid = Uid::from_raw(33);
1226+ /// let gid = Gid::from_raw(33);
1227+ /// initgroups(&user, gid)?;
1228+ /// setgid(gid)?;
1229+ /// setuid(uid)?;
1230+ /// ```
1231+ #[ cfg( not( any( target_os = "ios" , target_os = "macos" ) ) ) ]
1232+ pub fn initgroups ( user : & CStr , group : Gid ) -> Result < ( ) > {
1233+ cfg_if ! {
1234+ if #[ cfg( any( target_os = "ios" , target_os = "macos" ) ) ] {
1235+ type initgroups_group_t = c_int;
1236+ } else {
1237+ type initgroups_group_t = gid_t;
1238+ }
1239+ }
1240+ let gid: gid_t = group. into ( ) ;
1241+ let res = unsafe { libc:: initgroups ( user. as_ptr ( ) , gid as initgroups_group_t ) } ;
1242+
1243+ Errno :: result ( res) . map ( |_| ( ) )
1244+ }
1245+
10501246/// Suspend the thread until a signal is received
10511247///
10521248/// See also [pause(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html)
@@ -1361,7 +1557,7 @@ pub enum SysconfVar {
13611557 OPEN_MAX = libc:: _SC_OPEN_MAX,
13621558 #[ cfg( any( target_os="dragonfly" , target_os="freebsd" , target_os = "ios" ,
13631559 target_os="linux" , target_os = "macos" , target_os="openbsd" ) ) ]
1364- /// The implementation supports the Advisory Information option.
1560+ /// The implementation supports the Advisory Information option.
13651561 _POSIX_ADVISORY_INFO = libc:: _SC_ADVISORY_INFO,
13661562 #[ cfg( any( target_os="dragonfly" , target_os="freebsd" , target_os = "ios" ,
13671563 target_os="linux" , target_os = "macos" , target_os="netbsd" ,
@@ -1380,7 +1576,7 @@ pub enum SysconfVar {
13801576 target_os="openbsd" ) ) ]
13811577 /// The implementation supports the Process CPU-Time Clocks option.
13821578 _POSIX_CPUTIME = libc:: _SC_CPUTIME,
1383- /// The implementation supports the File Synchronization option.
1579+ /// The implementation supports the File Synchronization option.
13841580 _POSIX_FSYNC = libc:: _SC_FSYNC,
13851581 #[ cfg( any( target_os="dragonfly" , target_os="freebsd" , target_os = "ios" ,
13861582 target_os="linux" , target_os = "macos" , target_os="openbsd" ) ) ]
@@ -1495,7 +1691,7 @@ pub enum SysconfVar {
14951691 target_os="linux" , target_os = "macos" , target_os="openbsd" ) ) ]
14961692 /// The implementation supports timeouts.
14971693 _POSIX_TIMEOUTS = libc:: _SC_TIMEOUTS,
1498- /// The implementation supports timers.
1694+ /// The implementation supports timers.
14991695 _POSIX_TIMERS = libc:: _SC_TIMERS,
15001696 #[ cfg( any( target_os="dragonfly" , target_os="freebsd" , target_os = "ios" ,
15011697 target_os="linux" , target_os = "macos" , target_os="openbsd" ) ) ]
0 commit comments