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