@@ -1135,6 +1135,70 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> {
11351135 Errno :: result ( res) . map ( |_| ( ) )
11361136}
11371137
1138+ /// Calculate the supplementary group access list. Gets the group IDs of all
1139+ /// groups that `user` is a member of. The additional group `group` is also
1140+ /// added to the list.
1141+ ///
1142+ /// [Further reading](http://man7.org/linux/man-pages/man3/getgrouplist.3.html)
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+ pub fn getgrouplist ( user : & CString , group : Gid ) -> Result < Vec < Gid > > {
1154+ let ngroups_max = match sysconf ( SysconfVar :: NGROUPS_MAX ) {
1155+ Ok ( Some ( n) ) => n as c_int ,
1156+ Ok ( None ) | Err ( _) => <c_int >:: max_value ( ) ,
1157+ } ;
1158+ use std:: cmp:: min;
1159+ let mut ngroups = min ( ngroups_max, 8 ) ;
1160+ let mut groups = Vec :: < Gid > :: with_capacity ( ngroups as usize ) ;
1161+ cfg_if ! {
1162+ if #[ cfg( any( target_os = "ios" , target_os = "macos" ) ) ] {
1163+ type getgrouplist_group_t = c_int;
1164+ } else {
1165+ type getgrouplist_group_t = gid_t;
1166+ }
1167+ }
1168+ let gid: gid_t = group. into ( ) ;
1169+ loop {
1170+ let ret = unsafe {
1171+ libc:: getgrouplist ( user. as_ptr ( ) ,
1172+ gid as getgrouplist_group_t ,
1173+ groups. as_mut_ptr ( ) as * mut getgrouplist_group_t ,
1174+ & mut ngroups)
1175+ } ;
1176+ // BSD systems only return 0 or -1, Linux returns ngroups on success.
1177+ if ret >= 0 {
1178+ unsafe { groups. set_len ( ngroups as usize ) } ;
1179+ return Ok ( groups) ;
1180+ } else if ret == -1 {
1181+ // Returns -1 if ngroups is too small, but does not set errno.
1182+ // BSD systems will still fill the groups buffer with as many
1183+ // groups as possible, but Linux manpages do not mention this
1184+ // behavior.
1185+
1186+ let cap = groups. capacity ( ) ;
1187+ if cap >= ngroups_max as usize {
1188+ // We already have the largest capacity we can, give up
1189+ return Err ( Error :: invalid_argument ( ) ) ;
1190+ }
1191+
1192+ // Reserve space for at least ngroups
1193+ groups. reserve ( ngroups as usize ) ;
1194+
1195+ // Even if the buffer gets resized to bigger than ngroups_max,
1196+ // don't ever ask for more than ngroups_max groups
1197+ ngroups = min ( ngroups_max, groups. capacity ( ) as c_int ) ;
1198+ }
1199+ }
1200+ }
1201+
11381202/// Initialize the supplementary group access list. Sets the supplementary
11391203/// group IDs for the calling process using all groups that `user` is a member
11401204/// of. The additional group `group` is also added to the list.
0 commit comments