svn commit: r301024 - head/sbin/swapon
Chris Torek
torek at torek.net
Tue May 31 09:00:42 UTC 2016
> 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 at 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);
}
More information about the svn-src-all
mailing list