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