@@ -6,14 +6,13 @@ use fcntl::{fcntl, OFlag, O_CLOEXEC, FD_CLOEXEC};
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} ;
9
- use std:: mem;
9
+ use std:: { fmt , mem, ptr } ;
10
10
use std:: ffi:: { CString , CStr , OsString , OsStr } ;
11
11
use std:: os:: unix:: ffi:: { OsStringExt , OsStrExt } ;
12
12
use std:: os:: unix:: io:: RawFd ;
13
13
use std:: path:: { PathBuf } ;
14
14
use void:: Void ;
15
15
use sys:: stat:: Mode ;
16
- use std:: fmt;
17
16
18
17
#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
19
18
pub use self :: pivot_root:: * ;
@@ -464,7 +463,7 @@ pub fn mkdir<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> {
464
463
/// fn main() {
465
464
/// let tmp_dir = TempDir::new("test_fifo").unwrap();
466
465
/// let fifo_path = tmp_dir.path().join("foo.pipe");
467
- ///
466
+ ///
468
467
/// // create new fifo and give read, write and execute rights to the owner
469
468
/// match unistd::mkfifo(&fifo_path, stat::S_IRWXU) {
470
469
/// Ok(_) => println!("created {:?}", fifo_path),
@@ -554,9 +553,6 @@ pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<Uid>, group: Option<Gi
554
553
}
555
554
556
555
fn to_exec_array ( args : & [ CString ] ) -> Vec < * const c_char > {
557
- use std:: ptr;
558
- use libc:: c_char;
559
-
560
556
let mut args_p: Vec < * const c_char > = args. iter ( ) . map ( |s| s. as_ptr ( ) ) . collect ( ) ;
561
557
args_p. push ( ptr:: null ( ) ) ;
562
558
args_p
@@ -804,7 +800,7 @@ pub enum Whence {
804
800
SeekCur = libc:: SEEK_CUR ,
805
801
/// Specify an offset relative to the end of the file.
806
802
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
808
804
/// equal to offset that contains some data. If offset points to
809
805
/// some data, then the file offset is set to offset.
810
806
#[ cfg( any( target_os = "dragonfly" , target_os = "freebsd" ,
@@ -813,7 +809,7 @@ pub enum Whence {
813
809
target_arch = "mips64" ) ) ) ) ) ]
814
810
SeekData = libc:: SEEK_DATA ,
815
811
/// 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
817
813
/// the file offset should be set to offset. If there is no hole past offset,
818
814
/// then the file offset should be adjusted to the end of the file (i.e., there
819
815
/// is an implicit hole at the end of any file).
@@ -1047,6 +1043,206 @@ pub fn setgid(gid: Gid) -> Result<()> {
1047
1043
Errno :: result ( res) . map ( drop)
1048
1044
}
1049
1045
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
+
1050
1246
/// Suspend the thread until a signal is received
1051
1247
///
1052
1248
/// See also [pause(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html)
@@ -1361,7 +1557,7 @@ pub enum SysconfVar {
1361
1557
OPEN_MAX = libc:: _SC_OPEN_MAX,
1362
1558
#[ cfg( any( target_os="dragonfly" , target_os="freebsd" , target_os = "ios" ,
1363
1559
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.
1365
1561
_POSIX_ADVISORY_INFO = libc:: _SC_ADVISORY_INFO,
1366
1562
#[ cfg( any( target_os="dragonfly" , target_os="freebsd" , target_os = "ios" ,
1367
1563
target_os="linux" , target_os = "macos" , target_os="netbsd" ,
@@ -1380,7 +1576,7 @@ pub enum SysconfVar {
1380
1576
target_os="openbsd" ) ) ]
1381
1577
/// The implementation supports the Process CPU-Time Clocks option.
1382
1578
_POSIX_CPUTIME = libc:: _SC_CPUTIME,
1383
- /// The implementation supports the File Synchronization option.
1579
+ /// The implementation supports the File Synchronization option.
1384
1580
_POSIX_FSYNC = libc:: _SC_FSYNC,
1385
1581
#[ cfg( any( target_os="dragonfly" , target_os="freebsd" , target_os = "ios" ,
1386
1582
target_os="linux" , target_os = "macos" , target_os="openbsd" ) ) ]
@@ -1495,7 +1691,7 @@ pub enum SysconfVar {
1495
1691
target_os="linux" , target_os = "macos" , target_os="openbsd" ) ) ]
1496
1692
/// The implementation supports timeouts.
1497
1693
_POSIX_TIMEOUTS = libc:: _SC_TIMEOUTS,
1498
- /// The implementation supports timers.
1694
+ /// The implementation supports timers.
1499
1695
_POSIX_TIMERS = libc:: _SC_TIMERS,
1500
1696
#[ cfg( any( target_os="dragonfly" , target_os="freebsd" , target_os = "ios" ,
1501
1697
target_os="linux" , target_os = "macos" , target_os="openbsd" ) ) ]
0 commit comments