aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTito Ragusa <farmatito@tiscali.it>2015-01-02 21:37:59 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2015-01-02 21:37:59 +0100
commit1da09cfacf1c4789cc74322857a098c2ddb06e31 (patch)
tree0121bddf6ed1d159ca76f2b350832e400bf83c26
parent78854520ebecfd24d5c80a266d6779bd1e069016 (diff)
downloadbusybox-w32-1da09cfacf1c4789cc74322857a098c2ddb06e31.tar.gz
busybox-w32-1da09cfacf1c4789cc74322857a098c2ddb06e31.tar.bz2
busybox-w32-1da09cfacf1c4789cc74322857a098c2ddb06e31.zip
libpwdgrp: rewritten to use malloced implementation
This removed buffer size limitations. function old new delta convert_to_struct - 269 +269 getXXnam_r - 204 +204 parse_common - 185 +185 getXXnam - 164 +164 tokenize - 126 +126 bb_internal_getpwent_r 102 167 +65 get_S 30 88 +58 getgrouplist_internal 195 240 +45 const_sp_db - 20 +20 const_pw_db - 20 +20 const_gr_db - 20 +20 bb_internal_endpwent 27 36 +9 bb_internal_endgrent 27 36 +9 decode_one_format 726 734 +8 bb_internal_setpwent 17 24 +7 volume_id_probe_iso9660 319 322 +3 scriptreplay_main 204 207 +3 mkfs_minix_main 2684 2687 +3 id_main 478 480 +2 hash_find 233 235 +2 pstree_main 321 322 +1 gr_off 3 4 +1 expand_one_var 1579 1578 -1 pwf 4 - -4 grf 4 - -4 pack_gzip 1787 1780 -7 addattr32 67 56 -11 buffer_fill_and_print 191 178 -13 dpkg_main 2944 2927 -17 bb_internal_setgrent 17 - -17 bb_internal_getpwuid 38 19 -19 bb_internal_getgrgid 44 19 -25 bb_internal_getpwnam 38 11 -27 bb_internal_getgrnam 44 14 -30 bb_internal_fgetpwent_r 51 - -51 bb_internal_fgetgrent_r 51 - -51 bb_internal_getspnam_r 121 42 -79 bb_internal_getpwnam_r 121 39 -82 bb_internal_getgrent_r 102 - -102 bb__parsepwent 110 - -110 bb_internal_getpwuid_r 113 - -113 bb_internal_getgrgid_r 113 - -113 bb__parsespent 120 - -120 bb_internal_getgrnam_r 121 - -121 bb__pgsreader 213 - -213 bb__parsegrent 226 - -226 ------------------------------------------------------------------------------ (add/remove: 8/13 grow/shrink: 14/11 up/down: 1224/-1556) Total: -332 bytes text data bss dec hex filename 923471 928 17684 942083 e6003 busybox_old 923167 928 17676 941771 e5ecb busybox_unstripped Signed-off-by: Tito Ragusa <farmatito@tiscali.it> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--include/grp_.h54
-rw-r--r--include/pwd_.h25
-rw-r--r--libbb/bb_pwd.c48
-rw-r--r--libpwdgrp/pwd_grp.c1238
-rw-r--r--libpwdgrp/pwd_grp_internal.c61
5 files changed, 359 insertions, 1067 deletions
diff --git a/include/grp_.h b/include/grp_.h
index e5075e5a0..f7b8d836f 100644
--- a/include/grp_.h
+++ b/include/grp_.h
@@ -30,17 +30,9 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
30 * so that function calls are directed to bb_internal_XXX replacements 30 * so that function calls are directed to bb_internal_XXX replacements
31 */ 31 */
32#undef endgrent 32#undef endgrent
33#define setgrent bb_internal_setgrent
34#define endgrent bb_internal_endgrent 33#define endgrent bb_internal_endgrent
35#define getgrent bb_internal_getgrent
36#define fgetgrent bb_internal_fgetgrent
37#define putgrent bb_internal_putgrent
38#define getgrgid bb_internal_getgrgid 34#define getgrgid bb_internal_getgrgid
39#define getgrnam bb_internal_getgrnam 35#define getgrnam bb_internal_getgrnam
40#define getgrent_r bb_internal_getgrent_r
41#define getgrgid_r bb_internal_getgrgid_r
42#define getgrnam_r bb_internal_getgrnam_r
43#define fgetgrent_r bb_internal_fgetgrent_r
44#define getgrouplist bb_internal_getgrouplist 36#define getgrouplist bb_internal_getgrouplist
45#define initgroups bb_internal_initgroups 37#define initgroups bb_internal_initgroups
46 38
@@ -48,60 +40,16 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
48/* All function names below should be remapped by #defines above 40/* All function names below should be remapped by #defines above
49 * in order to not collide with libc names. */ 41 * in order to not collide with libc names. */
50 42
51
52/* Rewind the group-file stream. */
53extern void setgrent(void);
54
55/* Close the group-file stream. */ 43/* Close the group-file stream. */
56extern void endgrent(void); 44extern void endgrent(void);
57 45
58#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
59/* Read an entry from the group-file stream, opening it if necessary. */
60extern struct group *getgrent(void);
61
62/* Read a group entry from STREAM. */
63extern struct group *fgetgrent(FILE *__stream);
64
65/* Write the given entry onto the given stream. */
66extern int putgrent(const struct group *__restrict __p,
67 FILE *__restrict __f);
68#endif
69
70/* Search for an entry with a matching group ID. */ 46/* Search for an entry with a matching group ID. */
71extern struct group *getgrgid(gid_t __gid); 47extern struct group *getgrgid(gid_t __gid);
72 48
73/* Search for an entry with a matching group name. */ 49/* Search for an entry with a matching group name. */
74extern struct group *getgrnam(const char *__name); 50extern struct group *getgrnam(const char *__name);
75 51
76/* Reentrant versions of some of the functions above. 52/* Reentrant versions of some of the functions above. */
77
78 PLEASE NOTE: the `getgrent_r' function is not (yet) standardized.
79 The interface may change in later versions of this library. But
80 the interface is designed following the principals used for the
81 other reentrant functions so the chances are good this is what the
82 POSIX people would choose. */
83
84extern int getgrent_r(struct group *__restrict __resultbuf,
85 char *__restrict __buffer, size_t __buflen,
86 struct group **__restrict __result);
87
88/* Search for an entry with a matching group ID. */
89extern int getgrgid_r(gid_t __gid, struct group *__restrict __resultbuf,
90 char *__restrict __buffer, size_t __buflen,
91 struct group **__restrict __result);
92
93/* Search for an entry with a matching group name. */
94extern int getgrnam_r(const char *__restrict __name,
95 struct group *__restrict __resultbuf,
96 char *__restrict __buffer, size_t __buflen,
97 struct group **__restrict __result);
98
99/* Read a group entry from STREAM. This function is not standardized
100 an probably never will. */
101extern int fgetgrent_r(FILE *__restrict __stream,
102 struct group *__restrict __resultbuf,
103 char *__restrict __buffer, size_t __buflen,
104 struct group **__restrict __result);
105 53
106/* Store at most *NGROUPS members of the group set for USER into 54/* Store at most *NGROUPS members of the group set for USER into
107 *GROUPS. Also include GROUP. The actual number of groups found is 55 *GROUPS. Also include GROUP. The actual number of groups found is
diff --git a/include/pwd_.h b/include/pwd_.h
index 625b6f5a2..d086f86e3 100644
--- a/include/pwd_.h
+++ b/include/pwd_.h
@@ -34,20 +34,14 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
34#define setpwent bb_internal_setpwent 34#define setpwent bb_internal_setpwent
35#define endpwent bb_internal_endpwent 35#define endpwent bb_internal_endpwent
36#define getpwent bb_internal_getpwent 36#define getpwent bb_internal_getpwent
37#define fgetpwent bb_internal_fgetpwent
38#define putpwent bb_internal_putpwent
39#define getpwuid bb_internal_getpwuid 37#define getpwuid bb_internal_getpwuid
40#define getpwnam bb_internal_getpwnam 38#define getpwnam bb_internal_getpwnam
41#define getpwent_r bb_internal_getpwent_r 39#define getpwent_r bb_internal_getpwent_r
42#define getpwuid_r bb_internal_getpwuid_r
43#define getpwnam_r bb_internal_getpwnam_r 40#define getpwnam_r bb_internal_getpwnam_r
44#define fgetpwent_r bb_internal_fgetpwent_r
45
46 41
47/* All function names below should be remapped by #defines above 42/* All function names below should be remapped by #defines above
48 * in order to not collide with libc names. */ 43 * in order to not collide with libc names. */
49 44
50
51/* Rewind the password-file stream. */ 45/* Rewind the password-file stream. */
52extern void setpwent(void); 46extern void setpwent(void);
53 47
@@ -57,13 +51,6 @@ extern void endpwent(void);
57#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS 51#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
58/* Read an entry from the password-file stream, opening it if necessary. */ 52/* Read an entry from the password-file stream, opening it if necessary. */
59extern struct passwd *getpwent(void); 53extern struct passwd *getpwent(void);
60
61/* Read an entry from STREAM. */
62extern struct passwd *fgetpwent(FILE *__stream);
63
64/* Write the given entry onto the given stream. */
65extern int putpwent(const struct passwd *__restrict __p,
66 FILE *__restrict __f);
67#endif 54#endif
68 55
69/* Search for an entry with a matching user ID. */ 56/* Search for an entry with a matching user ID. */
@@ -84,23 +71,11 @@ extern int getpwent_r(struct passwd *__restrict __resultbuf,
84 char *__restrict __buffer, size_t __buflen, 71 char *__restrict __buffer, size_t __buflen,
85 struct passwd **__restrict __result); 72 struct passwd **__restrict __result);
86 73
87extern int getpwuid_r(uid_t __uid,
88 struct passwd *__restrict __resultbuf,
89 char *__restrict __buffer, size_t __buflen,
90 struct passwd **__restrict __result);
91
92extern int getpwnam_r(const char *__restrict __name, 74extern int getpwnam_r(const char *__restrict __name,
93 struct passwd *__restrict __resultbuf, 75 struct passwd *__restrict __resultbuf,
94 char *__restrict __buffer, size_t __buflen, 76 char *__restrict __buffer, size_t __buflen,
95 struct passwd **__restrict __result); 77 struct passwd **__restrict __result);
96 78
97/* Read an entry from STREAM. This function is not standardized and
98 probably never will. */
99extern int fgetpwent_r(FILE *__restrict __stream,
100 struct passwd *__restrict __resultbuf,
101 char *__restrict __buffer, size_t __buflen,
102 struct passwd **__restrict __result);
103
104POP_SAVED_FUNCTION_VISIBILITY 79POP_SAVED_FUNCTION_VISIBILITY
105 80
106#endif 81#endif
diff --git a/libbb/bb_pwd.c b/libbb/bb_pwd.c
index 8250cd446..4829b723a 100644
--- a/libbb/bb_pwd.c
+++ b/libbb/bb_pwd.c
@@ -110,51 +110,3 @@ unsigned long FAST_FUNC get_ug_id(const char *s,
110 return xname2id(s); 110 return xname2id(s);
111 return r; 111 return r;
112} 112}
113
114/* Experimental "mallocing" API.
115 * The goal is nice: "we want to support a case when "guests" group is very large"
116 * but the code is butt-ugly.
117 */
118#if 0
119static char *find_latest(char last, char *cp)
120{
121 if (!cp)
122 return last;
123 cp += strlen(cp) + 1;
124 if (last < cp)
125 last = cp;
126 return last;
127}
128
129struct group* FAST_FUNC xmalloc_getgrnam(const char *name)
130{
131 struct {
132 struct group gr;
133 // May still be not enough!
134 char buf[64*1024 - sizeof(struct group) - 16];
135 } *s;
136 struct group *grp;
137 int r;
138 char *last;
139 char **gr_mem;
140
141 s = xmalloc(sizeof(*s));
142 r = getgrnam_r(name, &s->gr, s->buf, sizeof(s->buf), &grp);
143 if (!grp) {
144 free(s);
145 return grp;
146 }
147 last = find_latest(s->buf, grp->gr_name);
148 last = find_latest(last, grp->gr_passwd);
149 gr_mem = grp->gr_mem;
150 while (*gr_mem)
151 last = find_latest(last, *gr_mem++);
152 gr_mem++; /* points past NULL */
153 if (last < (char*)gr_mem)
154 last = (char*)gr_mem;
155//FIXME: what if we get not only truncated, but also moved here?
156// grp->gr_name pointer and friends are invalid now!!!
157 s = xrealloc(s, last - (char*)s);
158 return grp;
159}
160#endif
diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c
index 2060d7811..ed8370124 100644
--- a/libpwdgrp/pwd_grp.c
+++ b/libpwdgrp/pwd_grp.c
@@ -1,598 +1,480 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* Copyright (C) 2003 Manuel Novoa III 2/* Copyright (C) 2014 Tito Ragusa <farmatito@tiscali.it>
3 * 3 *
4 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 4 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 */ 5 */
6 6/* This program is distributed in the hope that it will be useful,
7/* Nov 6, 2003 Initial version. 7 * but WITHOUT ANY WARRANTY!!
8 * 8 *
9 * NOTE: This implementation is quite strict about requiring all 9 * Rewrite of some parts. Main differences are:
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 * 10 *
14 * TODO: 11 * 1) the buffer for getpwuid, getgrgid, getpwnam, getgrnam is dynamically
15 * Move to dynamic allocation of (currently statically allocated) 12 * allocated and reused by later calls. if ERANGE error pops up it is
16 * buffers; especially for the group-related functions since 13 * reallocated to the size of the longest line found so far in the
17 * large group member lists will cause error returns. 14 * passwd/group files and reused for later calls.
15 * If ENABLE_FEATURE_CLEAN_UP is set the buffers are freed at program
16 * exit using the atexit function to make valgrind happy.
17 * 2) the passwd/group files:
18 * a) must contain the expected number of fields (as per count of field
19 * delimeters ":") or we will complain with a error message.
20 * b) leading or trailing whitespace in fields is allowed and handled.
21 * c) some fields are not allowed to be empty (e.g. username, uid/gid,
22 * homedir, shell) and in this case NULL is returned and errno is
23 * set to EINVAL. This behaviour could be easily changed by
24 * modifying PW_DEF, GR_DEF, SP_DEF strings (uppercase
25 * makes a field mandatory).
26 * d) the string representing uid/gid must be convertible by strtoXX
27 * functions or NULL is returned and errno is set to EINVAL.
28 * e) leading or trailing whitespaces in member names and empty members
29 * are allowed and handled.
30 * 3) the internal function for getgrouplist uses a dynamically allocated
31 * buffer and retries with a bigger one in case it is too small;
32 * 4) the _r functions use the user supplied buffers that are never reallocated
33 * but use mostly the same common code as the other functions.
34 * 5) at the moment only the functions really used by busybox code are
35 * implemented, if you need a particular missing function it should be
36 * easy to write it by using the internal common code.
18 */ 37 */
19 38
20#include "libbb.h" 39#include "libbb.h"
21#include <assert.h>
22
23/**********************************************************************/
24/* Sizes for statically allocated buffers. */
25 40
26#define PWD_BUFFER_SIZE 256 41/* S = string not empty, s = string maybe empty, */
27#define GRP_BUFFER_SIZE 256 42/* I = uid,gid, l = long maybe empty, m = members,*/
28 43/* r = reserved */
29/**********************************************************************/ 44#define PW_DEF "SsIIsSS"
30/* Prototypes for internal functions. */ 45#define GR_DEF "SsIm"
46#define SP_DEF "Ssllllllr"
47
48static const uint8_t pw_off[] ALIGN1 = {
49 offsetof(struct passwd, pw_name), /* 0 S */
50 offsetof(struct passwd, pw_passwd), /* 1 s */
51 offsetof(struct passwd, pw_uid), /* 2 I */
52 offsetof(struct passwd, pw_gid), /* 3 I */
53 offsetof(struct passwd, pw_gecos), /* 4 s */
54 offsetof(struct passwd, pw_dir), /* 5 S */
55 offsetof(struct passwd, pw_shell) /* 6 S */
56};
57static const uint8_t gr_off[] ALIGN1 = {
58 offsetof(struct group, gr_name), /* 0 S */
59 offsetof(struct group, gr_passwd), /* 1 s */
60 offsetof(struct group, gr_gid), /* 2 I */
61 offsetof(struct group, gr_mem) /* 3 m (char **) */
62};
63#if ENABLE_USE_BB_SHADOW
64static const uint8_t sp_off[] ALIGN1 = {
65 offsetof(struct spwd, sp_namp), /* 0 S Login name */
66 offsetof(struct spwd, sp_pwdp), /* 1 s Encrypted password */
67 offsetof(struct spwd, sp_lstchg), /* 2 l */
68 offsetof(struct spwd, sp_min), /* 3 l */
69 offsetof(struct spwd, sp_max), /* 4 l */
70 offsetof(struct spwd, sp_warn), /* 5 l */
71 offsetof(struct spwd, sp_inact), /* 6 l */
72 offsetof(struct spwd, sp_expire), /* 7 l */
73 offsetof(struct spwd, sp_flag) /* 8 r Reserved */
74};
75#endif
31 76
32static int bb__pgsreader( 77struct const_passdb {
33 int FAST_FUNC (*parserfunc)(void *d, char *line), 78 const char *filename;
34 void *data, 79 const uint8_t *off;
35 char *__restrict line_buff, 80 const char def[10];
36 size_t buflen, 81 uint8_t numfields;
37 FILE *f); 82 uint8_t size_of;
83};
84struct passdb {
85 const char *filename;
86 const uint8_t *off;
87 const char def[10];
88 uint8_t numfields;
89 uint8_t size_of;
90 FILE *fp;
91 void *malloced;
92};
38 93
39static int FAST_FUNC bb__parsepwent(void *pw, char *line); 94static const struct const_passdb const_pw_db = { _PATH_PASSWD, pw_off, PW_DEF, sizeof(PW_DEF)-1, sizeof(struct passwd) };
40static int FAST_FUNC bb__parsegrent(void *gr, char *line); 95static const struct const_passdb const_gr_db = { _PATH_GROUP , gr_off, GR_DEF, sizeof(GR_DEF)-1, sizeof(struct group) };
41#if ENABLE_USE_BB_SHADOW 96#if ENABLE_USE_BB_SHADOW
42static int FAST_FUNC bb__parsespent(void *sp, char *line); 97static const struct const_passdb const_sp_db = { _PATH_SHADOW, sp_off, SP_DEF, sizeof(SP_DEF)-1, sizeof(struct spwd) };
43#endif 98#endif
44 99
45/**********************************************************************/
46/* We avoid having big global data. */ 100/* We avoid having big global data. */
47
48struct statics { 101struct statics {
49 /* Smaller things first */
50 /* It's ok to use one buffer for getpwuid and getpwnam. Manpage says: 102 /* It's ok to use one buffer for getpwuid and getpwnam. Manpage says:
51 * "The return value may point to a static area, and may be overwritten 103 * "The return value may point to a static area, and may be overwritten
52 * by subsequent calls to getpwent(), getpwnam(), or getpwuid()." 104 * by subsequent calls to getpwent(), getpwnam(), or getpwuid()."
53 */ 105 */
54 struct passwd getpw_resultbuf; 106 struct passdb db[2 + ENABLE_USE_BB_SHADOW];
55 struct group getgr_resultbuf; 107 char *tokenize_end;
56
57 char getpw_buffer[PWD_BUFFER_SIZE];
58 char getgr_buffer[GRP_BUFFER_SIZE];
59#if 0 //ENABLE_USE_BB_SHADOW
60 struct spwd getsp_resultbuf;
61 char getsp_buffer[PWD_BUFFER_SIZE];
62#endif
63// Not converted - too small to bother
64//pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
65//FILE *pwf /*= NULL*/;
66//FILE *grf /*= NULL*/;
67//FILE *spf /*= NULL*/;
68}; 108};
69 109
70static struct statics *ptr_to_statics; 110static struct statics *ptr_to_statics;
111#define S (*ptr_to_statics)
112#define has_S (ptr_to_statics)
71 113
72static struct statics *get_S(void) 114static struct statics *get_S(void)
73{ 115{
74 if (!ptr_to_statics) 116 if (!ptr_to_statics) {
75 ptr_to_statics = xzalloc(sizeof(*ptr_to_statics)); 117 ptr_to_statics = xzalloc(sizeof(S));
118 memcpy(&S.db[0], &const_pw_db, sizeof(const_pw_db));
119 memcpy(&S.db[1], &const_gr_db, sizeof(const_gr_db));
120#if ENABLE_USE_BB_SHADOW
121 memcpy(&S.db[2], &const_sp_db, sizeof(const_sp_db));
122#endif
123 }
76 return ptr_to_statics; 124 return ptr_to_statics;
77} 125}
78 126
79/* Always use in this order, get_S() must be called first */
80#define RESULTBUF(name) &((S = get_S())->name##_resultbuf)
81#define BUFFER(name) (S->name##_buffer)
82
83/**********************************************************************/ 127/**********************************************************************/
84/* For the various fget??ent_r funcs, return 128/* Internal functions */
85 *
86 * 0: success
87 * ENOENT: end-of-file encountered
88 * ERANGE: buflen too small
89 * other error values possible. See bb__pgsreader.
90 *
91 * Also, *result == resultbuf on success and NULL on failure.
92 *
93 * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
94 * We do not, as it really isn't an error if we reach the end-of-file.
95 * Doing so is analogous to having fgetc() set errno on EOF.
96 */
97/**********************************************************************/ 129/**********************************************************************/
98 130
99int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf, 131/* Divide the passwd/group/shadow record in fields
100 char *__restrict buffer, size_t buflen, 132 * by substituting the given delimeter
101 struct passwd **__restrict result) 133 * e.g. ':' or ',' with '\0'.
134 * Returns the number of fields found.
135 * Strips leading and trailing whitespace in fields.
136 */
137static int tokenize(char *buffer, int ch)
102{ 138{
103 int rv; 139 char *p = buffer;
140 char *s = p;
141 int num_fields = 0;
104 142
105 *result = NULL; 143 for (;;) {
106 144 if (isblank(*s)) {
107 rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream); 145 overlapping_strcpy(s, skip_whitespace(s));
108 if (!rv) { 146 }
109 *result = resultbuf; 147 if (*p == ch || *p == '\0') {
148 char *end = p;
149 while (p != s && isblank(p[-1]))
150 p--;
151 if (p != end)
152 overlapping_strcpy(p, end);
153 num_fields++;
154 if (*end == '\0') {
155 S.tokenize_end = p + 1;
156 return num_fields;
157 }
158 *p = '\0';
159 s = p + 1;
160 }
161 p++;
110 } 162 }
111
112 return rv;
113} 163}
114 164
115int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf, 165/* Returns !NULL on success and matching line broken up in fields by '\0' in buf.
116 char *__restrict buffer, size_t buflen, 166 * We require the expected number of fields to be found.
117 struct group **__restrict result) 167 */
118{ 168static char *parse_common(FILE *fp, const char *filename,
119 int rv; 169 int n_fields,
170 const char *key, int field_pos)
171{
172 int count = 0;
173 char *buf;
174
175 while ((buf = xmalloc_fgetline(fp)) != NULL) {
176 count++;
177 /* Skip empty lines, comment lines */
178 if (buf[0] == '\0' || buf[0] == '#')
179 goto free_and_next;
180 if (tokenize(buf, ':') != n_fields) {
181 /* number of fields is wrong */
182 bb_error_msg("bad record at %s:%u", filename, count);
183 goto free_and_next;
184 }
120 185
121 *result = NULL; 186/* Ugly hack: group db requires aqdditional buffer space
187 * for members[] array. If there is only one group, we need space
188 * for 3 pointers: alignment padding, group name, NULL.
189 * +1 for every additional group.
190 */
191 if (n_fields == sizeof(GR_DEF)-1) { /* if we read group file */
192 int resize = 3;
193 char *p = buf;
194 while (*p)
195 if (*p++ == ',')
196 resize++;
197 resize *= sizeof(char**);
198 resize += S.tokenize_end - buf;
199 buf = xrealloc(buf, resize);
200 }
122 201
123 rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream); 202 if (!key) {
124 if (!rv) { 203 /* no key specified: sequential read, return a record */
125 *result = resultbuf; 204 break;
205 }
206 if (strcmp(key, nth_string(buf, field_pos)) == 0) {
207 /* record found */
208 break;
209 }
210 free_and_next:
211 free(buf);
126 } 212 }
127 213
128 return rv; 214 return buf;
129} 215}
130 216
131#if ENABLE_USE_BB_SHADOW 217static char *parse_file(const char *filename,
132#ifdef UNUSED_FOR_NOW 218 int n_fields,
133int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf, 219 const char *key, int field_pos)
134 char *__restrict buffer, size_t buflen,
135 struct spwd **__restrict result)
136{ 220{
137 int rv; 221 char *buf = NULL;
222 FILE *fp = fopen_for_read(filename);
138 223
139 *result = NULL; 224 if (fp) {
140 225 buf = parse_common(fp, filename, n_fields, key, field_pos);
141 rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream); 226 fclose(fp);
142 if (!rv) {
143 *result = resultbuf;
144 } 227 }
145 228 return buf;
146 return rv;
147}
148#endif
149#endif
150
151/**********************************************************************/
152/* For the various fget??ent funcs, return NULL on failure and a
153 * pointer to the appropriate struct (statically allocated) on success.
154 * TODO: audit & stop using these in bbox, they pull in static buffers */
155/**********************************************************************/
156
157#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
158struct passwd *fgetpwent(FILE *stream)
159{
160 struct statics *S;
161 struct passwd *resultbuf = RESULTBUF(getpw);
162 char *buffer = BUFFER(getpw);
163 struct passwd *result;
164
165 fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
166 return result;
167} 229}
168 230
169struct group *fgetgrent(FILE *stream) 231/* Convert passwd/group/shadow file record in buffer to a struct */
232static void *convert_to_struct(const char *def, const unsigned char *off,
233 char *buffer, void *result)
170{ 234{
171 struct statics *S; 235 for (;;) {
172 struct group *resultbuf = RESULTBUF(getgr); 236 void *member = (char*)result + (*off++);
173 char *buffer = BUFFER(getgr);
174 struct group *result;
175
176 fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
177 return result;
178}
179#endif
180 237
238 if ((*def | 0x20) == 's') { /* s or S */
239 *(char **)member = (char*)buffer;
240 if (!buffer[0] && (*def == 'S')) {
241 errno = EINVAL;
242 }
243 }
244 if (*def == 'I') {
245 *(int *)member = bb_strtou(buffer, NULL, 10);
246 }
181#if ENABLE_USE_BB_SHADOW 247#if ENABLE_USE_BB_SHADOW
182#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS 248 if (*def == 'l') {
183struct spwd *fgetspent(FILE *stream) 249 long n = -1;
184{ 250 if (buffer[0])
185 struct statics *S; 251 n = bb_strtol(buffer, NULL, 10);
186 struct spwd *resultbuf = RESULTBUF(getsp); 252 *(long *)member = n;
187 char *buffer = BUFFER(getsp); 253 }
188 struct spwd *result;
189
190 fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
191 return result;
192}
193#endif 254#endif
194 255 if (*def == 'm') {
195#ifdef UNUSED_FOR_NOW 256 char **members;
196int sgetspent_r(const char *string, struct spwd *result_buf, 257 int i = tokenize(buffer, ',');
197 char *buffer, size_t buflen, struct spwd **result) 258
198{ 259 /* Store members[] after buffer's end.
199 int rv = ERANGE; 260 * This is safe ONLY because there is a hack
200 261 * in parse_common() which allocates additional space
201 *result = NULL; 262 * at the end of malloced buffer!
202 263 */
203 if (buflen < PWD_BUFFER_SIZE) { 264 members = (char **)
204 DO_ERANGE: 265 ( ((intptr_t)S.tokenize_end + sizeof(char**))
205 errno = rv; 266 & -(intptr_t)sizeof(char**)
206 goto DONE; 267 );
207 } 268
208 269 ((struct group *)result)->gr_mem = members;
209 if (string != buffer) { 270 while (--i >= 0) {
210 if (strlen(string) >= buflen) { 271 *members++ = buffer;
211 goto DO_ERANGE; 272 buffer += strlen(buffer) + 1;
273 }
274 *members = NULL;
212 } 275 }
213 strcpy(buffer, string); 276 /* def "r" does nothing */
214 }
215 277
216 rv = bb__parsespent(result_buf, buffer); 278 def++;
217 if (!rv) { 279 if (*def == '\0')
218 *result = result_buf; 280 break;
281 buffer += strlen(buffer) + 1;
219 } 282 }
220 283
221 DONE: 284 if (errno)
222 return rv; 285 result = NULL;
223}
224#endif
225#endif /* ENABLE_USE_BB_SHADOW */
226
227/**********************************************************************/
228
229#define GETXXKEY_R_FUNC getpwnam_r
230#define GETXXKEY_R_PARSER bb__parsepwent
231#define GETXXKEY_R_ENTTYPE struct passwd
232#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->pw_name, key))
233#define GETXXKEY_R_KEYTYPE const char *__restrict
234#define GETXXKEY_R_PATHNAME _PATH_PASSWD
235#include "pwd_grp_internal.c"
236
237#define GETXXKEY_R_FUNC getgrnam_r
238#define GETXXKEY_R_PARSER bb__parsegrent
239#define GETXXKEY_R_ENTTYPE struct group
240#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->gr_name, key))
241#define GETXXKEY_R_KEYTYPE const char *__restrict
242#define GETXXKEY_R_PATHNAME _PATH_GROUP
243#include "pwd_grp_internal.c"
244
245#if ENABLE_USE_BB_SHADOW
246#define GETXXKEY_R_FUNC getspnam_r
247#define GETXXKEY_R_PARSER bb__parsespent
248#define GETXXKEY_R_ENTTYPE struct spwd
249#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->sp_namp, key))
250#define GETXXKEY_R_KEYTYPE const char *__restrict
251#define GETXXKEY_R_PATHNAME _PATH_SHADOW
252#include "pwd_grp_internal.c"
253#endif
254
255#define GETXXKEY_R_FUNC getpwuid_r
256#define GETXXKEY_R_PARSER bb__parsepwent
257#define GETXXKEY_R_ENTTYPE struct passwd
258#define GETXXKEY_R_TEST(ENT) ((ENT)->pw_uid == key)
259#define GETXXKEY_R_KEYTYPE uid_t
260#define GETXXKEY_R_PATHNAME _PATH_PASSWD
261#include "pwd_grp_internal.c"
262
263#define GETXXKEY_R_FUNC getgrgid_r
264#define GETXXKEY_R_PARSER bb__parsegrent
265#define GETXXKEY_R_ENTTYPE struct group
266#define GETXXKEY_R_TEST(ENT) ((ENT)->gr_gid == key)
267#define GETXXKEY_R_KEYTYPE gid_t
268#define GETXXKEY_R_PATHNAME _PATH_GROUP
269#include "pwd_grp_internal.c"
270
271/**********************************************************************/
272/* TODO: audit & stop using these in bbox, they pull in static buffers */
273
274/* This one has many users */
275struct passwd *getpwuid(uid_t uid)
276{
277 struct statics *S;
278 struct passwd *resultbuf = RESULTBUF(getpw);
279 char *buffer = BUFFER(getpw);
280 struct passwd *result;
281
282 getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
283 return result; 286 return result;
284} 287}
285 288
286/* This one has many users */ 289/****** getXXnam/id_r */
287struct group *getgrgid(gid_t gid)
288{
289 struct statics *S;
290 struct group *resultbuf = RESULTBUF(getgr);
291 char *buffer = BUFFER(getgr);
292 struct group *result;
293 290
294 getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); 291static int getXXnam_r(const char *name, uintptr_t db_and_field_pos, char *buffer, size_t buflen,
295 return result; 292 void *result)
296}
297
298#if 0 //ENABLE_USE_BB_SHADOW
299/* This function is non-standard and is currently not built. It seems
300 * to have been created as a reentrant version of the non-standard
301 * functions getspuid. Why getspuid was added, I do not know. */
302int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
303 char *__restrict buffer, size_t buflen,
304 struct spwd **__restrict result)
305{ 293{
306 int rv; 294 void *struct_buf = *(void**)result;
307 struct passwd *pp; 295 char *buf;
308 struct passwd password; 296 struct passdb *db;
309 char pwd_buff[PWD_BUFFER_SIZE]; 297 get_S();
298 db = &S.db[db_and_field_pos >> 2];
310 299
311 *result = NULL; 300 *(void**)result = NULL;
312 rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp); 301 buf = parse_file(db->filename, db->numfields, name, db_and_field_pos & 3);
313 if (!rv) { 302 if (buf) {
314 rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result); 303 size_t size = S.tokenize_end - buf;
304 if (size > buflen) {
305 errno = ERANGE;
306 } else {
307 memcpy(buffer, buf, size);
308 *(void**)result = convert_to_struct(db->def, db->off, buffer, struct_buf);
309 }
310 free(buf);
315 } 311 }
316 312 /* "The reentrant functions return zero on success.
317 return rv; 313 * In case of error, an error number is returned."
318} 314 * NB: not finding the record is also a "success" here:
319 315 */
320/* This function is non-standard and is currently not built. 316 return errno;
321 * Why it was added, I do not know. */
322struct spwd *getspuid(uid_t uid)
323{
324 struct statics *S;
325 struct spwd *resultbuf = RESULTBUF(getsp);
326 char *buffer = BUFFER(getsp);
327 struct spwd *result;
328
329 getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
330 return result;
331}
332#endif
333
334/* This one has many users */
335struct passwd *getpwnam(const char *name)
336{
337 struct statics *S;
338 struct passwd *resultbuf = RESULTBUF(getpw);
339 char *buffer = BUFFER(getpw);
340 struct passwd *result;
341
342 getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
343 return result;
344} 317}
345 318
346/* This one has many users */ 319int getpwnam_r(const char *name, struct passwd *struct_buf, char *buffer, size_t buflen,
347struct group *getgrnam(const char *name) 320 struct passwd **result)
348{ 321{
349 struct statics *S; 322 /* Why the "store buffer address in result" trick?
350 struct group *resultbuf = RESULTBUF(getgr); 323 * This way, getXXnam_r has the same ABI signature as getpwnam_r,
351 char *buffer = BUFFER(getgr); 324 * hopefully compiler can optimize tall call better in this case.
352 struct group *result; 325 */
353 326 *result = struct_buf;
354 getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgr)), &result); 327 return getXXnam_r(name, (0 << 2) + 0, buffer, buflen, result);
355 return result;
356} 328}
357 329#if ENABLE_USE_BB_SHADOW
358#if 0 //ENABLE_USE_BB_SHADOW 330int getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen,
359struct spwd *getspnam(const char *name) 331 struct spwd **result)
360{ 332{
361 struct statics *S; 333 *result = struct_buf;
362 struct spwd *resultbuf = RESULTBUF(getsp); 334 return getXXnam_r(name, (2 << 2) + 0, buffer, buflen, result);
363 char *buffer = BUFFER(getsp);
364 struct spwd *result;
365
366 getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
367 return result;
368} 335}
369#endif 336#endif
370 337
371/**********************************************************************/ 338/****** getXXent_r */
372
373/* FIXME: we don't have such CONFIG_xx - ?! */
374
375#if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER
376static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
377# define LOCK pthread_mutex_lock(&mylock)
378# define UNLOCK pthread_mutex_unlock(&mylock);
379#else
380# define LOCK ((void) 0)
381# define UNLOCK ((void) 0)
382#endif
383
384static FILE *pwf /*= NULL*/;
385void setpwent(void)
386{
387 LOCK;
388 if (pwf) {
389 rewind(pwf);
390 }
391 UNLOCK;
392}
393
394void endpwent(void)
395{
396 LOCK;
397 if (pwf) {
398 fclose(pwf);
399 pwf = NULL;
400 }
401 UNLOCK;
402}
403
404 339
405int getpwent_r(struct passwd *__restrict resultbuf, 340static int getXXent_r(void *struct_buf, char *buffer, size_t buflen,
406 char *__restrict buffer, size_t buflen, 341 void *result,
407 struct passwd **__restrict result) 342 unsigned db_idx)
408{ 343{
409 int rv; 344 char *buf;
345 struct passdb *db;
346 get_S();
347 db = &S.db[db_idx];
410 348
411 LOCK; 349 *(void**)result = NULL;
412 *result = NULL; /* In case of error... */
413 350
414 if (!pwf) { 351 if (!db->fp) {
415 pwf = fopen_for_read(_PATH_PASSWD); 352 db->fp = fopen_for_read(db->filename);
416 if (!pwf) { 353 if (!db->fp) {
417 rv = errno; 354 return errno;
418 goto ERR;
419 } 355 }
420 close_on_exec_on(fileno(pwf)); 356 close_on_exec_on(fileno(db->fp));
421 } 357 }
422 358
423 rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf); 359 buf = parse_common(db->fp, db->filename, db->numfields, /*no search key:*/ NULL, 0);
424 if (!rv) { 360 if (buf) {
425 *result = resultbuf; 361 size_t size = S.tokenize_end - buf;
362 if (size > buflen) {
363 errno = ERANGE;
364 } else {
365 memcpy(buffer, buf, size);
366 *(void**)result = convert_to_struct(db->def, db->off, buffer, struct_buf);
367 }
368 free(buf);
426 } 369 }
427 370 /* "The reentrant functions return zero on success.
428 ERR: 371 * In case of error, an error number is returned."
429 UNLOCK; 372 * NB: not finding the record is also a "success" here:
430 return rv; 373 */
374 return errno;
431} 375}
432 376
433static FILE *grf /*= NULL*/; 377int getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, struct passwd **result)
434void setgrent(void)
435{ 378{
436 LOCK; 379 return getXXent_r(struct_buf, buffer, buflen, result, 0);
437 if (grf) {
438 rewind(grf);
439 }
440 UNLOCK;
441} 380}
442 381
443void endgrent(void) 382/****** getXXnam/id */
444{
445 LOCK;
446 if (grf) {
447 fclose(grf);
448 grf = NULL;
449 }
450 UNLOCK;
451}
452 383
453int getgrent_r(struct group *__restrict resultbuf, 384static void *getXXnam(const char *name, unsigned db_and_field_pos)
454 char *__restrict buffer, size_t buflen,
455 struct group **__restrict result)
456{ 385{
457 int rv; 386 char *buf;
387 void *result;
388 struct passdb *db;
389 get_S();
390 db = &S.db[db_and_field_pos >> 2];
458 391
459 LOCK; 392 result = NULL;
460 *result = NULL; /* In case of error... */
461 393
462 if (!grf) { 394 if (!db->fp) {
463 grf = fopen_for_read(_PATH_GROUP); 395 db->fp = fopen_for_read(db->filename);
464 if (!grf) { 396 if (!db->fp) {
465 rv = errno; 397 return NULL;
466 goto ERR;
467 } 398 }
468 close_on_exec_on(fileno(grf)); 399 close_on_exec_on(fileno(db->fp));
469 } 400 }
470 401
471 rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf); 402 free(db->malloced);
472 if (!rv) { 403 db->malloced = NULL;
473 *result = resultbuf; 404 buf = parse_common(db->fp, db->filename, db->numfields, name, db_and_field_pos & 3);
405 if (buf) {
406 db->malloced = xzalloc(db->size_of);
407 result = convert_to_struct(db->def, db->off, buf, db->malloced);
474 } 408 }
475 409 return result;
476 ERR:
477 UNLOCK;
478 return rv;
479} 410}
480 411
481#ifdef UNUSED_FOR_NOW 412struct passwd *getpwnam(const char *name)
482#if ENABLE_USE_BB_SHADOW
483static FILE *spf /*= NULL*/;
484void setspent(void)
485{ 413{
486 LOCK; 414 return getXXnam(name, (0 << 2) + 0);
487 if (spf) {
488 rewind(spf);
489 }
490 UNLOCK;
491} 415}
492 416struct group *getgrnam(const char *name)
493void endspent(void)
494{ 417{
495 LOCK; 418 return getXXnam(name, (1 << 2) + 0);
496 if (spf) {
497 fclose(spf);
498 spf = NULL;
499 }
500 UNLOCK;
501} 419}
502 420struct passwd *getpwuid(uid_t id)
503int getspent_r(struct spwd *resultbuf, char *buffer,
504 size_t buflen, struct spwd **result)
505{ 421{
506 int rv; 422 return getXXnam(utoa(id), (0 << 2) + 2);
507
508 LOCK;
509 *result = NULL; /* In case of error... */
510
511 if (!spf) {
512 spf = fopen_for_read(_PATH_SHADOW);
513 if (!spf) {
514 rv = errno;
515 goto ERR;
516 }
517 close_on_exec_on(fileno(spf));
518 }
519
520 rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf);
521 if (!rv) {
522 *result = resultbuf;
523 }
524
525 ERR:
526 UNLOCK;
527 return rv;
528} 423}
529#endif 424struct group *getgrgid(gid_t id)
530#endif /* UNUSED_FOR_NOW */
531
532#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
533struct passwd *getpwent(void)
534{ 425{
535 static char line_buff[PWD_BUFFER_SIZE]; 426 return getXXnam(utoa(id), (1 << 2) + 2);
536 static struct passwd pwd;
537 struct passwd *result;
538
539 getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
540 return result;
541} 427}
542 428
543struct group *getgrent(void) 429/****** end/setXXend */
544{
545 static char line_buff[GRP_BUFFER_SIZE];
546 static struct group gr;
547 struct group *result;
548 430
549 getgrent_r(&gr, line_buff, sizeof(line_buff), &result); 431void endpwent(void)
550 return result; 432{
433 if (has_S && S.db[0].fp) {
434 fclose(S.db[0].fp);
435 S.db[0].fp = NULL;
436 }
551} 437}
552 438void setpwent(void)
553#if ENABLE_USE_BB_SHADOW
554struct spwd *getspent(void)
555{ 439{
556 static char line_buff[PWD_BUFFER_SIZE]; 440 if (has_S && S.db[0].fp) {
557 static struct spwd spwd; 441 rewind(S.db[0].fp);
558 struct spwd *result; 442 }
559
560 getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
561 return result;
562} 443}
563 444void endgrent(void)
564struct spwd *sgetspent(const char *string)
565{ 445{
566 static char line_buff[PWD_BUFFER_SIZE]; 446 if (has_S && S.db[1].fp) {
567 static struct spwd spwd; 447 fclose(S.db[1].fp);
568 struct spwd *result; 448 S.db[1].fp = NULL;
569 449 }
570 sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
571 return result;
572} 450}
573#endif
574#endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */
575 451
576static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid) 452/****** initgroups and getgrouplist */
453
454static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr,
455 const char *user, gid_t gid)
577{ 456{
578 FILE *grfile; 457 FILE *fp;
579 gid_t *group_list; 458 gid_t *group_list;
580 int ngroups; 459 int ngroups;
581 struct group group; 460
582 char buff[PWD_BUFFER_SIZE]; 461 get_S();
583 462
584 /* We alloc space for 8 gids at a time. */ 463 /* We alloc space for 8 gids at a time. */
585 group_list = xmalloc(8 * sizeof(group_list[0])); 464 group_list = xmalloc(8 * sizeof(group_list[0]));
586 group_list[0] = gid; 465 group_list[0] = gid;
587 ngroups = 1; 466 ngroups = 1;
588 467
589 grfile = fopen_for_read(_PATH_GROUP); 468 fp = fopen_for_read(_PATH_GROUP);
590 if (grfile) { 469 if (fp) {
591 while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) { 470 char *buf;
471 while ((buf = parse_common(fp, _PATH_GROUP, sizeof(GR_DEF)-1, NULL, 0)) != NULL) {
592 char **m; 472 char **m;
593 assert(group.gr_mem); /* Must have at least a NULL terminator. */ 473 struct group group;
474 if (!convert_to_struct(GR_DEF, gr_off, buf, &group))
475 goto next;
594 if (group.gr_gid == gid) 476 if (group.gr_gid == gid)
595 continue; 477 goto next;
596 for (m = group.gr_mem; *m; m++) { 478 for (m = group.gr_mem; *m; m++) {
597 if (strcmp(*m, user) != 0) 479 if (strcmp(*m, user) != 0)
598 continue; 480 continue;
@@ -600,8 +482,10 @@ static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gi
600 group_list[ngroups++] = group.gr_gid; 482 group_list[ngroups++] = group.gr_gid;
601 break; 483 break;
602 } 484 }
485 next:
486 free(buf);
603 } 487 }
604 fclose(grfile); 488 fclose(fp);
605 } 489 }
606 *ngroups_ptr = ngroups; 490 *ngroups_ptr = ngroups;
607 return group_list; 491 return group_list;
@@ -631,409 +515,3 @@ int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
631 free(group_list); 515 free(group_list);
632 return ngroups_old; 516 return ngroups_old;
633} 517}
634
635#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
636int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
637{
638 int rv = -1;
639
640#if 0
641 /* glibc does this check */
642 if (!p || !f) {
643 errno = EINVAL;
644 return rv;
645 }
646#endif
647
648 /* No extra thread locking is needed above what fprintf does. */
649 if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
650 p->pw_name, p->pw_passwd,
651 (unsigned long)(p->pw_uid),
652 (unsigned long)(p->pw_gid),
653 p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
654 ) {
655 rv = 0;
656 }
657
658 return rv;
659}
660
661int putgrent(const struct group *__restrict p, FILE *__restrict f)
662{
663 int rv = -1;
664
665#if 0
666 /* glibc does this check */
667 if (!p || !f) {
668 errno = EINVAL;
669 return rv;
670 }
671#endif
672
673 if (fprintf(f, "%s:%s:%lu:",
674 p->gr_name, p->gr_passwd,
675 (unsigned long)(p->gr_gid)) >= 0
676 ) {
677 static const char format[] ALIGN1 = ",%s";
678
679 char **m;
680 const char *fmt;
681
682 fmt = format + 1;
683
684 assert(p->gr_mem);
685 m = p->gr_mem;
686
687 while (1) {
688 if (!*m) {
689 if (fputc('\n', f) >= 0) {
690 rv = 0;
691 }
692 break;
693 }
694 if (fprintf(f, fmt, *m) < 0) {
695 break;
696 }
697 m++;
698 fmt = format;
699 }
700 }
701
702 return rv;
703}
704#endif
705
706#if ENABLE_USE_BB_SHADOW
707#ifdef UNUSED_FOR_NOW
708static const unsigned char put_sp_off[] ALIGN1 = {
709 offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */
710 offsetof(struct spwd, sp_min), /* 3 - not a char ptr */
711 offsetof(struct spwd, sp_max), /* 4 - not a char ptr */
712 offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */
713 offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */
714 offsetof(struct spwd, sp_expire) /* 7 - not a char ptr */
715};
716
717int putspent(const struct spwd *p, FILE *stream)
718{
719 const char *fmt;
720 long x;
721 int i;
722 int rv = -1;
723
724 /* Unlike putpwent and putgrent, glibc does not check the args. */
725 if (fprintf(stream, "%s:%s:", p->sp_namp,
726 (p->sp_pwdp ? p->sp_pwdp : "")) < 0
727 ) {
728 goto DO_UNLOCK;
729 }
730
731 for (i = 0; i < sizeof(put_sp_off); i++) {
732 fmt = "%ld:";
733 x = *(long *)((char *)p + put_sp_off[i]);
734 if (x == -1) {
735 fmt += 3;
736 }
737 if (fprintf(stream, fmt, x) < 0) {
738 goto DO_UNLOCK;
739 }
740 }
741
742 if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
743 goto DO_UNLOCK;
744 }
745
746 if (fputc('\n', stream) > 0) {
747 rv = 0;
748 }
749
750 DO_UNLOCK:
751 return rv;
752}
753#endif
754#endif /* USE_BB_SHADOW */
755
756/**********************************************************************/
757/* Internal functions */
758/**********************************************************************/
759
760static const unsigned char pw_off[] ALIGN1 = {
761 offsetof(struct passwd, pw_name), /* 0 */
762 offsetof(struct passwd, pw_passwd), /* 1 */
763 offsetof(struct passwd, pw_uid), /* 2 - not a char ptr */
764 offsetof(struct passwd, pw_gid), /* 3 - not a char ptr */
765 offsetof(struct passwd, pw_gecos), /* 4 */
766 offsetof(struct passwd, pw_dir), /* 5 */
767 offsetof(struct passwd, pw_shell) /* 6 */
768};
769
770static int FAST_FUNC bb__parsepwent(void *data, char *line)
771{
772 char *endptr;
773 char *p;
774 int i;
775
776 i = 0;
777 while (1) {
778 p = (char *) data + pw_off[i];
779
780 if (i < 2 || i > 3) {
781 *((char **) p) = line;
782 if (i == 6) {
783 return 0;
784 }
785 /* NOTE: glibc difference - glibc allows omission of
786 * ':' seperators after the gid field if all remaining
787 * entries are empty. We require all separators. */
788 line = strchr(line, ':');
789 if (!line) {
790 break;
791 }
792 } else {
793 unsigned long t = strtoul(line, &endptr, 10);
794 /* Make sure we had at least one digit, and that the
795 * failing char is the next field seperator ':'. See
796 * glibc difference note above. */
797 /* TODO: Also check for leading whitespace? */
798 if ((endptr == line) || (*endptr != ':')) {
799 break;
800 }
801 line = endptr;
802 if (i & 1) { /* i == 3 -- gid */
803 *((gid_t *) p) = t;
804 } else { /* i == 2 -- uid */
805 *((uid_t *) p) = t;
806 }
807 }
808
809 *line++ = '\0';
810 i++;
811 } /* while (1) */
812
813 return -1;
814}
815
816/**********************************************************************/
817
818static const unsigned char gr_off[] ALIGN1 = {
819 offsetof(struct group, gr_name), /* 0 */
820 offsetof(struct group, gr_passwd), /* 1 */
821 offsetof(struct group, gr_gid) /* 2 - not a char ptr */
822};
823
824static int FAST_FUNC bb__parsegrent(void *data, char *line)
825{
826 char *endptr;
827 char *p;
828 int i;
829 char **members;
830 char *end_of_buf;
831
832 end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
833 i = 0;
834 while (1) {
835 p = (char *) data + gr_off[i];
836
837 if (i < 2) {
838 *((char **) p) = line;
839 line = strchr(line, ':');
840 if (!line) {
841 break;
842 }
843 *line++ = '\0';
844 i++;
845 } else {
846 *((gid_t *) p) = strtoul(line, &endptr, 10);
847
848 /* NOTE: glibc difference - glibc allows omission of the
849 * trailing colon when there is no member list. We treat
850 * this as an error. */
851
852 /* Make sure we had at least one digit, and that the
853 * failing char is the next field seperator ':'. See
854 * glibc difference note above. */
855 if ((endptr == line) || (*endptr != ':')) {
856 break;
857 }
858
859 i = 1; /* Count terminating NULL ptr. */
860 p = endptr;
861
862 if (p[1]) { /* We have a member list to process. */
863 /* Overwrite the last ':' with a ',' before counting.
864 * This allows us to (1) test for initial ','
865 * and (2) adds one ',' so that the number of commas
866 * equals the member count. */
867 *p = ',';
868 do {
869 /* NOTE: glibc difference - glibc allows and trims leading
870 * (but not trailing) space. We treat this as an error. */
871 /* NOTE: glibc difference - glibc allows consecutive and
872 * trailing commas, and ignores "empty string" users. We
873 * treat this as an error. */
874 if (*p == ',') {
875 ++i;
876 *p = 0; /* nul-terminate each member string. */
877 if (!*++p || (*p == ',') || isspace(*p)) {
878 goto ERR;
879 }
880 }
881 } while (*++p);
882 }
883
884 /* Now align (p+1), rounding up. */
885 /* Assumes sizeof(char **) is a power of 2. */
886 members = (char **)( (((intptr_t) p) + sizeof(char **))
887 & ~((intptr_t)(sizeof(char **) - 1)) );
888
889 if (((char *)(members + i)) > end_of_buf) { /* No space. */
890 break;
891 }
892
893 ((struct group *) data)->gr_mem = members;
894
895 if (--i) {
896 p = endptr; /* Pointing to char prior to first member. */
897 while (1) {
898 *members++ = ++p;
899 if (!--i)
900 break;
901 while (*++p)
902 continue;
903 }
904 }
905 *members = NULL;
906
907 return 0;
908 }
909 } /* while (1) */
910
911 ERR:
912 return -1;
913}
914
915/**********************************************************************/
916
917#if ENABLE_USE_BB_SHADOW
918static const unsigned char sp_off[] ALIGN1 = {
919 offsetof(struct spwd, sp_namp), /* 0: char* */
920 offsetof(struct spwd, sp_pwdp), /* 1: char* */
921 offsetof(struct spwd, sp_lstchg), /* 2: long */
922 offsetof(struct spwd, sp_min), /* 3: long */
923 offsetof(struct spwd, sp_max), /* 4: long */
924 offsetof(struct spwd, sp_warn), /* 5: long */
925 offsetof(struct spwd, sp_inact), /* 6: long */
926 offsetof(struct spwd, sp_expire), /* 7: long */
927 offsetof(struct spwd, sp_flag) /* 8: unsigned long */
928};
929
930static int FAST_FUNC bb__parsespent(void *data, char *line)
931{
932 char *endptr;
933 char *p;
934 int i;
935
936 i = 0;
937 while (1) {
938 p = (char *) data + sp_off[i];
939 if (i < 2) {
940 *((char **) p) = line;
941 line = strchr(line, ':');
942 if (!line) {
943 break; /* error */
944 }
945 } else {
946 *((long *) p) = strtoul(line, &endptr, 10);
947 if (endptr == line) {
948 *((long *) p) = -1L;
949 }
950 line = endptr;
951 if (i == 8) {
952 if (*line != '\0') {
953 break; /* error */
954 }
955 return 0; /* all ok */
956 }
957 if (*line != ':') {
958 break; /* error */
959 }
960 }
961 *line++ = '\0';
962 i++;
963 }
964
965 return EINVAL;
966}
967#endif
968
969/**********************************************************************/
970
971/* Reads until EOF, or until it finds a line which fits in the buffer
972 * and for which the parser function succeeds.
973 *
974 * Returns 0 on success and ENOENT for end-of-file (glibc convention).
975 */
976static int bb__pgsreader(
977 int FAST_FUNC (*parserfunc)(void *d, char *line),
978 void *data,
979 char *__restrict line_buff,
980 size_t buflen,
981 FILE *f)
982{
983 int skip;
984 int rv = ERANGE;
985
986 if (buflen < PWD_BUFFER_SIZE) {
987 errno = rv;
988 return rv;
989 }
990
991 skip = 0;
992 while (1) {
993 if (!fgets(line_buff, buflen, f)) {
994 if (feof(f)) {
995 rv = ENOENT;
996 }
997 break;
998 }
999
1000 {
1001 int line_len = strlen(line_buff) - 1;
1002 if (line_len >= 0 && line_buff[line_len] == '\n') {
1003 line_buff[line_len] = '\0';
1004 } else
1005 if (line_len + 2 == buflen) {
1006 /* A start (or continuation) of overlong line */
1007 skip = 1;
1008 continue;
1009 } /* else: a last line in the file, and it has no '\n' */
1010 }
1011
1012 if (skip) {
1013 /* This "line" is a remainder of overlong line, ignore */
1014 skip = 0;
1015 continue;
1016 }
1017
1018 /* NOTE: glibc difference - glibc strips leading whitespace from
1019 * records. We do not allow leading whitespace. */
1020
1021 /* Skip empty lines, comment lines, and lines with leading
1022 * whitespace. */
1023 if (line_buff[0] != '\0' && line_buff[0] != '#' && !isspace(line_buff[0])) {
1024 if (parserfunc == bb__parsegrent) {
1025 /* Do evil group hack:
1026 * The group entry parsing function needs to know where
1027 * the end of the buffer is so that it can construct the
1028 * group member ptr table. */
1029 ((struct group *) data)->gr_name = line_buff + buflen;
1030 }
1031 if (parserfunc(data, line_buff) == 0) {
1032 rv = 0;
1033 break;
1034 }
1035 }
1036 } /* while (1) */
1037
1038 return rv;
1039}
diff --git a/libpwdgrp/pwd_grp_internal.c b/libpwdgrp/pwd_grp_internal.c
deleted file mode 100644
index d6483be84..000000000
--- a/libpwdgrp/pwd_grp_internal.c
+++ /dev/null
@@ -1,61 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/* Copyright (C) 2003 Manuel Novoa III
3 *
4 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
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#ifndef GETXXKEY_R_FUNC
21#error GETXXKEY_R_FUNC is not defined!
22#endif
23
24int GETXXKEY_R_FUNC(GETXXKEY_R_KEYTYPE key,
25 GETXXKEY_R_ENTTYPE *__restrict resultbuf,
26 char *__restrict buffer, size_t buflen,
27 GETXXKEY_R_ENTTYPE **__restrict result)
28{
29 FILE *stream;
30 int rv;
31
32 *result = NULL;
33
34 stream = fopen_for_read(GETXXKEY_R_PATHNAME);
35 if (!stream)
36 return errno;
37 while (1) {
38 rv = bb__pgsreader(GETXXKEY_R_PARSER, resultbuf, buffer, buflen, stream);
39 if (!rv) {
40 if (GETXXKEY_R_TEST(resultbuf)) { /* found key? */
41 *result = resultbuf;
42 break;
43 }
44 } else {
45 if (rv == ENOENT) { /* EOF encountered */
46 rv = 0;
47 }
48 break;
49 }
50 }
51 fclose(stream);
52
53 return rv;
54}
55
56#undef GETXXKEY_R_FUNC
57#undef GETXXKEY_R_PARSER
58#undef GETXXKEY_R_ENTTYPE
59#undef GETXXKEY_R_TEST
60#undef GETXXKEY_R_KEYTYPE
61#undef GETXXKEY_R_PATHNAME