summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2010-04-20 10:16:44 +0000
committerkib <kib@FreeBSD.org>2010-04-20 10:16:44 +0000
commit0be03f320b674f387da392d9e4fece5b67a79ae1 (patch)
tree49cd6b92975d51b11b3034ce84d086a90b7f707b
parentdf535bd79d3648211cc1b5783e543ab3c1bea2bc (diff)
downloadFreeBSD-src-0be03f320b674f387da392d9e4fece5b67a79ae1.zip
FreeBSD-src-0be03f320b674f387da392d9e4fece5b67a79ae1.tar.gz
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
-rw-r--r--include/stdlib.h2
-rw-r--r--lib/libc/stdlib/realpath.319
-rw-r--r--lib/libc/stdlib/realpath.c54
3 files changed, 66 insertions, 9 deletions
diff --git a/include/stdlib.h b/include/stdlib.h
index b8cd1a3..b833203 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -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
diff --git a/lib/libc/stdlib/realpath.3 b/lib/libc/stdlib/realpath.3
index 4205a3d..371ee3b2 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 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
diff --git a/lib/libc/stdlib/realpath.c b/lib/libc/stdlib/realpath.c
index 3082f5f..31c93e6 100644
--- a/lib/libc/stdlib/realpath.c
+++ b/lib/libc/stdlib/realpath.c
@@ -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);
}
OpenPOWER on IntegriCloud