@@ -1070,6 +1070,70 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> {
10701070 Errno :: result ( res) . map ( |_| ( ) )
10711071}
10721072
1073+ /// Calculate the supplementary group access list. Gets the group IDs of all
1074+ /// groups that `user` is a member of. The additional group `group` is also
1075+ /// added to the list.
1076+ ///
1077+ /// [Further reading](http://man7.org/linux/man-pages/man3/getgrouplist.3.html)
1078+ ///
1079+ /// # Errors
1080+ ///
1081+ /// Although the `getgrouplist()` call does not return any specific
1082+ /// errors on any known platforms, this implementation will return a system
1083+ /// error of `EINVAL` if the number of groups to be fetched exceeds the
1084+ /// `NGROUPS_MAX` sysconf value. This mimics the behaviour of `getgroups()`
1085+ /// and `setgroups()`. Additionally, while some implementations will return a
1086+ /// partial list of groups when `NGROUPS_MAX` is exceeded, this implementation
1087+ /// will only ever return the complete list or else an error.
1088+ pub fn getgrouplist ( user : & CString , group : Gid ) -> Result < Vec < Gid > > {
1089+ let ngroups_max = match sysconf ( SysconfVar :: NGROUPS_MAX ) {
1090+ Ok ( Some ( n) ) => n as c_int ,
1091+ Ok ( None ) | Err ( _) => <c_int >:: max_value ( ) ,
1092+ } ;
1093+ use std:: cmp:: min;
1094+ let mut ngroups = min ( ngroups_max, 8 ) ;
1095+ let mut groups = Vec :: < Gid > :: with_capacity ( ngroups as usize ) ;
1096+ cfg_if ! {
1097+ if #[ cfg( any( target_os = "ios" , target_os = "macos" ) ) ] {
1098+ type getgrouplist_group_t = c_int;
1099+ } else {
1100+ type getgrouplist_group_t = gid_t;
1101+ }
1102+ }
1103+ let gid: gid_t = group. into ( ) ;
1104+ loop {
1105+ let ret = unsafe {
1106+ libc:: getgrouplist ( user. as_ptr ( ) ,
1107+ gid as getgrouplist_group_t ,
1108+ groups. as_mut_ptr ( ) as * mut getgrouplist_group_t ,
1109+ & mut ngroups)
1110+ } ;
1111+ // BSD systems only return 0 or -1, Linux returns ngroups on success.
1112+ if ret >= 0 {
1113+ unsafe { groups. set_len ( ngroups as usize ) } ;
1114+ return Ok ( groups) ;
1115+ } else if ret == -1 {
1116+ // Returns -1 if ngroups is too small, but does not set errno.
1117+ // BSD systems will still fill the groups buffer with as many
1118+ // groups as possible, but Linux manpages do not mention this
1119+ // behavior.
1120+
1121+ let cap = groups. capacity ( ) ;
1122+ if cap >= ngroups_max as usize {
1123+ // We already have the largest capacity we can, give up
1124+ return Err ( Error :: invalid_argument ( ) ) ;
1125+ }
1126+
1127+ // Reserve space for at least ngroups
1128+ groups. reserve ( ngroups as usize ) ;
1129+
1130+ // Even if the buffer gets resized to bigger than ngroups_max,
1131+ // don't ever ask for more than ngroups_max groups
1132+ ngroups = min ( ngroups_max, groups. capacity ( ) as c_int ) ;
1133+ }
1134+ }
1135+ }
1136+
10731137/// Initialize the supplementary group access list. Sets the supplementary
10741138/// group IDs for the calling process using all groups that `user` is a member
10751139/// of. The additional group `group` is also added to the list.
0 commit comments