diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-12-03 08:59:12 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-12-03 08:59:12 +0000 |
commit | fa3a19983dbd3df1fdcb1a9a583eab771f3412fd (patch) | |
tree | a4a7cfbc3a14fe8e87a18397d5969d93faa816c8 /libc/stdlib | |
parent | 6e91c122e5abb7f5cd0bd2db7d9cfbee6fa6b39f (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')
-rw-r--r-- | libc/stdlib/realpath.c | 70 |
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; } |