2 * Copyright 2016 Chris Torek <chris.torek@gmail.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted providing that the following conditions
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.
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.
33 #if defined(WITH_CASPER)
34 #include <libcasper.h>
35 #include <casper/cap_pwd.h>
36 #include <casper/cap_grp.h>
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
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.
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
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>.
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).
64 r_basename(const char *path, char *buf, size_t bufsize)
66 const char *endp, *comp;
70 * NULL or empty path means ".". This is perhaps overly
71 * forgiving but matches libc basename_r(), and avoids
72 * breaking the code below.
74 if (path == NULL || *path == '\0') {
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).
84 endp = path + strlen(path) - 1;
85 while (*endp == '/' && endp > path)
87 /* Invariant: *endp != '/' || endp == path */
89 /* then endp==path and hence entire path is "/" */
94 * We handled empty strings earlier, and
95 * we just proved *endp != '/'. Hence
96 * we have a non-empty basename, ending
99 * Back up one path name component. The
100 * part between these two is the basename.
102 * Note that we only stop backing up when
103 * either comp==path, or comp[-1] is '/'.
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 '/').
110 * Or, suppose path[0] is NOT '/'. Then,
111 * either there are no '/'s at all and
112 * comp==path, or comp[-1] is '/'.
114 * In all cases, we want all bytes from *comp
115 * to *endp, inclusive.
118 while (comp > path && comp[-1] != '/')
120 len = (size_t)(endp - comp + 1);
124 buf = malloc(len + 1);
128 if (len >= bufsize) {
129 errno = ENAMETOOLONG;
133 memcpy(buf, comp, len);
139 * This is much like POSIX dirname(), but is reentrant.
141 * We examine a path, find the directory portion, and copy that
142 * to a user supplied buffer <buf> of the given size <bufsize>.
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).
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>.
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).
159 r_dirname(const char *path, char *buf, size_t bufsize)
161 const char *endp, *dirpart;
165 * NULL or empty path means ".". This is perhaps overly
166 * forgiving but matches libc dirname(), and avoids breaking
169 if (path == NULL || *path == '\0') {
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.
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.
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).
195 endp = path + strlen(path) - 1;
196 while (endp > path && *endp == '/')
198 while (endp > path && *endp != '/')
200 while (endp > path && *endp == '/')
203 len = (size_t)(endp - path + 1);
204 if (endp == path && *endp != '/')
210 buf = malloc(len + 1);
214 if (len >= bufsize) {
215 errno = ENAMETOOLONG;
219 memcpy(buf, dirpart, len);
225 r_pginit(struct r_pgdata *pg)
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 */
234 r_pgexpand(struct r_pgdata *pg)
238 nsize = pg->r_pgbufsize << 1;
239 if (nsize >= (1 << 20) ||
240 (pg->r_pgbuf = realloc(pg->r_pgbuf, nsize)) == NULL)
246 r_pgfree(struct r_pgdata *pg)
253 r_getpwuid(uid_t uid, struct r_pgdata *pg)
255 struct passwd *result = NULL;
260 error = r_pgexpand(pg);
262 error = getpwuid_r(uid, &pg->r_pgun.un_pw,
263 pg->r_pgbuf, pg->r_pgbufsize, &result);
264 } while (error == ERANGE);
266 return (error ? NULL : result);
270 r_getgrgid(gid_t gid, struct r_pgdata *pg)
272 struct group *result = NULL;
277 error = r_pgexpand(pg);
279 error = getgrgid_r(gid, &pg->r_pgun.un_gr,
280 pg->r_pgbuf, pg->r_pgbufsize, &result);
281 } while (error == ERANGE);
283 return (error ? NULL : result);
286 #if defined(WITH_CASPER)
288 r_cap_getpwuid(cap_channel_t *cap, uid_t uid, struct r_pgdata *pg)
290 struct passwd *result = NULL;
295 error = r_pgexpand(pg);
297 error = cap_getpwuid_r(cap, uid, &pg->r_pgun.un_pw,
298 pg->r_pgbuf, pg->r_pgbufsize, &result);
299 } while (error == ERANGE);
301 return (error ? NULL : result);
305 r_cap_getgrgid(cap_channel_t *cap, gid_t gid, struct r_pgdata *pg)
307 struct group *result = NULL;
312 error = r_pgexpand(pg);
314 error = cap_getgrgid_r(cap, gid, &pg->r_pgun.un_gr,
315 pg->r_pgbuf, pg->r_pgbufsize, &result);
316 } while (error == ERANGE);
318 return (error ? NULL : result);