]>
Commit | Line | Data |
---|---|---|
6dbe3af9 KZ |
1 | /* |
2 | * realpath.c -- canonicalize pathname by removing symlinks | |
3 | * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU Library Public License as published by | |
7 | * the Free Software Foundation; either version 2, or (at your option) | |
8 | * any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU Library Public License for more details. | |
14 | */ | |
15 | ||
df1dddf9 | 16 | #define resolve_symlinks |
364cda48 | 17 | |
6dbe3af9 | 18 | /* |
fd6b7a7f KZ |
19 | * This routine is part of libc. We include it nevertheless, |
20 | * since the libc version has some security flaws. | |
6dbe3af9 | 21 | */ |
6dbe3af9 | 22 | #include <unistd.h> |
6dbe3af9 | 23 | #include <string.h> |
6dbe3af9 | 24 | #include <errno.h> |
eb63b9b8 KZ |
25 | #include "realpath.h" |
26 | #include "sundries.h" /* for xstrdup */ | |
6dbe3af9 | 27 | |
4717ea4a KZ |
28 | #ifndef MAXSYMLINKS |
29 | # define MAXSYMLINKS 256 | |
30 | #endif | |
6dbe3af9 | 31 | |
f1b3f066 KZ |
32 | int |
33 | is_pseudo_fs(const char *type) | |
34 | { | |
35 | if (type == NULL || *type == '/') | |
36 | return 0; | |
37 | if (streq(type, "none") || | |
38 | streq(type, "proc") || | |
39 | streq(type, "tmpfs") || | |
40 | streq(type, "sysfs") || | |
41 | streq(type, "devpts")) | |
42 | return 1; | |
43 | return 0; | |
44 | } | |
89fde75b KZ |
45 | |
46 | /* Make a canonical pathname from PATH. Returns a freshly malloced string. | |
47 | It is up the *caller* to ensure that the PATH is sensible. i.e. | |
48 | canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.'' | |
49 | is not a legal pathname for ``/dev/fd0''. Anything we cannot parse | |
50 | we return unmodified. */ | |
51 | char * | |
f1b3f066 KZ |
52 | canonicalize_spec (const char *path) |
53 | { | |
89fde75b KZ |
54 | if (path == NULL) |
55 | return NULL; | |
f1b3f066 | 56 | if (is_pseudo_fs(path)) |
89fde75b | 57 | return xstrdup(path); |
89fde75b KZ |
58 | return canonicalize(path); |
59 | } | |
60 | ||
61 | char * | |
62 | canonicalize (const char *path) { | |
63 | char canonical[PATH_MAX+2]; | |
64 | ||
65 | if (path == NULL) | |
66 | return NULL; | |
67 | ||
68 | if (myrealpath (path, canonical, PATH_MAX+1)) | |
69 | return xstrdup(canonical); | |
70 | ||
71 | return xstrdup(path); | |
72 | } | |
73 | ||
eb63b9b8 KZ |
74 | char * |
75 | myrealpath(const char *path, char *resolved_path, int maxreslth) { | |
6dbe3af9 | 76 | int readlinks = 0; |
364cda48 KZ |
77 | char *npath; |
78 | char link_path[PATH_MAX+1]; | |
79 | int n; | |
d26aa358 | 80 | char *buf = NULL; |
6dbe3af9 | 81 | |
eb63b9b8 | 82 | npath = resolved_path; |
fd6b7a7f | 83 | |
eb63b9b8 | 84 | /* If it's a relative pathname use getcwd for starters. */ |
6dbe3af9 | 85 | if (*path != '/') { |
22853e4a KZ |
86 | if (!getcwd(npath, maxreslth-2)) |
87 | return NULL; | |
eb63b9b8 KZ |
88 | npath += strlen(npath); |
89 | if (npath[-1] != '/') | |
90 | *npath++ = '/'; | |
91 | } else { | |
92 | *npath++ = '/'; | |
6dbe3af9 KZ |
93 | path++; |
94 | } | |
eb63b9b8 | 95 | |
6dbe3af9 KZ |
96 | /* Expand each slash-separated pathname component. */ |
97 | while (*path != '\0') { | |
eb63b9b8 | 98 | /* Ignore stray "/" */ |
6dbe3af9 KZ |
99 | if (*path == '/') { |
100 | path++; | |
101 | continue; | |
102 | } | |
eb63b9b8 KZ |
103 | if (*path == '.' && (path[1] == '\0' || path[1] == '/')) { |
104 | /* Ignore "." */ | |
105 | path++; | |
106 | continue; | |
107 | } | |
108 | if (*path == '.' && path[1] == '.' && | |
109 | (path[2] == '\0' || path[2] == '/')) { | |
110 | /* Backup for ".." */ | |
111 | path += 2; | |
112 | while (npath > resolved_path+1 && | |
113 | (--npath)[-1] != '/') | |
114 | ; | |
115 | continue; | |
6dbe3af9 KZ |
116 | } |
117 | /* Safely copy the next pathname component. */ | |
118 | while (*path != '\0' && *path != '/') { | |
eb63b9b8 | 119 | if (npath-resolved_path > maxreslth-2) { |
6dbe3af9 | 120 | errno = ENAMETOOLONG; |
d26aa358 | 121 | goto err; |
6dbe3af9 | 122 | } |
eb63b9b8 | 123 | *npath++ = *path++; |
6dbe3af9 | 124 | } |
eb63b9b8 | 125 | |
6dbe3af9 | 126 | /* Protect against infinite loops. */ |
4717ea4a | 127 | if (readlinks++ > MAXSYMLINKS) { |
6dbe3af9 | 128 | errno = ELOOP; |
d26aa358 | 129 | goto err; |
6dbe3af9 | 130 | } |
eb63b9b8 | 131 | |
364cda48 | 132 | /* See if last pathname component is a symlink. */ |
eb63b9b8 KZ |
133 | *npath = '\0'; |
134 | n = readlink(resolved_path, link_path, PATH_MAX); | |
6dbe3af9 KZ |
135 | if (n < 0) { |
136 | /* EINVAL means the file exists but isn't a symlink. */ | |
137 | if (errno != EINVAL) | |
d26aa358 | 138 | goto err; |
eb63b9b8 | 139 | } else { |
364cda48 | 140 | #ifdef resolve_symlinks /* Richard Gooch dislikes sl resolution */ |
d26aa358 | 141 | int m; |
a9d6150d | 142 | char *newbuf; |
d26aa358 | 143 | |
6dbe3af9 KZ |
144 | /* Note: readlink doesn't add the null byte. */ |
145 | link_path[n] = '\0'; | |
146 | if (*link_path == '/') | |
147 | /* Start over for an absolute symlink. */ | |
eb63b9b8 | 148 | npath = resolved_path; |
6dbe3af9 KZ |
149 | else |
150 | /* Otherwise back up over this component. */ | |
eb63b9b8 | 151 | while (*(--npath) != '/') |
6dbe3af9 | 152 | ; |
22853e4a | 153 | |
6dbe3af9 | 154 | /* Insert symlink contents into path. */ |
22853e4a | 155 | m = strlen(path); |
a9d6150d NB |
156 | newbuf = xmalloc(m + n + 1); |
157 | memcpy(newbuf, link_path, n); | |
158 | memcpy(newbuf + n, path, m + 1); | |
71c445db | 159 | free(buf); |
a9d6150d | 160 | path = buf = newbuf; |
364cda48 | 161 | #endif |
6dbe3af9 | 162 | } |
eb63b9b8 | 163 | *npath++ = '/'; |
6dbe3af9 KZ |
164 | } |
165 | /* Delete trailing slash but don't whomp a lone slash. */ | |
eb63b9b8 KZ |
166 | if (npath != resolved_path+1 && npath[-1] == '/') |
167 | npath--; | |
6dbe3af9 | 168 | /* Make sure it's null terminated. */ |
eb63b9b8 | 169 | *npath = '\0'; |
d26aa358 | 170 | |
71c445db | 171 | free(buf); |
6dbe3af9 | 172 | return resolved_path; |
d26aa358 KZ |
173 | |
174 | err: | |
71c445db | 175 | free(buf); |
d26aa358 | 176 | return NULL; |
6dbe3af9 | 177 | } |