libpwdgrp: comment out more unused functions
[oweals/busybox.git] / libpwdgrp / pwd_grp.c
1 /* vi: set sw=4 ts=4: */
2 /*  Copyright (C) 2003     Manuel Novoa III
3  *
4  *  Licensed under GPL v2, or later.  See file LICENSE in this tarball.
5  */
6
7 /*  Nov 6, 2003  Initial version.
8  *
9  *  NOTE: This implementation is quite strict about requiring all
10  *    field seperators.  It also does not allow leading whitespace
11  *    except when processing the numeric fields.  glibc is more
12  *    lenient.  See the various glibc difference comments below.
13  *
14  *  TODO:
15  *    Move to dynamic allocation of (currently statically allocated)
16  *      buffers; especially for the group-related functions since
17  *      large group member lists will cause error returns.
18  *
19  */
20
21 #include "libbb.h"
22 #include <assert.h>
23
24 #ifndef _PATH_SHADOW
25 #define _PATH_SHADOW    "/etc/shadow"
26 #endif
27 #ifndef _PATH_PASSWD
28 #define _PATH_PASSWD    "/etc/passwd"
29 #endif
30 #ifndef _PATH_GROUP
31 #define _PATH_GROUP     "/etc/group"
32 #endif
33
34 /**********************************************************************/
35 /* Sizes for statically allocated buffers. */
36
37 /* If you change these values, also change _SC_GETPW_R_SIZE_MAX and
38  * _SC_GETGR_R_SIZE_MAX in libc/unistd/sysconf.c to match */
39 #define PWD_BUFFER_SIZE 256
40 #define GRP_BUFFER_SIZE 256
41
42 /**********************************************************************/
43 /* Prototypes for internal functions. */
44
45 static int bb__pgsreader(
46                 int FAST_FUNC (*parserfunc)(void *d, char *line),
47                 void *data,
48                 char *__restrict line_buff,
49                 size_t buflen,
50                 FILE *f);
51
52 static int FAST_FUNC bb__parsepwent(void *pw, char *line);
53 static int FAST_FUNC bb__parsegrent(void *gr, char *line);
54 #if ENABLE_USE_BB_SHADOW
55 static int FAST_FUNC bb__parsespent(void *sp, char *line);
56 #endif
57
58 /**********************************************************************/
59 /* We avoid having big global data. */
60
61 struct statics {
62         /* Smaller things first */
63         struct passwd getpwuid_resultbuf;
64         struct group getgrgid_resultbuf;
65         struct passwd getpwnam_resultbuf;
66         struct group getgrnam_resultbuf;
67
68         char getpwuid_buffer[PWD_BUFFER_SIZE];
69         char getgrgid_buffer[GRP_BUFFER_SIZE];
70         char getpwnam_buffer[PWD_BUFFER_SIZE];
71         char getgrnam_buffer[GRP_BUFFER_SIZE];
72 #if 0
73         struct passwd fgetpwent_resultbuf;
74         struct group fgetgrent_resultbuf;
75         struct spwd fgetspent_resultbuf;
76         char fgetpwent_buffer[PWD_BUFFER_SIZE];
77         char fgetgrent_buffer[GRP_BUFFER_SIZE];
78         char fgetspent_buffer[PWD_BUFFER_SIZE];
79 #endif
80 #if 0 //ENABLE_USE_BB_SHADOW
81         struct spwd getspuid_resultbuf;
82         struct spwd getspnam_resultbuf;
83         char getspuid_buffer[PWD_BUFFER_SIZE];
84         char getspnam_buffer[PWD_BUFFER_SIZE];
85 #endif
86 // Not converted - too small to bother
87 //pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
88 //FILE *pwf /*= NULL*/;
89 //FILE *grf /*= NULL*/;
90 //FILE *spf /*= NULL*/;
91 #if 0
92         struct passwd getpwent_pwd;
93         struct group getgrent_gr;
94         char getpwent_line_buff[PWD_BUFFER_SIZE];
95         char getgrent_line_buff[GRP_BUFFER_SIZE];
96 #endif
97 #if 0 //ENABLE_USE_BB_SHADOW
98         struct spwd getspent_spwd;
99         struct spwd sgetspent_spwd;
100         char getspent_line_buff[PWD_BUFFER_SIZE];
101         char sgetspent_line_buff[PWD_BUFFER_SIZE];
102 #endif
103 };
104
105 static struct statics *ptr_to_statics;
106
107 static struct statics *get_S(void)
108 {
109         if (!ptr_to_statics)
110                 ptr_to_statics = xzalloc(sizeof(*ptr_to_statics));
111         return ptr_to_statics;
112 }
113
114 /* Always use in this order, get_S() must be called first */
115 #define RESULTBUF(name) &((S = get_S())->name##_resultbuf)
116 #define BUFFER(name)    (S->name##_buffer)
117
118 /**********************************************************************/
119 /* For the various fget??ent_r funcs, return
120  *
121  *  0: success
122  *  ENOENT: end-of-file encountered
123  *  ERANGE: buflen too small
124  *  other error values possible. See bb__pgsreader.
125  *
126  * Also, *result == resultbuf on success and NULL on failure.
127  *
128  * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
129  *   We do not, as it really isn't an error if we reach the end-of-file.
130  *   Doing so is analogous to having fgetc() set errno on EOF.
131  */
132 /**********************************************************************/
133
134 int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf,
135                                 char *__restrict buffer, size_t buflen,
136                                 struct passwd **__restrict result)
137 {
138         int rv;
139
140         *result = NULL;
141
142         rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream);
143         if (!rv) {
144                 *result = resultbuf;
145         }
146
147         return rv;
148 }
149
150 int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf,
151                                 char *__restrict buffer, size_t buflen,
152                                 struct group **__restrict result)
153 {
154         int rv;
155
156         *result = NULL;
157
158         rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream);
159         if (!rv) {
160                 *result = resultbuf;
161         }
162
163         return rv;
164 }
165
166 #if ENABLE_USE_BB_SHADOW
167 #ifdef UNUSED_FOR_NOW
168 int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
169                                 char *__restrict buffer, size_t buflen,
170                                 struct spwd **__restrict result)
171 {
172         int rv;
173
174         *result = NULL;
175
176         rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream);
177         if (!rv) {
178                 *result = resultbuf;
179         }
180
181         return rv;
182 }
183 #endif
184 #endif
185
186 /**********************************************************************/
187 /* For the various fget??ent funcs, return NULL on failure and a
188  * pointer to the appropriate struct (statically allocated) on success.
189  * TODO: audit & stop using these in bbox, they pull in static buffers */
190 /**********************************************************************/
191
192 #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
193 struct passwd *fgetpwent(FILE *stream)
194 {
195         struct statics *S;
196         struct passwd *resultbuf = RESULTBUF(fgetpwent);
197         char *buffer = BUFFER(fgetpwent);
198         struct passwd *result;
199
200         fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetpwent)), &result);
201         return result;
202 }
203
204 struct group *fgetgrent(FILE *stream)
205 {
206         struct statics *S;
207         struct group *resultbuf = RESULTBUF(fgetgrent);
208         char *buffer = BUFFER(fgetgrent);
209         struct group *result;
210
211         fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetgrent)), &result);
212         return result;
213 }
214 #endif
215
216 #if ENABLE_USE_BB_SHADOW
217 #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
218 struct spwd *fgetspent(FILE *stream)
219 {
220         struct statics *S;
221         struct spwd *resultbuf = RESULTBUF(fgetspent);
222         char *buffer = BUFFER(fgetspent);
223         struct spwd *result;
224
225         fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetspent)), &result);
226         return result;
227 }
228 #endif
229
230 #ifdef UNUSED_FOR_NOW
231 int sgetspent_r(const char *string, struct spwd *result_buf,
232                                 char *buffer, size_t buflen, struct spwd **result)
233 {
234         int rv = ERANGE;
235
236         *result = NULL;
237
238         if (buflen < PWD_BUFFER_SIZE) {
239  DO_ERANGE:
240                 errno = rv;
241                 goto DONE;
242         }
243
244         if (string != buffer) {
245                 if (strlen(string) >= buflen) {
246                         goto DO_ERANGE;
247                 }
248                 strcpy(buffer, string);
249         }
250
251         rv = bb__parsespent(result_buf, buffer);
252         if (!rv) {
253                 *result = result_buf;
254         }
255
256  DONE:
257         return rv;
258 }
259 #endif
260 #endif /* ENABLE_USE_BB_SHADOW */
261
262 /**********************************************************************/
263
264 #define GETXXKEY_R_FUNC         getpwnam_r
265 #define GETXXKEY_R_PARSER       bb__parsepwent
266 #define GETXXKEY_R_ENTTYPE      struct passwd
267 #define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->pw_name, key))
268 #define GETXXKEY_R_KEYTYPE      const char *__restrict
269 #define GETXXKEY_R_PATHNAME     _PATH_PASSWD
270 #include "pwd_grp_internal.c"
271
272 #define GETXXKEY_R_FUNC         getgrnam_r
273 #define GETXXKEY_R_PARSER       bb__parsegrent
274 #define GETXXKEY_R_ENTTYPE      struct group
275 #define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->gr_name, key))
276 #define GETXXKEY_R_KEYTYPE      const char *__restrict
277 #define GETXXKEY_R_PATHNAME     _PATH_GROUP
278 #include "pwd_grp_internal.c"
279
280 #if ENABLE_USE_BB_SHADOW
281 #define GETXXKEY_R_FUNC         getspnam_r
282 #define GETXXKEY_R_PARSER       bb__parsespent
283 #define GETXXKEY_R_ENTTYPE      struct spwd
284 #define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->sp_namp, key))
285 #define GETXXKEY_R_KEYTYPE      const char *__restrict
286 #define GETXXKEY_R_PATHNAME     _PATH_SHADOW
287 #include "pwd_grp_internal.c"
288 #endif
289
290 #define GETXXKEY_R_FUNC         getpwuid_r
291 #define GETXXKEY_R_PARSER       bb__parsepwent
292 #define GETXXKEY_R_ENTTYPE      struct passwd
293 #define GETXXKEY_R_TEST(ENT)    ((ENT)->pw_uid == key)
294 #define GETXXKEY_R_KEYTYPE      uid_t
295 #define GETXXKEY_R_PATHNAME     _PATH_PASSWD
296 #include "pwd_grp_internal.c"
297
298 #define GETXXKEY_R_FUNC         getgrgid_r
299 #define GETXXKEY_R_PARSER       bb__parsegrent
300 #define GETXXKEY_R_ENTTYPE      struct group
301 #define GETXXKEY_R_TEST(ENT)    ((ENT)->gr_gid == key)
302 #define GETXXKEY_R_KEYTYPE      gid_t
303 #define GETXXKEY_R_PATHNAME     _PATH_GROUP
304 #include "pwd_grp_internal.c"
305
306 /**********************************************************************/
307 /* TODO: audit & stop using these in bbox, they pull in static buffers */
308
309 /* This one has many users */
310 struct passwd *getpwuid(uid_t uid)
311 {
312         struct statics *S;
313         struct passwd *resultbuf = RESULTBUF(getpwuid);
314         char *buffer = BUFFER(getpwuid);
315         struct passwd *result;
316
317         getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpwuid)), &result);
318         return result;
319 }
320
321 /* This one has many users */
322 struct group *getgrgid(gid_t gid)
323 {
324         struct statics *S;
325         struct group *resultbuf = RESULTBUF(getgrgid);
326         char *buffer = BUFFER(getgrgid);
327         struct group *result;
328
329         getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgrgid)), &result);
330         return result;
331 }
332
333 #if 0 //ENABLE_USE_BB_SHADOW
334 /* This function is non-standard and is currently not built.  It seems
335  * to have been created as a reentrant version of the non-standard
336  * functions getspuid.  Why getspuid was added, I do not know. */
337 int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
338                        char *__restrict buffer, size_t buflen,
339                        struct spwd **__restrict result)
340 {
341         int rv;
342         struct passwd *pp;
343         struct passwd password;
344         char pwd_buff[PWD_BUFFER_SIZE];
345
346         *result = NULL;
347         rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp);
348         if (!rv) {
349                 rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result);
350         }
351
352         return rv;
353 }
354
355 /* This function is non-standard and is currently not built.
356  * Why it was added, I do not know. */
357 struct spwd *getspuid(uid_t uid)
358 {
359         struct statics *S;
360         struct spwd *resultbuf = RESULTBUF(getspuid);
361         char *buffer = BUFFER(getspuid);
362         struct spwd *result;
363
364         getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getspuid)), &result);
365         return result;
366 }
367 #endif
368
369 /* This one has many users */
370 struct passwd *getpwnam(const char *name)
371 {
372         struct statics *S;
373         struct passwd *resultbuf = RESULTBUF(getpwnam);
374         char *buffer = BUFFER(getpwnam);
375         struct passwd *result;
376
377         getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpwnam)), &result);
378         return result;
379 }
380
381 /* This one has many users */
382 struct group *getgrnam(const char *name)
383 {
384         struct statics *S;
385         struct group *resultbuf = RESULTBUF(getgrnam);
386         char *buffer = BUFFER(getgrnam);
387         struct group *result;
388
389         getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgrnam)), &result);
390         return result;
391 }
392
393 #if 0 //ENABLE_USE_BB_SHADOW
394 struct spwd *getspnam(const char *name)
395 {
396         struct statics *S;
397         struct spwd *resultbuf = RESULTBUF(getspnam);
398         char *buffer = BUFFER(getspnam);
399         struct spwd *result;
400
401         getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getspnam)), &result);
402         return result;
403 }
404 #endif
405
406 /**********************************************************************/
407
408 /* FIXME: we don't have such CONFIG_xx - ?! */
409
410 #if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER
411 static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
412 # define LOCK           pthread_mutex_lock(&mylock)
413 # define UNLOCK         pthread_mutex_unlock(&mylock);
414 #else
415 # define LOCK           ((void) 0)
416 # define UNLOCK         ((void) 0)
417 #endif
418
419 static FILE *pwf /*= NULL*/;
420 void setpwent(void)
421 {
422         LOCK;
423         if (pwf) {
424                 rewind(pwf);
425         }
426         UNLOCK;
427 }
428
429 void endpwent(void)
430 {
431         LOCK;
432         if (pwf) {
433                 fclose(pwf);
434                 pwf = NULL;
435         }
436         UNLOCK;
437 }
438
439
440 int getpwent_r(struct passwd *__restrict resultbuf,
441                            char *__restrict buffer, size_t buflen,
442                            struct passwd **__restrict result)
443 {
444         int rv;
445
446         LOCK;
447         *result = NULL;                         /* In case of error... */
448
449         if (!pwf) {
450                 pwf = fopen_for_read(_PATH_PASSWD);
451                 if (!pwf) {
452                         rv = errno;
453                         goto ERR;
454                 }
455         }
456
457         rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf);
458         if (!rv) {
459                 *result = resultbuf;
460         }
461
462  ERR:
463         UNLOCK;
464         return rv;
465 }
466
467 static FILE *grf /*= NULL*/;
468 void setgrent(void)
469 {
470         LOCK;
471         if (grf) {
472                 rewind(grf);
473         }
474         UNLOCK;
475 }
476
477 void endgrent(void)
478 {
479         LOCK;
480         if (grf) {
481                 fclose(grf);
482                 grf = NULL;
483         }
484         UNLOCK;
485 }
486
487 int getgrent_r(struct group *__restrict resultbuf,
488                            char *__restrict buffer, size_t buflen,
489                            struct group **__restrict result)
490 {
491         int rv;
492
493         LOCK;
494         *result = NULL;                         /* In case of error... */
495
496         if (!grf) {
497                 grf = fopen_for_read(_PATH_GROUP);
498                 if (!grf) {
499                         rv = errno;
500                         goto ERR;
501                 }
502         }
503
504         rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf);
505         if (!rv) {
506                 *result = resultbuf;
507         }
508
509  ERR:
510         UNLOCK;
511         return rv;
512 }
513
514 #ifdef UNUSED_FOR_NOW
515 #if ENABLE_USE_BB_SHADOW
516 static FILE *spf /*= NULL*/;
517 void setspent(void)
518 {
519         LOCK;
520         if (spf) {
521                 rewind(spf);
522         }
523         UNLOCK;
524 }
525
526 void endspent(void)
527 {
528         LOCK;
529         if (spf) {
530                 fclose(spf);
531                 spf = NULL;
532         }
533         UNLOCK;
534 }
535
536 int getspent_r(struct spwd *resultbuf, char *buffer,
537                            size_t buflen, struct spwd **result)
538 {
539         int rv;
540
541         LOCK;
542         *result = NULL;                         /* In case of error... */
543
544         if (!spf) {
545                 spf = fopen_for_read(_PATH_SHADOW);
546                 if (!spf) {
547                         rv = errno;
548                         goto ERR;
549                 }
550         }
551
552         rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf);
553         if (!rv) {
554                 *result = resultbuf;
555         }
556
557  ERR:
558         UNLOCK;
559         return rv;
560 }
561 #endif
562 #endif /* UNUSED_FOR_NOW */
563
564 #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
565 struct passwd *getpwent(void)
566 {
567         static char line_buff[PWD_BUFFER_SIZE];
568         static struct passwd pwd;
569         struct passwd *result;
570
571         getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
572         return result;
573 }
574
575 struct group *getgrent(void)
576 {
577         static char line_buff[GRP_BUFFER_SIZE];
578         static struct group gr;
579         struct group *result;
580
581         getgrent_r(&gr, line_buff, sizeof(line_buff), &result);
582         return result;
583 }
584
585 #if ENABLE_USE_BB_SHADOW
586 struct spwd *getspent(void)
587 {
588         static char line_buff[PWD_BUFFER_SIZE];
589         static struct spwd spwd;
590         struct spwd *result;
591
592         getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
593         return result;
594 }
595
596 struct spwd *sgetspent(const char *string)
597 {
598         static char line_buff[PWD_BUFFER_SIZE];
599         static struct spwd spwd;
600         struct spwd *result;
601
602         sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
603         return result;
604 }
605 #endif
606 #endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */
607
608 static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid)
609 {
610         FILE *grfile;
611         gid_t *group_list;
612         int ngroups;
613         struct group group;
614         char buff[PWD_BUFFER_SIZE];
615
616         /* We alloc space for 8 gids at a time. */
617         group_list = xmalloc(8 * sizeof(group_list[0]));
618         group_list[0] = gid;
619         ngroups = 1;
620
621         grfile = fopen_for_read(_PATH_GROUP);
622         if (grfile) {
623                 while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) {
624                         char **m;
625                         assert(group.gr_mem); /* Must have at least a NULL terminator. */
626                         if (group.gr_gid == gid)
627                                 continue;
628                         for (m = group.gr_mem; *m; m++) {
629                                 if (strcmp(*m, user) != 0)
630                                         continue;
631                                 group_list = xrealloc_vector(group_list, /*8=2^3:*/ 3, ngroups);
632                                 group_list[ngroups++] = group.gr_gid;
633                                 break;
634                         }
635                 }
636                 fclose(grfile);
637         }
638         *ngroups_ptr = ngroups;
639         return group_list;
640 }
641
642 int initgroups(const char *user, gid_t gid)
643 {
644         int ngroups;
645         gid_t *group_list = getgrouplist_internal(&ngroups, user, gid);
646
647         ngroups = setgroups(ngroups, group_list);
648         free(group_list);
649         return ngroups;
650 }
651
652 int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
653 {
654         int ngroups_old = *ngroups;
655         gid_t *group_list = getgrouplist_internal(ngroups, user, gid);
656
657         if (*ngroups <= ngroups_old) {
658                 ngroups_old = *ngroups;
659                 memcpy(groups, group_list, ngroups_old * sizeof(groups[0]));
660         } else {
661                 ngroups_old = -1;
662         }
663         free(group_list);
664         return ngroups_old;
665 }
666
667 #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
668 int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
669 {
670         int rv = -1;
671
672 #if 0
673         /* glibc does this check */
674         if (!p || !f) {
675                 errno = EINVAL;
676                 return rv;
677         }
678 #endif
679
680         /* No extra thread locking is needed above what fprintf does. */
681         if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
682                                 p->pw_name, p->pw_passwd,
683                                 (unsigned long)(p->pw_uid),
684                                 (unsigned long)(p->pw_gid),
685                                 p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
686                 ) {
687                 rv = 0;
688         }
689
690         return rv;
691 }
692
693 int putgrent(const struct group *__restrict p, FILE *__restrict f)
694 {
695         int rv = -1;
696
697 #if 0
698         /* glibc does this check */
699         if (!p || !f) {
700                 errno = EINVAL;
701                 return rv;
702         }
703 #endif
704
705         if (fprintf(f, "%s:%s:%lu:",
706                                 p->gr_name, p->gr_passwd,
707                                 (unsigned long)(p->gr_gid)) >= 0
708         ) {
709                 static const char format[] ALIGN1 = ",%s";
710
711                 char **m;
712                 const char *fmt;
713
714                 fmt = format + 1;
715
716                 assert(p->gr_mem);
717                 m = p->gr_mem;
718
719                 while (1) {
720                         if (!*m) {
721                                 if (fputc('\n', f) >= 0) {
722                                         rv = 0;
723                                 }
724                                 break;
725                         }
726                         if (fprintf(f, fmt, *m) < 0) {
727                                 break;
728                         }
729                         m++;
730                         fmt = format;
731                 }
732         }
733
734         return rv;
735 }
736 #endif
737
738 #if ENABLE_USE_BB_SHADOW
739 static const unsigned char put_sp_off[] ALIGN1 = {
740         offsetof(struct spwd, sp_lstchg),       /* 2 - not a char ptr */
741         offsetof(struct spwd, sp_min),          /* 3 - not a char ptr */
742         offsetof(struct spwd, sp_max),          /* 4 - not a char ptr */
743         offsetof(struct spwd, sp_warn),         /* 5 - not a char ptr */
744         offsetof(struct spwd, sp_inact),        /* 6 - not a char ptr */
745         offsetof(struct spwd, sp_expire)        /* 7 - not a char ptr */
746 };
747
748 #ifdef UNUSED_FOR_NOW
749 int putspent(const struct spwd *p, FILE *stream)
750 {
751         const char *fmt;
752         long x;
753         int i;
754         int rv = -1;
755
756         /* Unlike putpwent and putgrent, glibc does not check the args. */
757         if (fprintf(stream, "%s:%s:", p->sp_namp,
758                                 (p->sp_pwdp ? p->sp_pwdp : "")) < 0
759         ) {
760                 goto DO_UNLOCK;
761         }
762
763         for (i = 0; i < sizeof(put_sp_off); i++) {
764                 fmt = "%ld:";
765                 x = *(long *)((char *)p + put_sp_off[i]);
766                 if (x == -1) {
767                         fmt += 3;
768                 }
769                 if (fprintf(stream, fmt, x) < 0) {
770                         goto DO_UNLOCK;
771                 }
772         }
773
774         if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
775                 goto DO_UNLOCK;
776         }
777
778         if (fputc('\n', stream) > 0) {
779                 rv = 0;
780         }
781
782  DO_UNLOCK:
783         return rv;
784 }
785 #endif
786 #endif /* USE_BB_SHADOW */
787
788 /**********************************************************************/
789 /* Internal functions                                                 */
790 /**********************************************************************/
791
792 static const unsigned char pw_off[] ALIGN1 = {
793         offsetof(struct passwd, pw_name),       /* 0 */
794         offsetof(struct passwd, pw_passwd),     /* 1 */
795         offsetof(struct passwd, pw_uid),        /* 2 - not a char ptr */
796         offsetof(struct passwd, pw_gid),        /* 3 - not a char ptr */
797         offsetof(struct passwd, pw_gecos),      /* 4 */
798         offsetof(struct passwd, pw_dir),        /* 5 */
799         offsetof(struct passwd, pw_shell)       /* 6 */
800 };
801
802 static int FAST_FUNC bb__parsepwent(void *data, char *line)
803 {
804         char *endptr;
805         char *p;
806         int i;
807
808         i = 0;
809         while (1) {
810                 p = (char *) data + pw_off[i];
811
812                 if (i < 2 || i > 3) {
813                         *((char **) p) = line;
814                         if (i == 6) {
815                                 return 0;
816                         }
817                         /* NOTE: glibc difference - glibc allows omission of
818                          * ':' seperators after the gid field if all remaining
819                          * entries are empty.  We require all separators. */
820                         line = strchr(line, ':');
821                         if (!line) {
822                                 break;
823                         }
824                 } else {
825                         unsigned long t = strtoul(line, &endptr, 10);
826                         /* Make sure we had at least one digit, and that the
827                          * failing char is the next field seperator ':'.  See
828                          * glibc difference note above. */
829                         /* TODO: Also check for leading whitespace? */
830                         if ((endptr == line) || (*endptr != ':')) {
831                                 break;
832                         }
833                         line = endptr;
834                         if (i & 1) {            /* i == 3 -- gid */
835                                 *((gid_t *) p) = t;
836                         } else {                        /* i == 2 -- uid */
837                                 *((uid_t *) p) = t;
838                         }
839                 }
840
841                 *line++ = '\0';
842                 i++;
843         } /* while (1) */
844
845         return -1;
846 }
847
848 /**********************************************************************/
849
850 static const unsigned char gr_off[] ALIGN1 = {
851         offsetof(struct group, gr_name),        /* 0 */
852         offsetof(struct group, gr_passwd),      /* 1 */
853         offsetof(struct group, gr_gid)          /* 2 - not a char ptr */
854 };
855
856 static int FAST_FUNC bb__parsegrent(void *data, char *line)
857 {
858         char *endptr;
859         char *p;
860         int i;
861         char **members;
862         char *end_of_buf;
863
864         end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
865         i = 0;
866         while (1) {
867                 p = (char *) data + gr_off[i];
868
869                 if (i < 2) {
870                         *((char **) p) = line;
871                         line = strchr(line, ':');
872                         if (!line) {
873                                 break;
874                         }
875                         *line++ = '\0';
876                         i++;
877                 } else {
878                         *((gid_t *) p) = strtoul(line, &endptr, 10);
879
880                         /* NOTE: glibc difference - glibc allows omission of the
881                          * trailing colon when there is no member list.  We treat
882                          * this as an error. */
883
884                         /* Make sure we had at least one digit, and that the
885                          * failing char is the next field seperator ':'.  See
886                          * glibc difference note above. */
887                         if ((endptr == line) || (*endptr != ':')) {
888                                 break;
889                         }
890
891                         i = 1;                          /* Count terminating NULL ptr. */
892                         p = endptr;
893
894                         if (p[1]) { /* We have a member list to process. */
895                                 /* Overwrite the last ':' with a ',' before counting.
896                                  * This allows us to (1) test for initial ','
897                                  * and (2) adds one ',' so that the number of commas
898                                  * equals the member count. */
899                                 *p = ',';
900                                 do {
901                                         /* NOTE: glibc difference - glibc allows and trims leading
902                                          * (but not trailing) space.  We treat this as an error. */
903                                         /* NOTE: glibc difference - glibc allows consecutive and
904                                          * trailing commas, and ignores "empty string" users.  We
905                                          * treat this as an error. */
906                                         if (*p == ',') {
907                                                 ++i;
908                                                 *p = 0; /* nul-terminate each member string. */
909                                                 if (!*++p || (*p == ',') || isspace(*p)) {
910                                                         goto ERR;
911                                                 }
912                                         }
913                                 } while (*++p);
914                         }
915
916                         /* Now align (p+1), rounding up. */
917                         /* Assumes sizeof(char **) is a power of 2. */
918                         members = (char **)( (((intptr_t) p) + sizeof(char **))
919                                                                  & ~((intptr_t)(sizeof(char **) - 1)) );
920
921                         if (((char *)(members + i)) > end_of_buf) {     /* No space. */
922                                 break;
923                         }
924
925                         ((struct group *) data)->gr_mem = members;
926
927                         if (--i) {
928                                 p = endptr;     /* Pointing to char prior to first member. */
929                                 while (1) {
930                                         *members++ = ++p;
931                                         if (!--i)
932                                                 break;
933                                         while (*++p)
934                                                 continue;
935                                 }
936                         }
937                         *members = NULL;
938
939                         return 0;
940                 }
941         } /* while (1) */
942
943  ERR:
944         return -1;
945 }
946
947 /**********************************************************************/
948
949 #if ENABLE_USE_BB_SHADOW
950 static const unsigned char sp_off[] ALIGN1 = {
951         offsetof(struct spwd, sp_namp),         /* 0: char* */
952         offsetof(struct spwd, sp_pwdp),         /* 1: char* */
953         offsetof(struct spwd, sp_lstchg),       /* 2: long */
954         offsetof(struct spwd, sp_min),          /* 3: long */
955         offsetof(struct spwd, sp_max),          /* 4: long */
956         offsetof(struct spwd, sp_warn),         /* 5: long */
957         offsetof(struct spwd, sp_inact),        /* 6: long */
958         offsetof(struct spwd, sp_expire),       /* 7: long */
959         offsetof(struct spwd, sp_flag)          /* 8: unsigned long */
960 };
961
962 static int FAST_FUNC bb__parsespent(void *data, char *line)
963 {
964         char *endptr;
965         char *p;
966         int i;
967
968         i = 0;
969         while (1) {
970                 p = (char *) data + sp_off[i];
971                 if (i < 2) {
972                         *((char **) p) = line;
973                         line = strchr(line, ':');
974                         if (!line) {
975                                 break; /* error */
976                         }
977                 } else {
978                         *((long *) p) = strtoul(line, &endptr, 10);
979                         if (endptr == line) {
980                                 *((long *) p) = -1L;
981                         }
982                         line = endptr;
983                         if (i == 8) {
984                                 if (*line != '\0') {
985                                         break; /* error */
986                                 }
987                                 return 0; /* all ok */
988                         }
989                         if (*line != ':') {
990                                 break; /* error */
991                         }
992                 }
993                 *line++ = '\0';
994                 i++;
995         }
996
997         return EINVAL;
998 }
999 #endif
1000
1001 /**********************************************************************/
1002
1003 /* Reads until if EOF, or until if finds a line which fits in the buffer
1004  * and for which the parser function succeeds.
1005  *
1006  * Returns 0 on success and ENOENT for end-of-file (glibc concession).
1007  */
1008 static int bb__pgsreader(
1009                 int FAST_FUNC (*parserfunc)(void *d, char *line),
1010                 void *data,
1011                 char *__restrict line_buff,
1012                 size_t buflen,
1013                 FILE *f)
1014 {
1015         int skip;
1016         int rv = ERANGE;
1017
1018         if (buflen < PWD_BUFFER_SIZE) {
1019                 errno = rv;
1020                 return rv;
1021         }
1022
1023         skip = 0;
1024         while (1) {
1025                 if (!fgets(line_buff, buflen, f)) {
1026                         if (feof(f)) {
1027                                 rv = ENOENT;
1028                         }
1029                         break;
1030                 }
1031
1032                 {
1033                         int line_len = strlen(line_buff) - 1;
1034                         if (line_len >= 0 && line_buff[line_len] == '\n') {
1035                                 line_buff[line_len] = '\0';
1036                         } else
1037                         if (line_len + 2 == buflen) {
1038                                 /* A start (or continuation) of overlong line */
1039                                 skip = 1;
1040                                 continue;
1041                         } /* else: a last line in the file, and it has no '\n' */
1042                 }
1043
1044                 if (skip) {
1045                         /* This "line" is a remainder of overlong line, ignore */
1046                         skip = 0;
1047                         continue;
1048                 }
1049
1050                 /* NOTE: glibc difference - glibc strips leading whitespace from
1051                  * records.  We do not allow leading whitespace. */
1052
1053                 /* Skip empty lines, comment lines, and lines with leading
1054                  * whitespace. */
1055                 if (line_buff[0] != '\0' && line_buff[0] != '#' && !isspace(line_buff[0])) {
1056                         if (parserfunc == bb__parsegrent) {
1057                                 /* Do evil group hack:
1058                                  * The group entry parsing function needs to know where
1059                                  * the end of the buffer is so that it can construct the
1060                                  * group member ptr table. */
1061                                 ((struct group *) data)->gr_name = line_buff + buflen;
1062                         }
1063                         if (parserfunc(data, line_buff) == 0) {
1064                                 rv = 0;
1065                                 break;
1066                         }
1067                 }
1068         } /* while (1) */
1069
1070         return rv;
1071 }