Change everything to store error code through provided pointer and not
[kopensolaris-gnu/glibc.git] / nis / nss_compat / compat-grp.c
1 /* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <nss.h>
23 #include <grp.h>
24 #include <ctype.h>
25 #include <bits/libc-lock.h>
26 #include <string.h>
27 #include <rpcsvc/yp.h>
28 #include <rpcsvc/ypclnt.h>
29 #include <rpcsvc/nis.h>
30 #include <nsswitch.h>
31
32 #include "nss-nisplus.h"
33 #include "nisplus-parser.h"
34
35 static service_user *ni = NULL;
36 static bool_t use_nisplus = FALSE; /* default: group_compat: nis */
37 static nis_name grptable = NULL; /* Name of the group table */
38 static size_t grptablelen = 0;
39
40 /* Get the declaration of the parser function.  */
41 #define ENTNAME grent
42 #define STRUCTURE group
43 #define EXTERN_PARSER
44 #include <nss/nss_files/files-parse.c>
45
46 /* Structure for remembering -group members ... */
47 #define BLACKLIST_INITIAL_SIZE 512
48 #define BLACKLIST_INCREMENT 256
49 struct blacklist_t
50   {
51     char *data;
52     int current;
53     int size;
54   };
55
56 struct ent_t
57   {
58     bool_t nis;
59     bool_t nis_first;
60     char *oldkey;
61     int oldkeylen;
62     nis_result *result;
63     FILE *stream;
64     struct blacklist_t blacklist;
65 };
66 typedef struct ent_t ent_t;
67
68 static ent_t ext_ent = {0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0}};
69
70 /* Protect global state against multiple changers.  */
71 __libc_lock_define_initialized (static, lock)
72
73 /* Prototypes for local functions.  */
74 static void blacklist_store_name (const char *, ent_t *);
75 static int in_blacklist (const char *, int, ent_t *);
76
77 static enum nss_status
78 _nss_first_init (void)
79 {
80   if (ni == NULL)
81     {
82       __nss_database_lookup ("group_compat", NULL, "nis", &ni);
83       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
84     }
85
86   if (grptable == NULL)
87     {
88       static const char key[] = "group.org_dir.";
89       const char *local_dir = nis_local_directory ();
90       size_t len_local_dir = strlen (local_dir);
91
92       grptable = malloc (sizeof (key) + len_local_dir);
93       if (grptable == NULL)
94         return NSS_STATUS_TRYAGAIN;
95
96       grptablelen = ((char *) mempcpy (mempcpy (grptable,
97                                                 key, sizeof (key) - 1),
98                                        local_dir, len_local_dir + 1)
99                      - grptable) - 1;
100     }
101
102   return NSS_STATUS_SUCCESS;
103 }
104
105 static enum nss_status
106 internal_setgrent (ent_t *ent)
107 {
108   enum nss_status status = NSS_STATUS_SUCCESS;
109
110   ent->nis = ent->nis_first = 0;
111
112   if (_nss_first_init () != NSS_STATUS_SUCCESS)
113     return NSS_STATUS_UNAVAIL;
114
115   if (ent->oldkey != NULL)
116     {
117       free (ent->oldkey);
118       ent->oldkey = NULL;
119       ent->oldkeylen = 0;
120     }
121
122   if (ent->result != NULL)
123     {
124       nis_freeresult (ent->result);
125       ent->result = NULL;
126     }
127
128   ent->blacklist.current = 0;
129   if (ent->blacklist.data != NULL)
130     ent->blacklist.data[0] = '\0';
131
132   if (ent->stream == NULL)
133     {
134       ent->stream = fopen ("/etc/group", "r");
135
136       if (ent->stream == NULL)
137         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
138       else
139         {
140           /* We have to make sure the file is  `closed on exec'.  */
141           int result, flags;
142
143           result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
144           if (result >= 0)
145             {
146               flags |= FD_CLOEXEC;
147               result = fcntl (fileno (ent->stream), F_SETFD, flags);
148             }
149           if (result < 0)
150             {
151               /* Something went wrong.  Close the stream and return a
152                  failure.  */
153               fclose (ent->stream);
154               ent->stream = NULL;
155               status = NSS_STATUS_UNAVAIL;
156             }
157         }
158     }
159   else
160     rewind (ent->stream);
161
162   return status;
163 }
164
165
166 enum nss_status
167 _nss_compat_setgrent (void)
168 {
169   enum nss_status result;
170
171   __libc_lock_lock (lock);
172
173   result = internal_setgrent (&ext_ent);
174
175   __libc_lock_unlock (lock);
176
177   return result;
178 }
179
180
181 static enum nss_status
182 internal_endgrent (ent_t *ent)
183 {
184   if (ent->stream != NULL)
185     {
186       fclose (ent->stream);
187       ent->stream = NULL;
188     }
189
190   ent->nis = ent->nis_first = 0;
191
192   if (ent->oldkey != NULL)
193     {
194       free (ent->oldkey);
195       ent->oldkey = NULL;
196       ent->oldkeylen = 0;
197     }
198
199   if (ent->result != NULL)
200     {
201       nis_freeresult (ent->result);
202       ent->result = NULL;
203     }
204
205   ent->blacklist.current = 0;
206   if (ent->blacklist.data != NULL)
207     ent->blacklist.data[0] = '\0';
208
209   return NSS_STATUS_SUCCESS;
210 }
211
212 enum nss_status
213 _nss_compat_endgrent (void)
214 {
215   enum nss_status result;
216
217   __libc_lock_lock (lock);
218
219   result = internal_endgrent (&ext_ent);
220
221   __libc_lock_unlock (lock);
222
223   return result;
224 }
225
226 static enum nss_status
227 getgrent_next_nis (struct group *result, ent_t *ent, char *buffer,
228                    size_t buflen, int *errnop)
229 {
230   struct parser_data *data = (void *) buffer;
231   char *domain;
232   char *outkey, *outval;
233   int outkeylen, outvallen, parse_res;
234   char *p;
235
236   if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
237     {
238       ent->nis = 0;
239       return NSS_STATUS_NOTFOUND;
240     }
241
242   do
243     {
244       char *save_oldkey;
245       int save_oldlen;
246       bool_t save_nis_first;
247
248       if (ent->nis_first)
249         {
250           if (yp_first (domain, "group.byname", &outkey, &outkeylen,
251                         &outval, &outvallen) != YPERR_SUCCESS)
252             {
253               ent->nis = 0;
254               return NSS_STATUS_UNAVAIL;
255             }
256           save_oldkey = ent->oldkey;
257           save_oldlen = ent->oldkeylen;
258           save_nis_first = TRUE;
259           ent->oldkey = outkey;
260           ent->oldkeylen = outkeylen;
261           ent->nis_first = FALSE;
262         }
263       else
264         {
265           if (yp_next (domain, "group.byname", ent->oldkey, ent->oldkeylen,
266                        &outkey, &outkeylen, &outval, &outvallen)
267               != YPERR_SUCCESS)
268             {
269               ent->nis = 0;
270               return NSS_STATUS_NOTFOUND;
271             }
272
273           save_oldkey = ent->oldkey;
274           save_oldlen = ent->oldkeylen;
275           save_nis_first = FALSE;
276           ent->oldkey = outkey;
277           ent->oldkeylen = outkeylen;
278         }
279
280       /* Copy the found data to our buffer  */
281       p = strncpy (buffer, outval, buflen);
282
283       /* ...and free the data.  */
284       free (outval);
285
286       while (isspace (*p))
287         ++p;
288
289       parse_res = _nss_files_parse_grent (p, result, data, buflen, errnop);
290       if (parse_res == -1)
291         {
292           free (ent->oldkey);
293           ent->oldkey = save_oldkey;
294           ent->oldkeylen = save_oldlen;
295           ent->nis_first = save_nis_first;
296           *errnop = ERANGE;
297           return NSS_STATUS_TRYAGAIN;
298         }
299       else
300         {
301           if (!save_nis_first)
302             free (save_oldkey);
303         }
304
305       if (parse_res &&
306           in_blacklist (result->gr_name, strlen (result->gr_name), ent))
307         parse_res = 0; /* if result->gr_name in blacklist,search next entry */
308     }
309   while (!parse_res);
310
311   return NSS_STATUS_SUCCESS;
312 }
313
314 static enum nss_status
315 getgrent_next_nisplus (struct group *result, ent_t *ent, char *buffer,
316                        size_t buflen, int *errnop)
317 {
318   int parse_res;
319
320   do
321     {
322       nis_result *save_oldres;
323       bool_t save_nis_first;
324
325       if (ent->nis_first)
326         {
327           save_oldres = ent->result;
328           save_nis_first = TRUE;
329           ent->result = nis_first_entry(grptable);
330           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
331             {
332               ent->nis = 0;
333               return niserr2nss (ent->result->status);
334             }
335           ent->nis_first = FALSE;
336         }
337       else
338         {
339           nis_result *res;
340
341           save_oldres = ent->result;
342           save_nis_first = FALSE;
343           res = nis_next_entry(grptable, &ent->result->cookie);
344           ent->result = res;
345           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
346             {
347               ent->nis = 0;
348               return niserr2nss (ent->result->status);
349             }
350         }
351       parse_res = _nss_nisplus_parse_grent (ent->result, 0, result,
352                                             buffer, buflen, errnop);
353       if (parse_res == -1)
354         {
355           nis_freeresult (ent->result);
356           ent->result = save_oldres;
357           ent->nis_first = save_nis_first;
358           *errnop = ERANGE;
359           return NSS_STATUS_TRYAGAIN;
360         }
361       else
362         {
363           if (!save_nis_first)
364             nis_freeresult (save_oldres);
365         }
366
367       if (parse_res &&
368           in_blacklist (result->gr_name, strlen (result->gr_name), ent))
369         parse_res = 0; /* if result->gr_name in blacklist,search next entry */
370     }
371   while (!parse_res);
372
373   return NSS_STATUS_SUCCESS;
374 }
375
376 /* This function handle the +group entrys in /etc/group */
377 static enum nss_status
378 getgrnam_plusgroup (const char *name, struct group *result, char *buffer,
379                     size_t buflen, int *errnop)
380 {
381   struct parser_data *data = (void *) buffer;
382   int parse_res;
383
384   if (use_nisplus) /* Do the NIS+ query here */
385     {
386       nis_result *res;
387       char buf[strlen (name) + 24 + grptablelen];
388
389       sprintf(buf, "[name=%s],%s", name, grptable);
390       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
391       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
392         {
393           enum nss_status status =  niserr2nss (res->status);
394
395           nis_freeresult (res);
396           return status;
397         }
398       parse_res = _nss_nisplus_parse_grent (res, 0, result, buffer, buflen,
399                                             errnop);
400       if (parse_res == -1)
401         {
402           nis_freeresult (res);
403           *errnop = ERANGE;
404           return NSS_STATUS_TRYAGAIN;
405         }
406       nis_freeresult (res);
407     }
408   else /* Use NIS */
409     {
410       char *domain, *outval, *p;
411       int outvallen;
412
413       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
414         {
415           *errnop = errno;
416           return NSS_STATUS_TRYAGAIN;
417         }
418
419       if (yp_match (domain, "group.byname", name, strlen (name),
420                     &outval, &outvallen) != YPERR_SUCCESS)
421         {
422           *errnop = errno;
423           return NSS_STATUS_TRYAGAIN;
424         }
425       p = strncpy (buffer, outval,
426                    buflen < (size_t) outvallen ? buflen : (size_t) outvallen);
427       free (outval);
428       while (isspace (*p))
429         p++;
430       parse_res = _nss_files_parse_grent (p, result, data, buflen, errnop);
431       if (parse_res == -1)
432         return NSS_STATUS_TRYAGAIN;
433     }
434
435   if (parse_res)
436     /* We found the entry.  */
437     return NSS_STATUS_SUCCESS;
438   else
439     return NSS_STATUS_RETURN;
440 }
441
442 static enum nss_status
443 getgrent_next_file (struct group *result, ent_t *ent,
444                     char *buffer, size_t buflen, int *errnop)
445 {
446   struct parser_data *data = (void *) buffer;
447   while (1)
448     {
449       fpos_t pos;
450       int parse_res = 0;
451       char *p;
452
453       do
454         {
455           fgetpos (ent->stream, &pos);
456           p = fgets (buffer, buflen, ent->stream);
457           if (p == NULL)
458             {
459               if (feof (ent->stream))
460                 return NSS_STATUS_NOTFOUND;
461               else
462                 {
463                   fsetpos (ent->stream, &pos);
464                   *errnop = ERANGE;
465                   return NSS_STATUS_TRYAGAIN;
466                 }
467             }
468
469           /* Terminate the line for any case.  */
470           buffer[buflen - 1] = '\0';
471
472           /* Skip leading blanks.  */
473           while (isspace (*p))
474             ++p;
475         }
476       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
477       /* Parse the line.  If it is invalid, loop to
478          get the next line of the file to parse.  */
479              !(parse_res = _nss_files_parse_grent (p, result, data, buflen,
480                                                    errnop)));
481
482       if (parse_res == -1)
483         {
484           /* The parser ran out of space.  */
485           fsetpos (ent->stream, &pos);
486           *errnop = ERANGE;
487           return NSS_STATUS_TRYAGAIN;
488         }
489
490       if (result->gr_name[0] != '+' && result->gr_name[0] != '-')
491         /* This is a real entry.  */
492         break;
493
494       /* -group */
495       if (result->gr_name[0] == '-' && result->gr_name[1] != '\0'
496           && result->gr_name[1] != '@')
497         {
498           blacklist_store_name (&result->gr_name[1], ent);
499           continue;
500         }
501
502       /* +group */
503       if (result->gr_name[0] == '+' && result->gr_name[1] != '\0'
504           && result->gr_name[1] != '@')
505         {
506           enum nss_status status;
507
508           /* Store the group in the blacklist for the "+" at the end of
509              /etc/group */
510           blacklist_store_name (&result->gr_name[1], ent);
511           status = getgrnam_plusgroup (&result->gr_name[1], result, buffer,
512                                        buflen, errnop);
513           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
514             break;
515           else
516             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
517               continue;
518             else
519               {
520                 if (status == NSS_STATUS_TRYAGAIN)
521                   {
522                     /* The parser ran out of space.  */
523                     fsetpos (ent->stream, &pos);
524                     *errnop = ERANGE;
525                   }
526                 return status;
527               }
528         }
529
530       /* +:... */
531       if (result->gr_name[0] == '+' && result->gr_name[1] == '\0')
532         {
533           ent->nis = TRUE;
534           ent->nis_first = TRUE;
535
536           if (use_nisplus)
537             return getgrent_next_nisplus (result, ent, buffer, buflen, errnop);
538           else
539             return getgrent_next_nis (result, ent, buffer, buflen, errnop);
540         }
541     }
542
543   return NSS_STATUS_SUCCESS;
544 }
545
546
547 static enum nss_status
548 internal_getgrent_r (struct group *gr, ent_t *ent, char *buffer,
549                      size_t buflen, int *errnop)
550 {
551   if (ent->nis)
552     {
553       if (use_nisplus)
554         return getgrent_next_nisplus (gr, ent, buffer, buflen, errnop);
555       else
556         return getgrent_next_nis (gr, ent, buffer, buflen, errnop);
557     }
558   else
559     return getgrent_next_file (gr, ent, buffer, buflen, errnop);
560 }
561
562 enum nss_status
563 _nss_compat_getgrent_r (struct group *grp, char *buffer, size_t buflen,
564                         int *errnop)
565 {
566   enum nss_status status = NSS_STATUS_SUCCESS;
567
568   __libc_lock_lock (lock);
569
570   /* Be prepared that the setgrent function was not called before.  */
571   if (ext_ent.stream == NULL)
572     status = internal_setgrent (&ext_ent);
573
574   if (status == NSS_STATUS_SUCCESS)
575     status = internal_getgrent_r (grp, &ext_ent, buffer, buflen, errnop);
576
577   __libc_lock_unlock (lock);
578
579   return status;
580 }
581
582 /* Searches in /etc/group and the NIS/NIS+ map for a special group */
583 static enum nss_status
584 internal_getgrnam_r (const char *name, struct group *result, ent_t *ent,
585                      char *buffer, size_t buflen, int *errnop)
586 {
587   struct parser_data *data = (void *) buffer;
588   while (1)
589     {
590       fpos_t pos;
591       int parse_res = 0;
592       char *p;
593
594       do
595         {
596           fgetpos (ent->stream, &pos);
597           p = fgets (buffer, buflen, ent->stream);
598           if (p == NULL)
599             {
600               if (feof (ent->stream))
601                 return NSS_STATUS_NOTFOUND;
602               else
603                 {
604                   fsetpos (ent->stream, &pos);
605                   *errnop = ERANGE;
606                   return NSS_STATUS_TRYAGAIN;
607                 }
608             }
609
610           /* Terminate the line for any case.  */
611           buffer[buflen - 1] = '\0';
612
613           /* Skip leading blanks.  */
614           while (isspace (*p))
615             ++p;
616         }
617       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
618       /* Parse the line.  If it is invalid, loop to
619          get the next line of the file to parse.  */
620              !(parse_res = _nss_files_parse_grent (p, result, data, buflen,
621                                                    errnop)));
622
623       if (parse_res == -1)
624         {
625           /* The parser ran out of space.  */
626           fsetpos (ent->stream, &pos);
627           *errnop = ERANGE;
628           return NSS_STATUS_TRYAGAIN;
629         }
630
631       /* This is a real entry.  */
632       if (result->gr_name[0] != '+' && result->gr_name[0] != '-')
633         {
634           if (strcmp (result->gr_name, name) == 0)
635             return NSS_STATUS_SUCCESS;
636           else
637             continue;
638         }
639
640       /* -group */
641       if (result->gr_name[0] == '-' && result->gr_name[1] != '\0')
642         {
643           if (strcmp (&result->gr_name[1], name) == 0)
644             return NSS_STATUS_NOTFOUND;
645           else
646             continue;
647         }
648
649       /* +group */
650       if (result->gr_name[0] == '+' && result->gr_name[1] != '\0')
651         {
652           if (strcmp (name, &result->gr_name[1]) == 0)
653             {
654               enum nss_status status;
655
656               status = getgrnam_plusgroup (name, result, buffer, buflen,
657                                            errnop);
658               if (status == NSS_STATUS_RETURN)
659                 /* We couldn't parse the entry */
660                 continue;
661               else
662                 return status;
663             }
664         }
665       /* +:... */
666       if (result->gr_name[0] == '+' && result->gr_name[1] == '\0')
667         {
668           enum nss_status status;
669
670           status = getgrnam_plusgroup (name, result, buffer, buflen, errnop);
671           if (status == NSS_STATUS_RETURN)
672             /* We couldn't parse the entry */
673             continue;
674           else
675             return status;
676         }
677     }
678
679   return NSS_STATUS_SUCCESS;
680 }
681
682 enum nss_status
683 _nss_compat_getgrnam_r (const char *name, struct group *grp,
684                         char *buffer, size_t buflen, int *errnop)
685 {
686   ent_t ent = {0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0}};
687   enum nss_status status;
688
689   if (name[0] == '-' || name[0] == '+')
690     return NSS_STATUS_NOTFOUND;
691
692   __libc_lock_lock (lock);
693
694   status = internal_setgrent (&ent);
695
696   __libc_lock_unlock (lock);
697
698   if (status != NSS_STATUS_SUCCESS)
699     return status;
700
701   status = internal_getgrnam_r (name, grp, &ent, buffer, buflen, errnop);
702
703   internal_endgrent (&ent);
704
705   return status;
706 }
707
708 /* This function handle the + entry in /etc/group */
709 static enum nss_status
710 getgrgid_plusgroup (gid_t gid, struct group *result, char *buffer,
711                     size_t buflen, int *errnop)
712 {
713   struct parser_data *data = (void *) buffer;
714   int parse_res;
715
716   if (use_nisplus) /* Do the NIS+ query here */
717     {
718       nis_result *res;
719       char buf[24 + grptablelen];
720
721       sprintf(buf, "[gid=%d],%s", gid, grptable);
722       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
723       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
724         {
725           enum nss_status status =  niserr2nss (res->status);
726
727           nis_freeresult (res);
728           return status;
729         }
730       if ((parse_res = _nss_nisplus_parse_grent (res, 0, result, buffer,
731                                                  buflen, errnop)) == -1)
732         {
733           nis_freeresult (res);
734           *errnop = ERANGE;
735           return NSS_STATUS_TRYAGAIN;
736         }
737       nis_freeresult (res);
738     }
739   else /* Use NIS */
740     {
741       char buf[24];
742       char *domain, *outval, *p;
743       int outvallen;
744
745       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
746         {
747           *errnop = errno;
748           return NSS_STATUS_TRYAGAIN;
749         }
750
751       snprintf (buf, sizeof (buf), "%d", gid);
752
753       if (yp_match (domain, "group.bygid", buf, strlen (buf),
754                     &outval, &outvallen) != YPERR_SUCCESS)
755         {
756           *errnop = errno;
757           return NSS_STATUS_TRYAGAIN;
758         }
759       p = strncpy (buffer, outval,
760                    buflen < (size_t) outvallen ? buflen : (size_t) outvallen);
761       free (outval);
762       while (isspace (*p))
763         p++;
764       parse_res = _nss_files_parse_grent (p, result, data, buflen, errnop);
765       if (parse_res == -1)
766         return NSS_STATUS_TRYAGAIN;
767     }
768
769   if (parse_res)
770     /* We found the entry.  */
771     return NSS_STATUS_SUCCESS;
772   else
773     return NSS_STATUS_RETURN;
774 }
775
776 /* Searches in /etc/group and the NIS/NIS+ map for a special group id */
777 static enum nss_status
778 internal_getgrgid_r (gid_t gid, struct group *result, ent_t *ent,
779                      char *buffer, size_t buflen, int *errnop)
780 {
781   struct parser_data *data = (void *) buffer;
782   while (1)
783     {
784       fpos_t pos;
785       int parse_res = 0;
786       char *p;
787
788       do
789         {
790           fgetpos (ent->stream, &pos);
791           p = fgets (buffer, buflen, ent->stream);
792           if (p == NULL)
793             {
794               if (feof (ent->stream))
795                 return NSS_STATUS_NOTFOUND;
796               else
797                 {
798                   fsetpos (ent->stream, &pos);
799                   *errnop = ERANGE;
800                   return NSS_STATUS_TRYAGAIN;
801                 }
802             }
803
804           /* Terminate the line for any case.  */
805           buffer[buflen - 1] = '\0';
806
807           /* Skip leading blanks.  */
808           while (isspace (*p))
809             ++p;
810         }
811       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
812       /* Parse the line.  If it is invalid, loop to
813          get the next line of the file to parse.  */
814              !(parse_res = _nss_files_parse_grent (p, result, data, buflen,
815                                                    errnop)));
816
817       if (parse_res == -1)
818         {
819           /* The parser ran out of space.  */
820           fsetpos (ent->stream, &pos);
821           *errnop = ERANGE;
822           return NSS_STATUS_TRYAGAIN;
823         }
824
825       /* This is a real entry.  */
826       if (result->gr_name[0] != '+' && result->gr_name[0] != '-')
827         {
828           if (result->gr_gid == gid)
829             return NSS_STATUS_SUCCESS;
830           else
831             continue;
832         }
833
834       /* -group */
835       if (result->gr_name[0] == '-' && result->gr_name[1] != '\0')
836         {
837           blacklist_store_name (&result->gr_name[1], ent);
838           continue;
839         }
840
841       /* +group */
842       if (result->gr_name[0] == '+' && result->gr_name[1] != '\0')
843         {
844           enum nss_status status;
845
846           /* Store the group in the blacklist for the "+" at the end of
847              /etc/group */
848           blacklist_store_name (&result->gr_name[1], ent);
849           status = getgrnam_plusgroup (&result->gr_name[1], result, buffer,
850                                       buflen, errnop);
851           if (status == NSS_STATUS_SUCCESS && result->gr_gid == gid)
852             break;
853           else
854             continue;
855         }
856       /* +:... */
857       if (result->gr_name[0] == '+' && result->gr_name[1] == '\0')
858         {
859           enum nss_status status;
860
861           status = getgrgid_plusgroup (gid, result, buffer, buflen, errnop);
862           if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
863             return NSS_STATUS_NOTFOUND;
864           else
865             return status;
866         }
867     }
868
869   return NSS_STATUS_SUCCESS;
870 }
871
872 enum nss_status
873 _nss_compat_getgrgid_r (gid_t gid, struct group *grp,
874                         char *buffer, size_t buflen, int *errnop)
875 {
876   ent_t ent = {0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0}};
877   enum nss_status status;
878
879   __libc_lock_lock (lock);
880
881   status = internal_setgrent (&ent);
882
883   __libc_lock_unlock (lock);
884
885   if (status != NSS_STATUS_SUCCESS)
886     return status;
887
888   status = internal_getgrgid_r (gid, grp, &ent, buffer, buflen, errnop);
889
890   internal_endgrent (&ent);
891
892   return status;
893 }
894
895
896 /* Support routines for remembering -@netgroup and -user entries.
897    The names are stored in a single string with `|' as separator. */
898 static void
899 blacklist_store_name (const char *name, ent_t *ent)
900 {
901   int namelen = strlen (name);
902   char *tmp;
903
904   /* first call, setup cache */
905   if (ent->blacklist.size == 0)
906     {
907       ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
908       ent->blacklist.data = malloc (ent->blacklist.size);
909       if (ent->blacklist.data == NULL)
910         return;
911       ent->blacklist.data[0] = '|';
912       ent->blacklist.data[1] = '\0';
913       ent->blacklist.current = 1;
914     }
915   else
916     {
917       if (in_blacklist (name, namelen, ent))
918         return;                 /* no duplicates */
919
920       if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
921         {
922           ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
923           tmp = realloc (ent->blacklist.data, ent->blacklist.size);
924           if (tmp == NULL)
925             {
926               free (ent->blacklist.data);
927               ent->blacklist.size = 0;
928               return;
929             }
930           ent->blacklist.data = tmp;
931         }
932     }
933
934   tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
935   *tmp++ = '|';
936   *tmp = '\0';
937   ent->blacklist.current += namelen + 1;
938
939   return;
940 }
941
942 /* returns TRUE if ent->blacklist contains name, else FALSE */
943 static bool_t
944 in_blacklist (const char *name, int namelen, ent_t *ent)
945 {
946   char buf[namelen + 3];
947   char *cp;
948
949   if (ent->blacklist.data == NULL)
950     return FALSE;
951
952   buf[0] = '|';
953   cp = stpcpy (&buf[1], name);
954   *cp++= '|';
955   *cp = '\0';
956   return strstr (ent->blacklist.data, buf) != NULL;
957 }