From 01660ab3d54ad0440b7c193a6fea999259cc5165 Mon Sep 17 00:00:00 2001 From: kib Date: Fri, 11 May 2012 11:29:08 +0000 Subject: According to SUSv4, realpath(3) must fail if [ENOENT] A component of file_name does not name an existing file or file_name points to an empty string. [ENOTDIR] A component of the path prefix is not a directory, or the file_name argument contains at least one non- character and ends with one or more trailing characters and the last pathname component names an existing file that is neither a directory nor a symbolic link to a directory. Add checks for the listed conditions, and set errno accordingly. Update the realpath(3) manpage to mention SUS behaviour. Remove the requirement to include sys/param.h before stdlib.h. PR: 128933 MFC after: 3 weeks --- lib/libc/stdlib/realpath.3 | 8 ++++---- lib/libc/stdlib/realpath.c | 33 +++++++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 12 deletions(-) (limited to 'lib/libc') diff --git a/lib/libc/stdlib/realpath.3 b/lib/libc/stdlib/realpath.3 index fec5258..c7384d6 100644 --- a/lib/libc/stdlib/realpath.3 +++ b/lib/libc/stdlib/realpath.3 @@ -31,7 +31,7 @@ .\" @(#)realpath.3 8.2 (Berkeley) 2/16/94 .\" $FreeBSD$ .\" -.Dd April 19, 2010 +.Dd May 11, 2012 .Dt REALPATH 3 .Os .Sh NAME @@ -40,7 +40,6 @@ .Sh LIBRARY .Lb libc .Sh SYNOPSIS -.In sys/param.h .In stdlib.h .Ft "char *" .Fn realpath "const char *pathname" "char *resolved_path" @@ -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 2c9562e..ffbf8ec 100644 --- a/lib/libc/stdlib/realpath.c +++ b/lib/libc/stdlib/realpath.c @@ -132,8 +132,29 @@ 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. The path + * before slash shall point to a directory. + * + * Only the trailing slashes are not covered + * by other checks in the loop, but we verify + * the prefix for any (rare) "//" or "/\0" + * occurence to not implement lookahead. + */ + if (lstat(resolved, &sb) != 0) { + if (m) + free(resolved); + return (NULL); + } + if (!S_ISDIR(sb.st_mode)) { + if (m) + free(resolved); + errno = ENOTDIR; + return (NULL); + } continue; + } else if (strcmp(next_token, ".") == 0) continue; else if (strcmp(next_token, "..") == 0) { @@ -151,9 +172,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) { @@ -163,10 +182,8 @@ 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 (errno != ENOENT || p != NULL) + errno = ENOTDIR; if (m) free(resolved); return (NULL); -- cgit v1.1