@@ -33,8 +33,9 @@ fn chgrp_test(args: &[&str], expected_output: &str, expected_error: &str, expect
3333 } ) ;
3434}
3535
36+ static INIT_GROUPS : Once = Once :: new ( ) ;
3637static mut PRIMARY_GROUP : String = String :: new ( ) ;
37- static INIT_PRIMARY_GROUP : Once = Once :: new ( ) ;
38+ static mut SECONDARY_GROUP : String = String :: new ( ) ;
3839
3940static mut GID1 : u32 = 0 ;
4041static mut GID2 : u32 = 0 ;
@@ -53,11 +54,12 @@ fn get_group_id(name: &CStr) -> u32 {
5354
5455// Return two groups that the current user belongs to.
5556fn get_groups ( ) -> ( ( String , u32 ) , ( String , u32 ) ) {
56- // Linux - (primary group of current user, "adm")
57+ // Linux - (primary group of current user, a group in the supplemental group list that
58+ // is not the primary group)
5759 // macOS - ("staff", "admin")
5860 let ( g1, g2) = if cfg ! ( target_os = "linux" ) {
5961 unsafe {
60- INIT_PRIMARY_GROUP . call_once ( || {
62+ INIT_GROUPS . call_once ( || {
6163 let uid = libc:: getuid ( ) ;
6264 let pw = libc:: getpwuid ( uid) ;
6365 if pw. is_null ( ) {
@@ -72,8 +74,63 @@ fn get_groups() -> ((String, u32), (String, u32)) {
7274
7375 let gr_name = CStr :: from_ptr ( ( & * gr) . gr_name ) . to_owned ( ) ;
7476 PRIMARY_GROUP = gr_name. to_str ( ) . unwrap ( ) . to_owned ( ) ;
77+
78+ let mut count = libc:: getgroups ( 0 , std:: ptr:: null_mut ( ) ) ;
79+ if count < 0 {
80+ panic ! (
81+ "unable to determine number of secondary groups {}" ,
82+ io:: Error :: last_os_error( )
83+ ) ;
84+ }
85+
86+ let mut groups_ptr: * mut libc:: gid_t =
87+ libc:: malloc ( std:: mem:: size_of :: < libc:: gid_t > ( ) * count as usize )
88+ as * mut libc:: gid_t ;
89+
90+ if groups_ptr. is_null ( ) {
91+ panic ! (
92+ "unable to allocate memory for groups list: {}" ,
93+ io:: Error :: last_os_error( )
94+ ) ;
95+ }
96+
97+ count = libc:: getgroups ( count, groups_ptr) ;
98+ match count {
99+ _ if count < 2 => panic ! ( "user must be a member of at least two groups" ) ,
100+ -1 => panic ! (
101+ "unable to get secondary groups: {}" ,
102+ io:: Error :: last_os_error( )
103+ ) ,
104+ _ => { }
105+ }
106+
107+ for _ in 0 ..count {
108+ if groups_ptr. is_null ( ) {
109+ panic ! ( "unable to get second group: reached end of group list" ) ;
110+ }
111+
112+ // Skip over the primary_gid
113+ if * groups_ptr == primary_gid {
114+ groups_ptr = groups_ptr. offset ( 1 ) ;
115+ continue ;
116+ } else {
117+ let second_gid = * groups_ptr;
118+ let sec_grent = libc:: getgrgid ( second_gid) ;
119+ if sec_grent. is_null ( ) {
120+ panic ! ( "Unable to get group entry for secondary group id {second_gid}" ) ;
121+ }
122+
123+ let sec_gr_name = CStr :: from_ptr ( ( & * sec_grent) . gr_name ) . to_owned ( ) ;
124+ SECONDARY_GROUP = sec_gr_name. to_str ( ) . unwrap ( ) . to_owned ( ) ;
125+ break ;
126+ }
127+ }
128+ if SECONDARY_GROUP == "" {
129+ panic ! ( "unable to find suitable secondary group" ) ;
130+ }
75131 } ) ;
76- ( PRIMARY_GROUP . clone ( ) , "adm" . to_owned ( ) )
132+
133+ ( PRIMARY_GROUP . clone ( ) , SECONDARY_GROUP . clone ( ) )
77134 }
78135 } else if cfg ! ( target_os = "macos" ) {
79136 ( "staff" . to_owned ( ) , "admin" . to_owned ( ) )
0 commit comments