Slightly modernize realpath(3).

SUSv4 requires that implementation returns EINVAL if supplied path is NULL,
and ENOENT if path is empty string [1].
Bring prototype in conformance with SUSv4, adding restrict keywords.
Allow the resolved path buffer pointer be NULL, in which case realpath(3)
allocates storage with malloc().

PR:	kern/121897 [1]
MFC after:	2 weeks
This commit is contained in:
Konstantin Belousov 2010-04-20 10:16:44 +00:00
parent ee94f0a272
commit 9d79ec20fb
3 changed files with 66 additions and 9 deletions

View File

@ -201,7 +201,7 @@ int posix_openpt(int);
char *ptsname(int);
int putenv(char *);
long random(void);
char *realpath(const char *, char resolved_path[]);
char *realpath(const char * __restrict, char * __restrict);
unsigned short
*seed48(unsigned short[3]);
#ifndef _SETKEY_DECLARED

View File

@ -31,7 +31,7 @@
.\" @(#)realpath.3 8.2 (Berkeley) 2/16/94
.\" $FreeBSD$
.\"
.Dd February 16, 1994
.Dd April 19, 2010
.Dt REALPATH 3
.Os
.Sh NAME
@ -43,7 +43,7 @@
.In sys/param.h
.In stdlib.h
.Ft "char *"
.Fn realpath "const char *pathname" "char resolved_path[PATH_MAX]"
.Fn realpath "const char *pathname" "char *resolved_path"
.Sh DESCRIPTION
The
.Fn realpath
@ -64,7 +64,8 @@ argument
.Em must
refer to a buffer capable of storing at least
.Dv PATH_MAX
characters.
characters, or be
.Dv NULL .
.Pp
The
.Fn realpath
@ -82,6 +83,13 @@ The
function returns
.Fa resolved_path
on success.
If the function was supplied
.Dv NULL
as
.Fa resolved_path ,
and operation did not cause errors, the returned value is
a null-terminated string in a buffer allocated by a call to
.Fn malloc 3 .
If an error occurs,
.Fn realpath
returns
@ -89,6 +97,11 @@ returns
and
.Fa resolved_path
contains the pathname which caused the problem.
If
.Fa resolved_path
was
.Dv NULL ,
then information of the failed pathname component is lost.
.Sh ERRORS
The function
.Fn realpath

View File

@ -43,23 +43,37 @@ __FBSDID("$FreeBSD$");
#include "un-namespace.h"
/*
* char *realpath(const char *path, char resolved[PATH_MAX]);
*
* Find the real name of path, by removing all ".", ".." and symlink
* components. Returns (resolved) on success, or (NULL) on failure,
* in which case the path which caused trouble is left in (resolved).
*/
char *
realpath(const char *path, char resolved[PATH_MAX])
realpath(const char * __restrict path, char * __restrict resolved)
{
struct stat sb;
char *p, *q, *s;
size_t left_len, resolved_len;
unsigned symlinks;
int serrno, slen;
int serrno, slen, m;
char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
if (path == NULL) {
errno = EINVAL;
return (NULL);
}
if (path[0] == '\0') {
errno = ENOENT;
return (NULL);
}
serrno = errno;
if (resolved == NULL) {
resolved = malloc(PATH_MAX);
if (resolved == NULL)
return (NULL);
m = 1;
} else
m = 0;
symlinks = 0;
if (path[0] == '/') {
resolved[0] = '/';
@ -71,12 +85,19 @@ realpath(const char *path, char resolved[PATH_MAX])
} else {
if (getcwd(resolved, PATH_MAX) == NULL) {
strlcpy(resolved, ".", PATH_MAX);
if (m) {
serrno = errno;
free(resolved);
errno = serrno;
}
return (NULL);
}
resolved_len = strlen(resolved);
left_len = strlcpy(left, path, sizeof(left));
}
if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
if (m)
free(resolved);
errno = ENAMETOOLONG;
return (NULL);
}
@ -92,6 +113,8 @@ realpath(const char *path, char resolved[PATH_MAX])
p = strchr(left, '/');
s = p ? p : left + left_len;
if (s - left >= sizeof(next_token)) {
if (m)
free(resolved);
errno = ENAMETOOLONG;
return (NULL);
}
@ -102,6 +125,8 @@ realpath(const char *path, char resolved[PATH_MAX])
memmove(left, s + 1, left_len + 1);
if (resolved[resolved_len - 1] != '/') {
if (resolved_len + 1 >= PATH_MAX) {
if (m)
free(resolved);
errno = ENAMETOOLONG;
return (NULL);
}
@ -133,6 +158,8 @@ realpath(const char *path, char resolved[PATH_MAX])
*/
resolved_len = strlcat(resolved, next_token, PATH_MAX);
if (resolved_len >= PATH_MAX) {
if (m)
free(resolved);
errno = ENAMETOOLONG;
return (NULL);
}
@ -141,16 +168,29 @@ realpath(const char *path, char resolved[PATH_MAX])
errno = serrno;
return (resolved);
}
if (m) {
serrno = errno;
free(resolved);
errno = serrno;
}
return (NULL);
}
if (S_ISLNK(sb.st_mode)) {
if (symlinks++ > MAXSYMLINKS) {
if (m)
free(resolved);
errno = ELOOP;
return (NULL);
}
slen = readlink(resolved, symlink, sizeof(symlink) - 1);
if (slen < 0)
if (slen < 0) {
if (m) {
serrno = errno;
free(resolved);
errno = serrno;
}
return (NULL);
}
symlink[slen] = '\0';
if (symlink[0] == '/') {
resolved[1] = 0;
@ -171,6 +211,8 @@ realpath(const char *path, char resolved[PATH_MAX])
if (p != NULL) {
if (symlink[slen - 1] != '/') {
if (slen + 1 >= sizeof(symlink)) {
if (m)
free(resolved);
errno = ENAMETOOLONG;
return (NULL);
}
@ -179,6 +221,8 @@ realpath(const char *path, char resolved[PATH_MAX])
}
left_len = strlcat(symlink, left, sizeof(left));
if (left_len >= sizeof(left)) {
if (m)
free(resolved);
errno = ENAMETOOLONG;
return (NULL);
}