From 8855a906566ece070ee9535e491544b7a3cbee55 Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Tue, 17 Mar 2015 23:56:56 +0100
Subject: [PATCH] libc/realpath: Better POSIX conformance regarding ENOENT and
 ENOTDIR.

Reported-by: Robin Hahling <robin.hahling@gw-computing.net>
Taken-from:  FreeBSD
---
 lib/libc/stdlib/realpath.3 | 12 ++++++------
 lib/libc/stdlib/realpath.c | 26 +++++++++++++-------------
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/lib/libc/stdlib/realpath.3 b/lib/libc/stdlib/realpath.3
index a674a8c..dd05443 100644
--- a/lib/libc/stdlib/realpath.3
+++ b/lib/libc/stdlib/realpath.3
@@ -29,9 +29,9 @@
 .\" SUCH DAMAGE.
 .\"
 .\"     @(#)realpath.3	8.2 (Berkeley) 2/16/94
-.\" $FreeBSD: src/lib/libc/stdlib/realpath.3 SVN 227090 2011/11/04 ed $
+.\" $FreeBSD: head/lib/libc/stdlib/realpath.3 251672 2013-06-13 00:19:30Z emaste $
 .\"
-.Dd May 5, 2012
+.Dd March 17, 2015
 .Dt REALPATH 3
 .Os
 .Sh NAME
@@ -40,10 +40,9 @@
 .Sh LIBRARY
 .Lb libc
 .Sh SYNOPSIS
-.In sys/param.h
 .In stdlib.h
 .Ft "char *"
-.Fn realpath "const char *pathname" "char *resolved_path"
+.Fn realpath "const char * restrict pathname" "char * restrict resolved_path"
 .Sh DESCRIPTION
 The
 .Fn realpath
@@ -72,11 +71,12 @@ The
 function will resolve both absolute and relative paths
 and return the absolute pathname corresponding to
 .Fa pathname .
-All but the last component of
+All components of
 .Fa pathname
 must exist when
 .Fn realpath
-is called.
+is called, and all but the last component must name either directories or
+symlinks pointing to the directories.
 .Sh "RETURN VALUES"
 The
 .Fn realpath
diff --git a/lib/libc/stdlib/realpath.c b/lib/libc/stdlib/realpath.c
index ccffda6..68e89cd 100644
--- a/lib/libc/stdlib/realpath.c
+++ b/lib/libc/stdlib/realpath.c
@@ -25,7 +25,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libc/stdlib/realpath.c SVN 227090 2011/11/04 ed $
+ * @(#)realpath.c	8.1 (Berkeley) 2/16/94
+ * $FreeBSD: head/lib/libc/stdlib/realpath.c 264417 2014-04-13 19:48:28Z jilles $
  */
 
 #include "namespace.h"
@@ -50,7 +51,7 @@ realpath(const char * __restrict path, char * __restrict resolved)
 	char *p, *q, *s;
 	size_t left_len, resolved_len;
 	unsigned symlinks;
-	int m, serrno, slen;
+	int m, slen;
 	char left[PATH_MAX], next_token[PATH_MAX], my_symlink[PATH_MAX];
 
 	if (path == NULL) {
@@ -61,7 +62,6 @@ realpath(const char * __restrict path, char * __restrict resolved)
 		errno = ENOENT;
 		return (NULL);
 	}
-	serrno = errno;
 	if (resolved == NULL) {
 		resolved = malloc(PATH_MAX);
 		if (resolved == NULL)
@@ -128,11 +128,12 @@ realpath(const char * __restrict path, char * __restrict resolved)
 			resolved[resolved_len++] = '/';
 			resolved[resolved_len] = '\0';
 		}
-		if (next_token[0] == '\0')
+		if (next_token[0] == '\0') {
+			/* Handle consequential slashes. */
 			continue;
-		else if (strcmp(next_token, ".") == 0)
+		} else if (strcmp(next_token, ".") == 0) {
 			continue;
-		else if (strcmp(next_token, "..") == 0) {
+		} else if (strcmp(next_token, "..") == 0) {
 			/*
 			 * Strip the last path component except when we have
 			 * single "/"
@@ -147,9 +148,7 @@ realpath(const char * __restrict path, char * __restrict resolved)
 		}
 
 		/*
-		 * Append the next path component and lstat() it. If
-		 * lstat() fails we still can return successfully if
-		 * there are no more path components left.
+		 * Append the next path component and lstat() it.
 		 */
 		resolved_len = strlcat(resolved, next_token, PATH_MAX);
 		if (resolved_len >= PATH_MAX) {
@@ -159,10 +158,6 @@ realpath(const char * __restrict path, char * __restrict resolved)
 			return (NULL);
 		}
 		if (lstat(resolved, &sb) != 0) {
-			if (errno == ENOENT && p == NULL) {
-				errno = serrno;
-				return (resolved);
-			}
 			if (m)
 				free(resolved);
 			return (NULL);
@@ -218,6 +213,11 @@ realpath(const char * __restrict path, char * __restrict resolved)
 				}
 			}
 			left_len = strlcpy(left, my_symlink, sizeof(left));
+		} else if (!S_ISDIR(sb.st_mode) && p != NULL) {
+			if (m)
+				free(resolved);
+			errno = ENOTDIR;
+			return (NULL);
 		}
 	}
 
-- 
2.3.1

