diff options
author | kib <kib@FreeBSD.org> | 2012-05-11 11:29:08 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2012-05-11 11:29:08 +0000 |
commit | 01660ab3d54ad0440b7c193a6fea999259cc5165 (patch) | |
tree | 59dba3c6b0d4c42a99eee48be8b64ee14f158644 | |
parent | de60317f67ebe09acba5170c8ecb3ce44229d388 (diff) | |
download | FreeBSD-src-01660ab3d54ad0440b7c193a6fea999259cc5165.zip FreeBSD-src-01660ab3d54ad0440b7c193a6fea999259cc5165.tar.gz |
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- <slash> character
and ends with one or more trailing <slash> 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
-rw-r--r-- | lib/libc/stdlib/realpath.3 | 8 | ||||
-rw-r--r-- | lib/libc/stdlib/realpath.c | 33 |
2 files changed, 29 insertions, 12 deletions
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); |