Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/lib9p/rfuncs.c
39481 views
1
/*
2
* Copyright 2016 Chris Torek <[email protected]>
3
* All rights reserved
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted providing that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
* POSSIBILITY OF SUCH DAMAGE.
25
*
26
*/
27
28
#include <errno.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <unistd.h>
32
33
#if defined(WITH_CASPER)
34
#include <libcasper.h>
35
#include <casper/cap_pwd.h>
36
#include <casper/cap_grp.h>
37
#endif
38
39
#include "rfuncs.h"
40
41
/*
42
* This is essentially a clone of the BSD basename_r function,
43
* which is like POSIX basename() but puts the result in a user
44
* supplied buffer.
45
*
46
* In BSD basename_r, the buffer must be least MAXPATHLEN bytes
47
* long. In our case we take the size of the buffer as an argument.
48
*
49
* Note that it's impossible in general to do this without
50
* a temporary buffer since basename("foo/bar") is "bar",
51
* but basename("foo/bar/") is still "bar" -- no trailing
52
* slash is allowed.
53
*
54
* The return value is your supplied buffer <buf>, or NULL if
55
* the length of the basename of the supplied <path> equals or
56
* exceeds your indicated <bufsize>.
57
*
58
* As a special but useful case, if you supply NULL for the <buf>
59
* argument, we allocate the buffer dynamically to match the
60
* basename, i.e., the result is basically strdup()ed for you.
61
* In this case <bufsize> is ignored (recommended: pass 0 here).
62
*/
63
char *
64
r_basename(const char *path, char *buf, size_t bufsize)
65
{
66
const char *endp, *comp;
67
size_t len;
68
69
/*
70
* NULL or empty path means ".". This is perhaps overly
71
* forgiving but matches libc basename_r(), and avoids
72
* breaking the code below.
73
*/
74
if (path == NULL || *path == '\0') {
75
comp = ".";
76
len = 1;
77
} else {
78
/*
79
* Back up over any trailing slashes. If we reach
80
* the top of the path and it's still a trailing
81
* slash, it's also a leading slash and the entire
82
* path is just "/" (or "//", or "///", etc).
83
*/
84
endp = path + strlen(path) - 1;
85
while (*endp == '/' && endp > path)
86
endp--;
87
/* Invariant: *endp != '/' || endp == path */
88
if (*endp == '/') {
89
/* then endp==path and hence entire path is "/" */
90
comp = "/";
91
len = 1;
92
} else {
93
/*
94
* We handled empty strings earlier, and
95
* we just proved *endp != '/'. Hence
96
* we have a non-empty basename, ending
97
* at endp.
98
*
99
* Back up one path name component. The
100
* part between these two is the basename.
101
*
102
* Note that we only stop backing up when
103
* either comp==path, or comp[-1] is '/'.
104
*
105
* Suppose path[0] is '/'. Then, since *endp
106
* is *not* '/', we had comp>path initially, and
107
* stopped backing up because we found a '/'
108
* (perhaps path[0], perhaps a later '/').
109
*
110
* Or, suppose path[0] is NOT '/'. Then,
111
* either there are no '/'s at all and
112
* comp==path, or comp[-1] is '/'.
113
*
114
* In all cases, we want all bytes from *comp
115
* to *endp, inclusive.
116
*/
117
comp = endp;
118
while (comp > path && comp[-1] != '/')
119
comp--;
120
len = (size_t)(endp - comp + 1);
121
}
122
}
123
if (buf == NULL) {
124
buf = malloc(len + 1);
125
if (buf == NULL)
126
return (NULL);
127
} else {
128
if (len >= bufsize) {
129
errno = ENAMETOOLONG;
130
return (NULL);
131
}
132
}
133
memcpy(buf, comp, len);
134
buf[len] = '\0';
135
return (buf);
136
}
137
138
/*
139
* This is much like POSIX dirname(), but is reentrant.
140
*
141
* We examine a path, find the directory portion, and copy that
142
* to a user supplied buffer <buf> of the given size <bufsize>.
143
*
144
* Note that dirname("/foo/bar/") is "/foo", dirname("/foo") is "/",
145
* and dirname("////") is "/". However, dirname("////foo/bar") is
146
* "////foo" (we do not resolve these leading slashes away -- this
147
* matches the BSD libc behavior).
148
*
149
* The return value is your supplied buffer <buf>, or NULL if
150
* the length of the dirname of the supplied <path> equals or
151
* exceeds your indicated <bufsize>.
152
*
153
* As a special but useful case, if you supply NULL for the <buf>
154
* argument, we allocate the buffer dynamically to match the
155
* dirname, i.e., the result is basically strdup()ed for you.
156
* In this case <bufsize> is ignored (recommended: pass 0 here).
157
*/
158
char *
159
r_dirname(const char *path, char *buf, size_t bufsize)
160
{
161
const char *endp, *dirpart;
162
size_t len;
163
164
/*
165
* NULL or empty path means ".". This is perhaps overly
166
* forgiving but matches libc dirname(), and avoids breaking
167
* the code below.
168
*/
169
if (path == NULL || *path == '\0') {
170
dirpart = ".";
171
len = 1;
172
} else {
173
/*
174
* Back up over any trailing slashes, then back up
175
* one path name, then back up over more slashes.
176
* In all cases, stop as soon as endp==path so
177
* that we do not back out of the buffer entirely.
178
*
179
* The first loop takes care of trailing slashes
180
* in names like "/foo/bar//" (where the dirname
181
* part is to be "/foo"), the second strips out
182
* the non-dir-name part, and the third leaves us
183
* pointing to the end of the directory component.
184
*
185
* If the entire name is of the form "/foo" or
186
* "//foo" (or "/foo/", etc, but we already
187
* handled trailing slashes), we end up pointing
188
* to the leading "/", which is what we want; but
189
* if it is of the form "foo" (or "foo/", etc) we
190
* point to a non-slash. So, if (and only if)
191
* endp==path AND *endp is not '/', the dirname is
192
* ".", but in all cases, the LENGTH of the
193
* dirname is (endp-path+1).
194
*/
195
endp = path + strlen(path) - 1;
196
while (endp > path && *endp == '/')
197
endp--;
198
while (endp > path && *endp != '/')
199
endp--;
200
while (endp > path && *endp == '/')
201
endp--;
202
203
len = (size_t)(endp - path + 1);
204
if (endp == path && *endp != '/')
205
dirpart = ".";
206
else
207
dirpart = path;
208
}
209
if (buf == NULL) {
210
buf = malloc(len + 1);
211
if (buf == NULL)
212
return (NULL);
213
} else {
214
if (len >= bufsize) {
215
errno = ENAMETOOLONG;
216
return (NULL);
217
}
218
}
219
memcpy(buf, dirpart, len);
220
buf[len] = '\0';
221
return (buf);
222
}
223
224
static void
225
r_pginit(struct r_pgdata *pg)
226
{
227
228
/* Note: init to half size since the first thing we do is double it */
229
pg->r_pgbufsize = 1 << 9;
230
pg->r_pgbuf = NULL; /* note that realloc(NULL) == malloc */
231
}
232
233
static int
234
r_pgexpand(struct r_pgdata *pg)
235
{
236
size_t nsize;
237
238
nsize = pg->r_pgbufsize << 1;
239
if (nsize >= (1 << 20) ||
240
(pg->r_pgbuf = realloc(pg->r_pgbuf, nsize)) == NULL)
241
return (ENOMEM);
242
return (0);
243
}
244
245
void
246
r_pgfree(struct r_pgdata *pg)
247
{
248
249
free(pg->r_pgbuf);
250
}
251
252
struct passwd *
253
r_getpwuid(uid_t uid, struct r_pgdata *pg)
254
{
255
struct passwd *result = NULL;
256
int error;
257
258
r_pginit(pg);
259
do {
260
error = r_pgexpand(pg);
261
if (error == 0)
262
error = getpwuid_r(uid, &pg->r_pgun.un_pw,
263
pg->r_pgbuf, pg->r_pgbufsize, &result);
264
} while (error == ERANGE);
265
266
return (error ? NULL : result);
267
}
268
269
struct group *
270
r_getgrgid(gid_t gid, struct r_pgdata *pg)
271
{
272
struct group *result = NULL;
273
int error;
274
275
r_pginit(pg);
276
do {
277
error = r_pgexpand(pg);
278
if (error == 0)
279
error = getgrgid_r(gid, &pg->r_pgun.un_gr,
280
pg->r_pgbuf, pg->r_pgbufsize, &result);
281
} while (error == ERANGE);
282
283
return (error ? NULL : result);
284
}
285
286
#if defined(WITH_CASPER)
287
struct passwd *
288
r_cap_getpwuid(cap_channel_t *cap, uid_t uid, struct r_pgdata *pg)
289
{
290
struct passwd *result = NULL;
291
int error;
292
293
r_pginit(pg);
294
do {
295
error = r_pgexpand(pg);
296
if (error == 0)
297
error = cap_getpwuid_r(cap, uid, &pg->r_pgun.un_pw,
298
pg->r_pgbuf, pg->r_pgbufsize, &result);
299
} while (error == ERANGE);
300
301
return (error ? NULL : result);
302
}
303
304
struct group *
305
r_cap_getgrgid(cap_channel_t *cap, gid_t gid, struct r_pgdata *pg)
306
{
307
struct group *result = NULL;
308
int error;
309
310
r_pginit(pg);
311
do {
312
error = r_pgexpand(pg);
313
if (error == 0)
314
error = cap_getgrgid_r(cap, gid, &pg->r_pgun.un_gr,
315
pg->r_pgbuf, pg->r_pgbufsize, &result);
316
} while (error == ERANGE);
317
318
return (error ? NULL : result);
319
}
320
#endif
321
322