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 */
27 #define PATH_MAX _POSIX_PATH_MAX
30 #define PATH_MAX MAXPATHLEN
37 #define MAX_READLINKS 32
39 char *realpath(const char *path
, char got_path
[])
41 char copy_path
[PATH_MAX
];
42 char *max_path
, *new_path
, *allocated_path
;
57 /* Make a copy of the source path since we may need to modify it. */
58 path_len
= strlen(path
);
59 if (path_len
>= PATH_MAX
- 2) {
60 __set_errno(ENAMETOOLONG
);
63 /* Copy so that path is at the end of copy_path[] */
64 strcpy(copy_path
+ (PATH_MAX
-1) - path_len
, path
);
65 path
= copy_path
+ (PATH_MAX
-1) - path_len
;
66 allocated_path
= got_path
? NULL
: (got_path
= malloc(PATH_MAX
));
67 max_path
= got_path
+ PATH_MAX
- 2; /* points to last non-NUL char */
70 /* If it's a relative pathname use getcwd for starters. */
71 if (!getcwd(new_path
, PATH_MAX
- 1))
73 new_path
+= strlen(new_path
);
74 if (new_path
[-1] != '/')
80 /* Expand each slash-separated pathname component. */
81 while (*path
!= '\0') {
82 /* Ignore stray "/". */
89 if (path
[1] == '\0' || path
[1] == '/') {
94 if (path
[2] == '\0' || path
[2] == '/') {
96 /* Ignore ".." at root. */
97 if (new_path
== got_path
+ 1)
99 /* Handle ".." by backing up. */
100 while ((--new_path
)[-1] != '/');
105 /* Safely copy the next pathname component. */
106 while (*path
!= '\0' && *path
!= '/') {
107 if (new_path
> max_path
) {
108 __set_errno(ENAMETOOLONG
);
110 free(allocated_path
);
113 *new_path
++ = *path
++;
116 /* Protect against infinite loops. */
117 if (readlinks
++ > MAX_READLINKS
) {
121 path_len
= strlen(path
);
122 /* See if last (so far) pathname component is a symlink. */
125 int sv_errno
= errno
;
126 link_len
= readlink(got_path
, copy_path
, PATH_MAX
- 1);
128 /* EINVAL means the file exists but isn't a symlink. */
129 if (errno
!= EINVAL
) {
133 /* Safe sex check. */
134 if (path_len
+ link_len
>= PATH_MAX
- 2) {
135 __set_errno(ENAMETOOLONG
);
138 /* Note: readlink doesn't add the null byte. */
139 /* copy_path[link_len] = '\0'; - we don't need it too */
140 if (*copy_path
== '/')
141 /* Start over for an absolute symlink. */
144 /* Otherwise back up over this component. */
145 while (*(--new_path
) != '/');
146 /* Prepend symlink contents to path. */
147 memmove(copy_path
+ (PATH_MAX
-1) - link_len
- path_len
, copy_path
, link_len
);
148 path
= copy_path
+ (PATH_MAX
-1) - link_len
- path_len
;
150 __set_errno(sv_errno
);
155 /* Delete trailing slash but don't whomp a lone slash. */
156 if (new_path
!= got_path
+ 1 && new_path
[-1] == '/')
158 /* Make sure it's null terminated. */
162 libc_hidden_def(realpath
)