Merge branch 'master' into bug13658-branch
[platform/upstream/glibc.git] / grp / compat-initgroups.c
1 /* Prototype for the setgrent functions we use here.  */
2 typedef enum nss_status (*set_function) (void);
3
4 /* Prototype for the endgrent functions we use here.  */
5 typedef enum nss_status (*end_function) (void);
6
7 /* Prototype for the setgrent functions we use here.  */
8 typedef enum nss_status (*get_function) (struct group *, char *,
9                                          size_t, int *);
10
11
12 static enum nss_status
13 compat_call (service_user *nip, const char *user, gid_t group, long int *start,
14              long int *size, gid_t **groupsp, long int limit, int *errnop)
15 {
16   struct group grpbuf;
17   size_t buflen = __sysconf (_SC_GETGR_R_SIZE_MAX);
18   enum nss_status status;
19   set_function setgrent_fct;
20   get_function getgrent_fct;
21   end_function endgrent_fct;
22   gid_t *groups = *groupsp;
23
24   getgrent_fct = __nss_lookup_function (nip, "getgrent_r");
25   if (getgrent_fct == NULL)
26     return NSS_STATUS_UNAVAIL;
27
28   setgrent_fct = __nss_lookup_function (nip, "setgrent");
29   if (setgrent_fct)
30     {
31       status = DL_CALL_FCT (setgrent_fct, ());
32       if (status != NSS_STATUS_SUCCESS)
33         return status;
34     }
35
36   endgrent_fct = __nss_lookup_function (nip, "endgrent");
37
38   char *tmpbuf = __alloca (buflen);
39   bool use_malloc = false;
40   enum nss_status result = NSS_STATUS_SUCCESS;
41
42   do
43     {
44       while ((status = DL_CALL_FCT (getgrent_fct,
45                                      (&grpbuf, tmpbuf, buflen, errnop)),
46               status == NSS_STATUS_TRYAGAIN)
47              && *errnop == ERANGE)
48         {
49           if (__libc_use_alloca (buflen * 2))
50             tmpbuf = extend_alloca (tmpbuf, buflen, buflen * 2);
51           else
52             {
53               buflen *= 2;
54               char *newbuf = realloc (use_malloc ? tmpbuf : NULL, buflen);
55               if (newbuf == NULL)
56                 {
57                   result = NSS_STATUS_TRYAGAIN;
58                   goto done;
59                 }
60               use_malloc = true;
61               tmpbuf = newbuf;
62             }
63         }
64
65       if (status != NSS_STATUS_SUCCESS)
66         goto done;
67
68       if (grpbuf.gr_gid != group)
69         {
70           char **m;
71
72           for (m = grpbuf.gr_mem; *m != NULL; ++m)
73             if (strcmp (*m, user) == 0)
74               {
75                 /* Check whether the group is already on the list.  */
76                 long int cnt;
77                 for (cnt = 0; cnt < *start; ++cnt)
78                   if (groups[cnt] == grpbuf.gr_gid)
79                     break;
80
81                 if (cnt == *start)
82                   {
83                     /* Matches user and not yet on the list.  Insert
84                        this group.  */
85                     if (__builtin_expect (*start == *size, 0))
86                       {
87                         /* Need a bigger buffer.  */
88                         gid_t *newgroups;
89                         long int newsize;
90
91                         if (limit > 0 && *size == limit)
92                           /* We reached the maximum.  */
93                           goto done;
94
95                         if (limit <= 0)
96                           newsize = 2 * *size;
97                         else
98                           newsize = MIN (limit, 2 * *size);
99
100                         newgroups = realloc (groups,
101                                              newsize * sizeof (*groups));
102                         if (newgroups == NULL)
103                           goto done;
104                         *groupsp = groups = newgroups;
105                         *size = newsize;
106                       }
107
108                     groups[*start] = grpbuf.gr_gid;
109                     *start += 1;
110                   }
111
112                 break;
113               }
114         }
115     }
116   while (status == NSS_STATUS_SUCCESS);
117
118  done:
119   if (use_malloc)
120     free (tmpbuf);
121
122   if (endgrent_fct)
123     DL_CALL_FCT (endgrent_fct, ());
124
125   return result;
126 }