Date: Tue, 31 May 2016 01:31:11 -0700 From: Chris Torek <torek@torek.net> To: Ed Schouten <ed@FreeBSD.org> Cc: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: Re: svn commit: r301024 - head/sbin/swapon Message-ID: <201605310831.u4V8VBg1081055@elf.torek.net> In-Reply-To: Your message of "Tue, 31 May 2016 06:45:19 -0000." <201605310645.u4V6jJdF078504@repo.freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
> This change makes the code use the POSIX basename() function. It has the > advantage that (if implemented correctly), it also imposes no restrict > on the pathname length. I'm not sure what the parenthetic remark "if implemented correctly" means, but libc basename() has this in it: char * basename(const char *path) { static char *bname = NULL; if (bname == NULL) { bname = (char *)malloc(MAXPATHLEN); if (bname == NULL) return (NULL); } return (basename_r(path, bname)); } which means that it is not only not thread-safe, it also imposes restrictions on pathname length. I recently wrote a pair of can-be-thread-safe yet can-be- unlimited-path-length yet can-be-backwards-compatible basename and dirname functions (I tested to see if they produced the same errno's and oddball outputs like for dirname("///")). I'll toss them in here in case anyone has some enthusiasm for fixing the mess. Of course they are equally non-standard, unless we can convince a future POSIX committee or something. (I also wrote some halfway useable reentrant getpw* and getgr* functions but they need more thought and do not play well with nsswitch so I am not including them here. NB: rfuncs.h just contains declarations for these reentrant r_* functions.) Chris ------- /* * Copyright 2016 Chris Torek <chris.torek@gmail.com> * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted providing that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include <errno.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "rfuncs.h" /* * This is essentially a clone of the BSD basename_r function, * which is like POSIX basename() but puts the result in a user * supplied buffer. * * In BSD basename_r, the buffer must be least MAXPATHLEN bytes * long. In our case we take the size of the buffer as an argument. * * Note that it's impossible in general to do this without * a temporary buffer since basename("foo/bar") is "bar", * but basename("foo/bar/") is still "bar" -- no trailing * slash is allowed. * * The return value is your supplied buffer <buf>, or NULL if * the length of the basename of the supplied <path> equals or * exceeds your indicated <bufsize>. * * As a special but useful case, if you supply NULL for the <buf> * argument, we allocate the buffer dynamically to match the * basename, i.e., the result is basically strdup()ed for you. * In this case <bufsize> is ignored (recommended: pass 0 here). */ char * r_basename(const char *path, char *buf, size_t bufsize) { const char *endp, *comp; size_t len; /* * NULL or empty path means ".". This is perhaps overly * forgiving but matches libc basename_r(), and avoids * breaking the code below. */ if (path == NULL || *path == '\0') { comp = "."; len = 1; } else { /* * Back up over any trailing slashes. If we reach * the top of the path and it's still a trailing * slash, it's also a leading slash and the entire * path is just "/" (or "//", or "///", etc). */ endp = path + strlen(path) - 1; while (*endp == '/' && endp > path) endp--; /* Invariant: *endp != '/' || endp == path */ if (*endp == '/') { /* then endp==path and hence entire path is "/" */ comp = "/"; len = 1; } else { /* * We handled empty strings earlier, and * we just proved *endp != '/'. Hence * we have a non-empty basename, ending * at endp. * * Back up one path name component. The * part between these two is the basename. * * Note that we only stop backing up when * either comp==path, or comp[-1] is '/'. * * Suppose path[0] is '/'. Then, since *endp * is *not* '/', we had comp>path initially, and * stopped backing up because we found a '/' * (perhaps path[0], perhaps a later '/'). * * Or, suppose path[0] is NOT '/'. Then, * either there are no '/'s at all and * comp==path, or comp[-1] is '/'. * * In all cases, we want all bytes from *comp * to *endp, inclusive. */ comp = endp; while (comp > path && comp[-1] != '/') comp--; len = (size_t)(endp - comp + 1); } } if (buf == NULL) { buf = malloc(len + 1); if (buf == NULL) return (NULL); } else { if (len >= bufsize) { errno = ENAMETOOLONG; return (NULL); } } memcpy(buf, comp, len); buf[len] = '\0'; return (buf); } /* * This is much like POSIX dirname(), but is reentrant. * * We examine a path, find the directory portion, and copy that * to a user supplied buffer <buf> of the given size <bufsize>. * * Note that dirname("/foo/bar/") is "/foo", dirname("/foo") is "/", * and dirname("////") is "/". However, dirname("////foo/bar") is * "////foo" (we do not resolve these leading slashes away -- this * matches the BSD libc behavior). * * The return value is your supplied buffer <buf>, or NULL if * the length of the dirname of the supplied <path> equals or * exceeds your indicated <bufsize>. * * As a special but useful case, if you supply NULL for the <buf> * argument, we allocate the buffer dynamically to match the * dirname, i.e., the result is basically strdup()ed for you. * In this case <bufsize> is ignored (recommended: pass 0 here). */ char * r_dirname(const char *path, char *buf, size_t bufsize) { const char *endp, *dirpart; size_t len; /* * NULL or empty path means ".". This is perhaps overly * forgiving but matches libc dirname(), and avoids breaking * the code below. */ if (path == NULL || *path == '\0') { dirpart = "."; len = 1; } else { /* * Back up over any trailing slashes, then back up * one path name, then back up over more slashes. * In all cases, stop as soon as endp==path so * that we do not back out of the buffer entirely. * * The first loop takes care of trailing slashes * in names like "/foo/bar//" (where the dirname * part is to be "/foo"), the second strips out * the non-dir-name part, and the third leaves us * pointing to the end of the directory component. * * If the entire name is of the form "/foo" or * "//foo" (or "/foo/", etc, but we already * handled trailing slashes), we end up pointing * to the leading "/", which is what we want; but * if it is of the form "foo" (or "foo/", etc) we * point to a non-slash. So, if (and only if) * endp==path AND *endp is not '/', the dirname is * ".", but in all cases, the LENGTH of the * dirname is (endp-path+1). */ endp = path + strlen(path) - 1; while (endp > path && *endp == '/') endp--; while (endp > path && *endp != '/') endp--; while (endp > path && *endp == '/') endp--; len = (size_t)(endp - path + 1); if (endp == path && *endp != '/') dirpart = "."; else dirpart = path; } if (buf == NULL) { buf = malloc(len + 1); if (buf == NULL) return (NULL); } else { if (len >= bufsize) { errno = ENAMETOOLONG; return (NULL); } } memcpy(buf, dirpart, len); buf[len] = '\0'; return (buf); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201605310831.u4V8VBg1081055>