]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/lib9p/rfuncs.c
ident(1): Normalizing date format
[FreeBSD/FreeBSD.git] / contrib / lib9p / rfuncs.c
1 /*
2  * Copyright 2016 Chris Torek <chris.torek@gmail.com>
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