Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 12 Aug 2016 07:03:58 +0000 (UTC)
From:      Ed Schouten <ed@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r303988 - head/lib/libc/gen
Message-ID:  <201608120703.u7C73whf007189@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ed
Date: Fri Aug 12 07:03:58 2016
New Revision: 303988
URL: https://svnweb.freebsd.org/changeset/base/303988

Log:
  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

Added:
  head/lib/libc/gen/dirname_compat.c
     - copied, changed from r303452, head/lib/libc/gen/dirname.c
Modified:
  head/lib/libc/gen/Makefile.inc
  head/lib/libc/gen/Symbol.map
  head/lib/libc/gen/dirname.3
  head/lib/libc/gen/dirname.c

Modified: head/lib/libc/gen/Makefile.inc
==============================================================================
--- head/lib/libc/gen/Makefile.inc	Fri Aug 12 06:19:40 2016	(r303987)
+++ head/lib/libc/gen/Makefile.inc	Fri Aug 12 07:03:58 2016	(r303988)
@@ -29,6 +29,7 @@ SRCS+=	__getosreldate.c \
 	devname.c \
 	dirfd.c \
 	dirname.c \
+	dirname_compat.c \
 	disklabel.c \
 	dlfcn.c \
 	drand48.c \

Modified: head/lib/libc/gen/Symbol.map
==============================================================================
--- head/lib/libc/gen/Symbol.map	Fri Aug 12 06:19:40 2016	(r303987)
+++ head/lib/libc/gen/Symbol.map	Fri Aug 12 07:03:58 2016	(r303988)
@@ -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;

Modified: head/lib/libc/gen/dirname.3
==============================================================================
--- head/lib/libc/gen/dirname.3	Fri Aug 12 06:19:40 2016	(r303987)
+++ head/lib/libc/gen/dirname.3	Fri Aug 12 07:03:58 2016	(r303988)
@@ -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.
+Otherwise,
+it returns a pointer to the parent directory of
+.Fa path .
 .Sh IMPLEMENTATION NOTES
-The
+This implementation of
 .Fn dirname
-function
-returns a pointer to internal storage space allocated on the first call
-that will be overwritten
-by subsequent calls.
+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
-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
-.Fa path .
-.Pp
-If
-.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
+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

Modified: head/lib/libc/gen/dirname.c
==============================================================================
--- head/lib/libc/gen/dirname.c	Fri Aug 12 06:19:40 2016	(r303987)
+++ head/lib/libc/gen/dirname.c	Fri Aug 12 07:03:58 2016	(r303988)
@@ -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;
-
-	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);
+	const char *in, *prev, *begin, *end;
+	char *out;
+	size_t prevlen;
+	bool skipslash;
+
+	/*
+	 * 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;
 	}
 
-	/* 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);
 }

Copied and modified: head/lib/libc/gen/dirname_compat.c (from r303452, head/lib/libc/gen/dirname.c)
==============================================================================
--- head/lib/libc/gen/dirname.c	Thu Jul 28 16:54:12 2016	(r303452, copy source)
+++ head/lib/libc/gen/dirname_compat.c	Fri Aug 12 07:03:58 2016	(r303988)
@@ -26,7 +26,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 
 char *
-dirname(char *path)
+__freebsd11_dirname(char *path)
 {
 	static char *dname = NULL;
 	size_t len;
@@ -75,3 +75,5 @@ dirname(char *path)
 	dname[len] = '\0';
 	return (dname);
 }
+
+__sym_compat(dirname, __freebsd11_dirname, FBSD_1.0);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201608120703.u7C73whf007189>