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 321