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
This commit is contained in:
Konstantin Belousov 2012-05-11 11:29:08 +00:00
parent 71b310be7b
commit fdbe55fccb
2 changed files with 29 additions and 12 deletions

View File

@ -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

View File

@ -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);