| /* Prototype for the setgrent functions we use here. */ |
| typedef enum nss_status (*set_function) (void); |
| |
| /* Prototype for the endgrent functions we use here. */ |
| typedef enum nss_status (*end_function) (void); |
| |
| /* Prototype for the setgrent functions we use here. */ |
| typedef enum nss_status (*get_function) (struct group *, char *, |
| size_t, int *); |
| |
| |
| static enum nss_status |
| compat_call (service_user *nip, const char *user, gid_t group, long int *start, |
| long int *size, gid_t **groupsp, long int limit, int *errnop) |
| { |
| struct group grpbuf; |
| size_t buflen = __sysconf (_SC_GETGR_R_SIZE_MAX); |
| enum nss_status status; |
| set_function setgrent_fct; |
| get_function getgrent_fct; |
| end_function endgrent_fct; |
| gid_t *groups = *groupsp; |
| |
| getgrent_fct = __nss_lookup_function (nip, "getgrent_r"); |
| if (getgrent_fct == NULL) |
| return NSS_STATUS_UNAVAIL; |
| |
| setgrent_fct = __nss_lookup_function (nip, "setgrent"); |
| if (setgrent_fct) |
| { |
| status = DL_CALL_FCT (setgrent_fct, ()); |
| if (status != NSS_STATUS_SUCCESS) |
| return status; |
| } |
| |
| endgrent_fct = __nss_lookup_function (nip, "endgrent"); |
| |
| char *tmpbuf = __alloca (buflen); |
| bool use_malloc = false; |
| enum nss_status result = NSS_STATUS_SUCCESS; |
| |
| do |
| { |
| while ((status = DL_CALL_FCT (getgrent_fct, |
| (&grpbuf, tmpbuf, buflen, errnop)), |
| status == NSS_STATUS_TRYAGAIN) |
| && *errnop == ERANGE) |
| { |
| if (__libc_use_alloca (buflen * 2)) |
| tmpbuf = extend_alloca (tmpbuf, buflen, buflen * 2); |
| else |
| { |
| buflen *= 2; |
| char *newbuf = realloc (use_malloc ? tmpbuf : NULL, buflen); |
| if (newbuf == NULL) |
| { |
| result = NSS_STATUS_TRYAGAIN; |
| goto done; |
| } |
| use_malloc = true; |
| tmpbuf = newbuf; |
| } |
| } |
| |
| if (status != NSS_STATUS_SUCCESS) |
| goto done; |
| |
| if (grpbuf.gr_gid != group) |
| { |
| char **m; |
| |
| for (m = grpbuf.gr_mem; *m != NULL; ++m) |
| if (strcmp (*m, user) == 0) |
| { |
| /* Check whether the group is already on the list. */ |
| long int cnt; |
| for (cnt = 0; cnt < *start; ++cnt) |
| if (groups[cnt] == grpbuf.gr_gid) |
| break; |
| |
| if (cnt == *start) |
| { |
| /* Matches user and not yet on the list. Insert |
| this group. */ |
| if (__builtin_expect (*start == *size, 0)) |
| { |
| /* Need a bigger buffer. */ |
| gid_t *newgroups; |
| long int newsize; |
| |
| if (limit > 0 && *size == limit) |
| /* We reached the maximum. */ |
| goto done; |
| |
| if (limit <= 0) |
| newsize = 2 * *size; |
| else |
| newsize = MIN (limit, 2 * *size); |
| |
| newgroups = realloc (groups, |
| newsize * sizeof (*groups)); |
| if (newgroups == NULL) |
| goto done; |
| *groupsp = groups = newgroups; |
| *size = newsize; |
| } |
| |
| groups[*start] = grpbuf.gr_gid; |
| *start += 1; |
| } |
| |
| break; |
| } |
| } |
| } |
| while (status == NSS_STATUS_SUCCESS); |
| |
| done: |
| if (use_malloc) |
| free (tmpbuf); |
| |
| if (endgrent_fct) |
| DL_CALL_FCT (endgrent_fct, ()); |
| |
| return result; |
| } |