2 * realpath.c -- canonicalize pathname by removing symlinks
3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
4 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
6 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
13 #include <sys/types.h>
17 #include <limits.h> /* for PATH_MAX */
18 #include <sys/param.h> /* for MAXPATHLEN */
22 #include <sys/stat.h> /* for S_IFLNK */
24 /* Experimentally off - libc_hidden_proto(strcat) */
25 /* Experimentally off - libc_hidden_proto(strcpy) */
26 /* Experimentally off - libc_hidden_proto(strlen) */
27 libc_hidden_proto(readlink)
28 libc_hidden_proto(getcwd)
32 #define PATH_MAX _POSIX_PATH_MAX
35 #define PATH_MAX MAXPATHLEN
42 #define MAX_READLINKS 32
45 char *realpath(const char *path, char got_path[])
47 char *realpath(path, got_path)
52 char copy_path[PATH_MAX];
53 /* use user supplied buffer directly - reduces stack usage */
54 /* char got_path[PATH_MAX]; */
71 /* Make a copy of the source path since we may need to modify it. */
72 path_len = strlen(path);
73 if (path_len >= PATH_MAX - 2) {
74 __set_errno(ENAMETOOLONG);
77 /* Copy so that path is at the end of copy_path[] */
78 strcpy(copy_path + (PATH_MAX-1) - path_len, path);
79 path = copy_path + (PATH_MAX-1) - path_len;
80 max_path = got_path + PATH_MAX - 2; /* points to last non-NUL char */
83 /* If it's a relative pathname use getcwd for starters. */
84 if (!getcwd(new_path, PATH_MAX - 1))
86 new_path += strlen(new_path);
87 if (new_path[-1] != '/')
93 /* Expand each slash-separated pathname component. */
94 while (*path != '\0') {
95 /* Ignore stray "/". */
102 if (path[1] == '\0' || path[1] == '/') {
106 if (path[1] == '.') {
107 if (path[2] == '\0' || path[2] == '/') {
109 /* Ignore ".." at root. */
110 if (new_path == got_path + 1)
112 /* Handle ".." by backing up. */
113 while ((--new_path)[-1] != '/');
118 /* Safely copy the next pathname component. */
119 while (*path != '\0' && *path != '/') {
120 if (new_path > max_path) {
121 __set_errno(ENAMETOOLONG);
124 *new_path++ = *path++;
127 /* Protect against infinite loops. */
128 if (readlinks++ > MAX_READLINKS) {
132 path_len = strlen(path);
133 /* See if last (so far) pathname component is a symlink. */
136 int sv_errno = errno;
137 link_len = readlink(got_path, copy_path, PATH_MAX - 1);
139 /* EINVAL means the file exists but isn't a symlink. */
140 if (errno != EINVAL) {
144 /* Safe sex check. */
145 if (path_len + link_len >= PATH_MAX - 2) {
146 __set_errno(ENAMETOOLONG);
149 /* Note: readlink doesn't add the null byte. */
150 /* copy_path[link_len] = '\0'; - we don't need it too */
151 if (*copy_path == '/')
152 /* Start over for an absolute symlink. */
155 /* Otherwise back up over this component. */
156 while (*(--new_path) != '/');
157 /* Prepend symlink contents to path. */
158 memmove(copy_path + (PATH_MAX-1) - link_len - path_len, copy_path, link_len);
159 path = copy_path + (PATH_MAX-1) - link_len - path_len;
161 __set_errno(sv_errno);
166 /* Delete trailing slash but don't whomp a lone slash. */
167 if (new_path != got_path + 1 && new_path[-1] == '/')
169 /* Make sure it's null terminated. */