@@ -1070,6 +1070,70 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> {
1070
1070
Errno :: result ( res) . map ( |_| ( ) )
1071
1071
}
1072
1072
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
+
1073
1137
/// Initialize the supplementary group access list. Sets the supplementary
1074
1138
/// group IDs for the calling process using all groups that `user` is a member
1075
1139
/// of. The additional group `group` is also added to the list.
0 commit comments