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