diff options
author | Tito Ragusa <farmatito@tiscali.it> | 2015-01-02 21:37:59 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2015-01-02 21:37:59 +0100 |
commit | 1da09cfacf1c4789cc74322857a098c2ddb06e31 (patch) | |
tree | 0121bddf6ed1d159ca76f2b350832e400bf83c26 | |
parent | 78854520ebecfd24d5c80a266d6779bd1e069016 (diff) | |
download | busybox-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_.h | 54 | ||||
-rw-r--r-- | include/pwd_.h | 25 | ||||
-rw-r--r-- | libbb/bb_pwd.c | 48 | ||||
-rw-r--r-- | libpwdgrp/pwd_grp.c | 1238 | ||||
-rw-r--r-- | libpwdgrp/pwd_grp_internal.c | 61 |
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. */ | ||
53 | extern void setgrent(void); | ||
54 | |||
55 | /* Close the group-file stream. */ | 43 | /* Close the group-file stream. */ |
56 | extern void endgrent(void); | 44 | extern 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. */ | ||
60 | extern struct group *getgrent(void); | ||
61 | |||
62 | /* Read a group entry from STREAM. */ | ||
63 | extern struct group *fgetgrent(FILE *__stream); | ||
64 | |||
65 | /* Write the given entry onto the given stream. */ | ||
66 | extern 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. */ |
71 | extern struct group *getgrgid(gid_t __gid); | 47 | extern 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. */ |
74 | extern struct group *getgrnam(const char *__name); | 50 | extern 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 | |||
84 | extern 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. */ | ||
89 | extern 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. */ | ||
94 | extern 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. */ | ||
101 | extern 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. */ |
52 | extern void setpwent(void); | 46 | extern 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. */ |
59 | extern struct passwd *getpwent(void); | 53 | extern struct passwd *getpwent(void); |
60 | |||
61 | /* Read an entry from STREAM. */ | ||
62 | extern struct passwd *fgetpwent(FILE *__stream); | ||
63 | |||
64 | /* Write the given entry onto the given stream. */ | ||
65 | extern 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 | ||
87 | extern int getpwuid_r(uid_t __uid, | ||
88 | struct passwd *__restrict __resultbuf, | ||
89 | char *__restrict __buffer, size_t __buflen, | ||
90 | struct passwd **__restrict __result); | ||
91 | |||
92 | extern int getpwnam_r(const char *__restrict __name, | 74 | extern 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. */ | ||
99 | extern int fgetpwent_r(FILE *__restrict __stream, | ||
100 | struct passwd *__restrict __resultbuf, | ||
101 | char *__restrict __buffer, size_t __buflen, | ||
102 | struct passwd **__restrict __result); | ||
103 | |||
104 | POP_SAVED_FUNCTION_VISIBILITY | 79 | POP_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 | ||
119 | static 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 | |||
129 | struct 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 | |||
48 | static 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 | }; | ||
57 | static 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 | ||
64 | static 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 | ||
32 | static int bb__pgsreader( | 77 | struct 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 | }; | ||
84 | struct 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 | ||
39 | static int FAST_FUNC bb__parsepwent(void *pw, char *line); | 94 | static const struct const_passdb const_pw_db = { _PATH_PASSWD, pw_off, PW_DEF, sizeof(PW_DEF)-1, sizeof(struct passwd) }; |
40 | static int FAST_FUNC bb__parsegrent(void *gr, char *line); | 95 | static 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 |
42 | static int FAST_FUNC bb__parsespent(void *sp, char *line); | 97 | static 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 | |||
48 | struct statics { | 101 | struct 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 | ||
70 | static struct statics *ptr_to_statics; | 110 | static struct statics *ptr_to_statics; |
111 | #define S (*ptr_to_statics) | ||
112 | #define has_S (ptr_to_statics) | ||
71 | 113 | ||
72 | static struct statics *get_S(void) | 114 | static 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 | ||
99 | int 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 | */ | ||
137 | static 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 | ||
115 | int 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 | { | 168 | static 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 | 217 | static char *parse_file(const char *filename, |
132 | #ifdef UNUSED_FOR_NOW | 218 | int n_fields, |
133 | int 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 | ||
158 | struct 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 | ||
169 | struct group *fgetgrent(FILE *stream) | 231 | /* Convert passwd/group/shadow file record in buffer to a struct */ |
232 | static 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') { |
183 | struct 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; |
196 | int 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 */ | ||
275 | struct 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 */ |
287 | struct 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); | 291 | static 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. */ | ||
302 | int 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. */ | ||
322 | struct 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 */ | ||
335 | struct 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 */ | 319 | int getpwnam_r(const char *name, struct passwd *struct_buf, char *buffer, size_t buflen, |
347 | struct 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 | 330 | int getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen, |
359 | struct 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 | ||
376 | static 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 | |||
384 | static FILE *pwf /*= NULL*/; | ||
385 | void setpwent(void) | ||
386 | { | ||
387 | LOCK; | ||
388 | if (pwf) { | ||
389 | rewind(pwf); | ||
390 | } | ||
391 | UNLOCK; | ||
392 | } | ||
393 | |||
394 | void endpwent(void) | ||
395 | { | ||
396 | LOCK; | ||
397 | if (pwf) { | ||
398 | fclose(pwf); | ||
399 | pwf = NULL; | ||
400 | } | ||
401 | UNLOCK; | ||
402 | } | ||
403 | |||
404 | 339 | ||
405 | int getpwent_r(struct passwd *__restrict resultbuf, | 340 | static 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 | ||
433 | static FILE *grf /*= NULL*/; | 377 | int getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, struct passwd **result) |
434 | void 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 | ||
443 | void endgrent(void) | 382 | /****** getXXnam/id */ |
444 | { | ||
445 | LOCK; | ||
446 | if (grf) { | ||
447 | fclose(grf); | ||
448 | grf = NULL; | ||
449 | } | ||
450 | UNLOCK; | ||
451 | } | ||
452 | 383 | ||
453 | int getgrent_r(struct group *__restrict resultbuf, | 384 | static 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 | 412 | struct passwd *getpwnam(const char *name) |
482 | #if ENABLE_USE_BB_SHADOW | ||
483 | static FILE *spf /*= NULL*/; | ||
484 | void 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 | 416 | struct group *getgrnam(const char *name) | |
493 | void 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 | 420 | struct passwd *getpwuid(uid_t id) | |
503 | int 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 | 424 | struct group *getgrgid(gid_t id) |
530 | #endif /* UNUSED_FOR_NOW */ | ||
531 | |||
532 | #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS | ||
533 | struct 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 | ||
543 | struct 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); | 431 | void 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 | 438 | void setpwent(void) | |
553 | #if ENABLE_USE_BB_SHADOW | ||
554 | struct 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 | 444 | void endgrent(void) | |
564 | struct 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 | ||
576 | static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid) | 452 | /****** initgroups and getgrouplist */ |
453 | |||
454 | static 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 | ||
636 | int 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 | |||
661 | int 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 | ||
708 | static 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 | |||
717 | int 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 | |||
760 | static 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 | |||
770 | static 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 | |||
818 | static 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 | |||
824 | static 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 | ||
918 | static 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 | |||
930 | static 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 | */ | ||
976 | static 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 | |||
24 | int 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 | ||