summaryrefslogtreecommitdiff
path: root/libc/stdlib/realpath.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-12-03 08:59:12 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-12-03 08:59:12 +0000
commitfa3a19983dbd3df1fdcb1a9a583eab771f3412fd (patch)
treea4a7cfbc3a14fe8e87a18397d5969d93faa816c8 /libc/stdlib/realpath.c
parent6e91c122e5abb7f5cd0bd2db7d9cfbee6fa6b39f (diff)
realpath: reduce stack usage from 3*PATH_MAX (12k) to 1*PATH_MAX (4k).
reduction is achieved by direct use of user-supplied PATH_MAX sized buffer for result (without intermediate copy) and changes in copy_buf[] usage - now it is used for both "source" pathname and link name (it works because they have to be less than PATH_MAX combined, otherwise we return NULL).
Diffstat (limited to 'libc/stdlib/realpath.c')
-rw-r--r--libc/stdlib/realpath.c70
1 files changed, 36 insertions, 34 deletions
diff --git a/libc/stdlib/realpath.c b/libc/stdlib/realpath.c
index aae8580a5..99afbde0c 100644
--- a/libc/stdlib/realpath.c
+++ b/libc/stdlib/realpath.c
@@ -43,20 +43,23 @@ libc_hidden_proto(getcwd)
#define MAX_READLINKS 32
#ifdef __STDC__
-char *realpath(const char *path, char resolved_path[])
+char *realpath(const char *path, char got_path[])
#else
-char *realpath(path, resolved_path)
+char *realpath(path, got_path)
const char *path;
-char resolved_path[];
+char got_path[];
#endif
{
char copy_path[PATH_MAX];
- char link_path[PATH_MAX];
- char got_path[PATH_MAX];
- char *new_path = got_path;
+ /* use user supplied buffer directly - reduces stack usage */
+ /* char got_path[PATH_MAX]; */
char *max_path;
+ char *new_path;
+ size_t path_len;
int readlinks = 0;
- int n;
+#ifdef S_IFLNK
+ int link_len;
+#endif
if (path == NULL) {
__set_errno(EINVAL);
@@ -67,17 +70,20 @@ char resolved_path[];
return NULL;
}
/* Make a copy of the source path since we may need to modify it. */
- if (strlen(path) >= PATH_MAX - 2) {
+ path_len = strlen(path);
+ if (path_len >= PATH_MAX - 2) {
__set_errno(ENAMETOOLONG);
return NULL;
}
- strcpy(copy_path, path);
- path = copy_path;
- max_path = copy_path + PATH_MAX - 2;
- /* If it's a relative pathname use getcwd for starters. */
+ /* Copy so that path is at the end of copy_path[] */
+ strcpy(copy_path + (PATH_MAX-1) - path_len, path);
+ path = copy_path + (PATH_MAX-1) - path_len;
+ max_path = got_path + PATH_MAX - 2; /* points to last non-NUL char */
+ new_path = got_path;
if (*path != '/') {
- /* Ohoo... */
- getcwd(new_path, PATH_MAX - 1);
+ /* If it's a relative pathname use getcwd for starters. */
+ if (!getcwd(new_path, PATH_MAX - 1))
+ return NULL;
new_path += strlen(new_path);
if (new_path[-1] != '/')
*new_path++ = '/';
@@ -112,7 +118,7 @@ char resolved_path[];
}
/* Safely copy the next pathname component. */
while (*path != '\0' && *path != '/') {
- if (path > max_path) {
+ if (new_path > max_path) {
__set_errno(ENAMETOOLONG);
return NULL;
}
@@ -124,35 +130,32 @@ char resolved_path[];
__set_errno(ELOOP);
return NULL;
}
- /* See if latest pathname component is a symlink. */
+ path_len = strlen(path);
+ /* See if last (so far) pathname component is a symlink. */
*new_path = '\0';
- n = readlink(got_path, link_path, PATH_MAX - 1);
- if (n < 0) {
+ link_len = readlink(got_path, copy_path, PATH_MAX - 1);
+ if (link_len < 0) {
/* EINVAL means the file exists but isn't a symlink. */
if (errno != EINVAL) {
- /* Make sure it's null terminated. */
- *new_path = '\0';
- strcpy(resolved_path, got_path);
return NULL;
}
} else {
+ /* Safe sex check. */
+ if (path_len + link_len >= PATH_MAX - 2) {
+ __set_errno(ENAMETOOLONG);
+ return NULL;
+ }
/* Note: readlink doesn't add the null byte. */
- link_path[n] = '\0';
- if (*link_path == '/')
+ /* copy_path[link_len] = '\0'; - we don't need it too */
+ if (*copy_path == '/')
/* Start over for an absolute symlink. */
new_path = got_path;
else
/* Otherwise back up over this component. */
while (*(--new_path) != '/');
- /* Safe sex check. */
- if (strlen(path) + n >= PATH_MAX - 2) {
- __set_errno(ENAMETOOLONG);
- return NULL;
- }
- /* Insert symlink contents into path. */
- strcat(link_path, path);
- strcpy(copy_path, link_path);
- path = copy_path;
+ /* Prepend symlink contents to path. */
+ memmove(copy_path + (PATH_MAX-1) - link_len - path_len, copy_path, link_len);
+ path = copy_path + (PATH_MAX-1) - link_len - path_len;
}
#endif /* S_IFLNK */
*new_path++ = '/';
@@ -162,6 +165,5 @@ char resolved_path[];
new_path--;
/* Make sure it's null terminated. */
*new_path = '\0';
- strcpy(resolved_path, got_path);
- return resolved_path;
+ return got_path;
}