mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-21 11:13:30 +00:00
Reimplement dirname(3) to be thread-safe.
Now that we've updated the prototypes of the basename(3) and dirname(3) functions to conform to POSIX, let's go ahead and reimplement dirname(3) in such a way that it's thread-safe, but also guaranteed to succeed. C libraries like glibc, musl and the one that's part of Solaris already follow such an approach. Move the existing implementation to another source file, freebsd11_dirname.c to keep existing users of the API that pass in a constant string happy, using symbol versioning. Put a new version of the function in dirname.c, obtained from CloudABI's C library. This version scans through the pathname string from left to right, normalizing it, while discarding the last pathname component. Reviewed by: emaste, jilles Differential Revision: https://reviews.freebsd.org/D7355
This commit is contained in:
parent
791444089f
commit
e2f6816100
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=303988
@ -29,6 +29,7 @@ SRCS+= __getosreldate.c \
|
||||
devname.c \
|
||||
dirfd.c \
|
||||
dirname.c \
|
||||
dirname_compat.c \
|
||||
disklabel.c \
|
||||
dlfcn.c \
|
||||
drand48.c \
|
||||
|
@ -82,7 +82,6 @@ FBSD_1.0 {
|
||||
daemon;
|
||||
devname;
|
||||
devname_r;
|
||||
dirname;
|
||||
getdiskbyname;
|
||||
dladdr;
|
||||
dlclose;
|
||||
@ -418,6 +417,10 @@ FBSD_1.4 {
|
||||
stravis;
|
||||
};
|
||||
|
||||
FBSD_1.5 {
|
||||
dirname;
|
||||
};
|
||||
|
||||
FBSDprivate_1.0 {
|
||||
/* needed by thread libraries */
|
||||
__thr_jtable;
|
||||
|
@ -16,7 +16,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd July 29, 2016
|
||||
.Dd August 12, 2016
|
||||
.Dt DIRNAME 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -37,6 +37,7 @@ Any trailing
|
||||
.Sq \&/
|
||||
characters are not counted as part of the directory
|
||||
name.
|
||||
.Sh RETURN VALUES
|
||||
If
|
||||
.Fa path
|
||||
is a null pointer, the empty string, or contains no
|
||||
@ -46,40 +47,24 @@ characters,
|
||||
returns a pointer to the string
|
||||
.Qq \&. ,
|
||||
signifying the current directory.
|
||||
.Sh IMPLEMENTATION NOTES
|
||||
The
|
||||
.Fn dirname
|
||||
function
|
||||
returns a pointer to internal storage space allocated on the first call
|
||||
that will be overwritten
|
||||
by subsequent calls.
|
||||
.Pp
|
||||
Other vendor implementations of
|
||||
.Fn dirname
|
||||
may store their result in the input buffer,
|
||||
making it safe to use in multithreaded applications.
|
||||
Future versions of
|
||||
.Fx
|
||||
will follow this approach as well.
|
||||
.Sh RETURN VALUES
|
||||
On successful completion,
|
||||
.Fn dirname
|
||||
returns a pointer to the parent directory of
|
||||
Otherwise,
|
||||
it returns a pointer to the parent directory of
|
||||
.Fa path .
|
||||
.Pp
|
||||
If
|
||||
.Sh IMPLEMENTATION NOTES
|
||||
This implementation of
|
||||
.Fn dirname
|
||||
fails, a null pointer is returned and the global variable
|
||||
.Va errno
|
||||
is set to indicate the error.
|
||||
.Sh ERRORS
|
||||
The following error codes may be set in
|
||||
.Va errno :
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er ENAMETOOLONG
|
||||
The path component to be returned was larger than
|
||||
.Dv MAXPATHLEN .
|
||||
.El
|
||||
uses the buffer provided by the caller to store the resulting parent
|
||||
directory.
|
||||
Other vendor implementations may return a pointer to internal storage
|
||||
space instead.
|
||||
The advantage of the former approach is that it ensures thread-safety,
|
||||
while also placing no upper limit on the supported length of the
|
||||
pathname.
|
||||
.Pp
|
||||
The algorithm used by this implementation also discards redundant
|
||||
slashes and
|
||||
.Qq \&.
|
||||
pathname components from the pathname string.
|
||||
.Sh SEE ALSO
|
||||
.Xr basename 1 ,
|
||||
.Xr dirname 1 ,
|
||||
@ -96,5 +81,10 @@ function first appeared in
|
||||
.Ox 2.2
|
||||
and
|
||||
.Fx 4.2 .
|
||||
.Pp
|
||||
In
|
||||
.Fx 12.0 ,
|
||||
this function was reimplemented to store its result in the provided
|
||||
input buffer.
|
||||
.Sh AUTHORS
|
||||
.An "Todd C. Miller"
|
||||
.An Nuxi, the Netherlands
|
||||
|
@ -1,77 +1,90 @@
|
||||
/* $OpenBSD: dirname.c,v 1.13 2005/08/08 08:05:33 espie Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
/*-
|
||||
* Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided 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.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
char *
|
||||
dirname(char *path)
|
||||
{
|
||||
static char *dname = NULL;
|
||||
size_t len;
|
||||
const char *endp;
|
||||
const char *in, *prev, *begin, *end;
|
||||
char *out;
|
||||
size_t prevlen;
|
||||
bool skipslash;
|
||||
|
||||
if (dname == NULL) {
|
||||
dname = (char *)malloc(MAXPATHLEN);
|
||||
if (dname == NULL)
|
||||
return(NULL);
|
||||
/*
|
||||
* If path is a null pointer or points to an empty string,
|
||||
* dirname() shall return a pointer to the string ".".
|
||||
*/
|
||||
if (path == NULL || *path == '\0')
|
||||
return ((char *)".");
|
||||
|
||||
/* Retain at least one leading slash character. */
|
||||
in = out = *path == '/' ? path + 1 : path;
|
||||
|
||||
skipslash = true;
|
||||
prev = ".";
|
||||
prevlen = 1;
|
||||
for (;;) {
|
||||
/* Extract the next pathname component. */
|
||||
while (*in == '/')
|
||||
++in;
|
||||
begin = in;
|
||||
while (*in != '/' && *in != '\0')
|
||||
++in;
|
||||
end = in;
|
||||
if (begin == end)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Copy over the previous pathname component, except if
|
||||
* it's dot. There is no point in retaining those.
|
||||
*/
|
||||
if (prevlen != 1 || *prev != '.') {
|
||||
if (!skipslash)
|
||||
*out++ = '/';
|
||||
skipslash = false;
|
||||
memmove(out, prev, prevlen);
|
||||
out += prevlen;
|
||||
}
|
||||
|
||||
/* Preserve the pathname component for the next iteration. */
|
||||
prev = begin;
|
||||
prevlen = end - begin;
|
||||
}
|
||||
|
||||
/* Empty or NULL string gets treated as "." */
|
||||
if (path == NULL || *path == '\0') {
|
||||
dname[0] = '.';
|
||||
dname[1] = '\0';
|
||||
return (dname);
|
||||
}
|
||||
|
||||
/* Strip any trailing slashes */
|
||||
endp = path + strlen(path) - 1;
|
||||
while (endp > path && *endp == '/')
|
||||
endp--;
|
||||
|
||||
/* Find the start of the dir */
|
||||
while (endp > path && *endp != '/')
|
||||
endp--;
|
||||
|
||||
/* Either the dir is "/" or there are no slashes */
|
||||
if (endp == path) {
|
||||
dname[0] = *endp == '/' ? '/' : '.';
|
||||
dname[1] = '\0';
|
||||
return (dname);
|
||||
} else {
|
||||
/* Move forward past the separating slashes */
|
||||
do {
|
||||
endp--;
|
||||
} while (endp > path && *endp == '/');
|
||||
}
|
||||
|
||||
len = endp - path + 1;
|
||||
if (len >= MAXPATHLEN) {
|
||||
errno = ENAMETOOLONG;
|
||||
return (NULL);
|
||||
}
|
||||
memcpy(dname, path, len);
|
||||
dname[len] = '\0';
|
||||
return (dname);
|
||||
/*
|
||||
* If path does not contain a '/', then dirname() shall return a
|
||||
* pointer to the string ".".
|
||||
*/
|
||||
if (out == path)
|
||||
*out++ = '.';
|
||||
*out = '\0';
|
||||
return (path);
|
||||
}
|
||||
|
79
lib/libc/gen/dirname_compat.c
Normal file
79
lib/libc/gen/dirname_compat.c
Normal file
@ -0,0 +1,79 @@
|
||||
/* $OpenBSD: dirname.c,v 1.13 2005/08/08 08:05:33 espie Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
char *
|
||||
__freebsd11_dirname(char *path)
|
||||
{
|
||||
static char *dname = NULL;
|
||||
size_t len;
|
||||
const char *endp;
|
||||
|
||||
if (dname == NULL) {
|
||||
dname = (char *)malloc(MAXPATHLEN);
|
||||
if (dname == NULL)
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/* Empty or NULL string gets treated as "." */
|
||||
if (path == NULL || *path == '\0') {
|
||||
dname[0] = '.';
|
||||
dname[1] = '\0';
|
||||
return (dname);
|
||||
}
|
||||
|
||||
/* Strip any trailing slashes */
|
||||
endp = path + strlen(path) - 1;
|
||||
while (endp > path && *endp == '/')
|
||||
endp--;
|
||||
|
||||
/* Find the start of the dir */
|
||||
while (endp > path && *endp != '/')
|
||||
endp--;
|
||||
|
||||
/* Either the dir is "/" or there are no slashes */
|
||||
if (endp == path) {
|
||||
dname[0] = *endp == '/' ? '/' : '.';
|
||||
dname[1] = '\0';
|
||||
return (dname);
|
||||
} else {
|
||||
/* Move forward past the separating slashes */
|
||||
do {
|
||||
endp--;
|
||||
} while (endp > path && *endp == '/');
|
||||
}
|
||||
|
||||
len = endp - path + 1;
|
||||
if (len >= MAXPATHLEN) {
|
||||
errno = ENAMETOOLONG;
|
||||
return (NULL);
|
||||
}
|
||||
memcpy(dname, path, len);
|
||||
dname[len] = '\0';
|
||||
return (dname);
|
||||
}
|
||||
|
||||
__sym_compat(dirname, __freebsd11_dirname, FBSD_1.0);
|
Loading…
Reference in New Issue
Block a user