Mercurial > hg > xemacs-beta
comparison src/realpath.c @ 1116:3bcd77d0bf93
[xemacs-hg @ 2002-11-22 12:57:09 by ben]
file-truename and other crash
fileio.c: Fix crashes due to incorrectly ported code from
FSF.
realpath.c: Don't return EIO when a filename is incorrect,
since the I/O error gets passed all the way up. Clean up this
file and avoid using macros as a poor-man's dispatch mechanism --
it just makes it impossible to follow the code.
author | ben |
---|---|
date | Fri, 22 Nov 2002 12:57:11 +0000 |
parents | 5df795348f45 |
children | e22b0213b713 |
comparison
equal
deleted
inserted
replaced
1115:cb1d0fc87e10 | 1116:3bcd77d0bf93 |
---|---|
1 /* | 1 /* |
2 * realpath.c -- canonicalize pathname by removing symlinks | 2 * realpath.c -- canonicalize pathname by removing symlinks |
3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> | 3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> |
4 * Copyright (C) 2001 Ben Wing. | 4 * Copyright (C) 2001, 2002 Ben Wing. |
5 * | 5 * |
6 | 6 |
7 This file is part of XEmacs. | 7 This file is part of XEmacs. |
8 | 8 |
9 XEmacs is free software; you can redistribute it and/or modify it | 9 XEmacs is free software; you can redistribute it and/or modify it |
30 | 30 |
31 #include <config.h> | 31 #include <config.h> |
32 #include "lisp.h" | 32 #include "lisp.h" |
33 | 33 |
34 #include "sysfile.h" | 34 #include "sysfile.h" |
35 #include "sysdir.h" | |
35 | 36 |
36 #define MAX_READLINKS 32 | 37 #define MAX_READLINKS 32 |
37 | 38 |
38 #if defined (HAVE_SYS_PARAM_H) && !defined (WIN32_NATIVE) | 39 #ifdef WIN32_ANY |
39 #include <sys/param.h> | |
40 #endif | |
41 | |
42 #ifdef WIN32_NATIVE | |
43 #include <direct.h> | |
44 #endif | |
45 | |
46 #include <sys/stat.h> /* for S_IFLNK */ | |
47 | |
48 #if defined(WIN32_NATIVE) || defined(CYGWIN) | |
49 #define WIN32_FILENAMES | |
50 #endif | |
51 | |
52 /* First char after start of absolute filename. */ | |
53 #define ABS_START(name) (name + ABS_LENGTH (name)) | |
54 | |
55 #if defined (WIN32_NATIVE) | |
56 /* Length of start of absolute filename. */ | |
57 # define ABS_LENGTH(name) (mswindows_abs_start (name)) | |
58 static int mswindows_abs_start (const Ibyte *name); | |
59 # define readlink_and_correct_case mswindows_readlink_and_correct_case | |
60 #else | |
61 # ifdef CYGWIN | |
62 # ifdef WIN32_FILENAMES | |
63 # define ABS_LENGTH(name) (mswindows_abs_start (name)) | |
64 static int mswindows_abs_start (const Ibyte * name); | |
65 # else | |
66 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? \ | |
67 (IS_DIRECTORY_SEP (name[1]) ? 2 : 1) : 0) | |
68 # endif | |
69 # define readlink_and_correct_case cygwin_readlink_and_correct_case | |
70 # else | |
71 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? 1 : 0) | |
72 # define readlink_and_correct_case qxe_readlink | |
73 # endif /* CYGWIN */ | |
74 #endif /* WIN32_NATIVE */ | |
75 | |
76 #if defined (WIN32_NATIVE) || defined (CYGWIN) | |
77 #include "syswindows.h" | 40 #include "syswindows.h" |
78 /* "Emulate" readlink on mswindows - finds real name (i.e. correct | |
79 case) of a file. (#### "readlink" is used extremely misleadingly | |
80 here. This is much more like "truename"!) UNC servers and shares | |
81 are lower-cased. Directories must be given without trailing | |
82 '/'. One day, this could read Win2K's reparse points. */ | |
83 static int | |
84 mswindows_readlink_and_correct_case (const Ibyte *name, Ibyte *buf, | |
85 int size) | |
86 { | |
87 int len = 0; | |
88 int err = 0; | |
89 const Ibyte *lastname; | |
90 int count = 0; | |
91 const Ibyte *tmp; | |
92 DECLARE_EISTRING (result); | |
93 | |
94 assert (*name); | |
95 | |
96 /* Sort of check we have a valid filename. */ | |
97 if (qxestrpbrk (name, "*?|<>\"") || qxestrlen (name) >= PATH_MAX) | |
98 { | |
99 errno = EIO; | |
100 return -1; | |
101 } | |
102 | |
103 /* Find start of filename */ | |
104 lastname = name + qxestrlen (name); | |
105 while (lastname > name && !IS_DIRECTORY_SEP (lastname[-1])) | |
106 --lastname; | |
107 | |
108 /* Count slashes in unc path */ | |
109 if (ABS_LENGTH (name) == 2) | |
110 for (tmp = name; *tmp; tmp++) | |
111 if (IS_DIRECTORY_SEP (*tmp)) | |
112 count++; | |
113 | |
114 if (count >= 2 && count < 4) | |
115 { | |
116 eicpy_rawz (result, lastname); | |
117 eilwr (result); | |
118 } | |
119 else | |
120 { | |
121 WIN32_FIND_DATAW find_data; | |
122 Extbyte *nameext; | |
123 HANDLE dir_handle; | |
124 | |
125 C_STRING_TO_TSTR (name, nameext); | |
126 dir_handle = qxeFindFirstFile (nameext, &find_data); | |
127 if (dir_handle == INVALID_HANDLE_VALUE) | |
128 { | |
129 errno = ENOENT; | |
130 return -1; | |
131 } | |
132 eicpy_ext (result, (Extbyte *) find_data.cFileName, Qmswindows_tstr); | |
133 FindClose (dir_handle); | |
134 } | |
135 | |
136 if ((len = eilen (result)) < size) | |
137 { | |
138 DECLARE_EISTRING (eilastname); | |
139 | |
140 eicpy_rawz (eilastname, lastname); | |
141 if (eicmp_ei (eilastname, result) == 0) | |
142 /* Signal that the name is already OK. */ | |
143 err = EINVAL; | |
144 else | |
145 memcpy (buf, eidata (result), len + 1); | |
146 } | |
147 else | |
148 err = ENAMETOOLONG; | |
149 | |
150 errno = err; | |
151 return err ? -1 : len; | |
152 } | |
153 #endif /* WIN32_NATIVE || CYGWIN */ | |
154 | |
155 #ifdef CYGWIN | |
156 /* Call readlink and try to find out the correct case for the file. */ | |
157 static int | |
158 cygwin_readlink_and_correct_case (const Ibyte *name, Ibyte *buf, | |
159 int size) | |
160 { | |
161 int n = qxe_readlink (name, buf, size); | |
162 if (n < 0 && errno == EINVAL) | |
163 { | |
164 /* The file may exist, but isn't a symlink. Try to find the | |
165 right name. */ | |
166 Ibyte *tmp = | |
167 (Ibyte *) ALLOCA (cygwin_posix_to_win32_path_list_buf_size | |
168 ((char *) name)); | |
169 cygwin_posix_to_win32_path_list ((char *) name, (char *) tmp); | |
170 n = mswindows_readlink_and_correct_case (tmp, buf, size); | |
171 } | |
172 return n; | |
173 } | |
174 #endif /* CYGWIN */ | |
175 | |
176 #ifdef WIN32_FILENAMES | |
177 #ifndef ELOOP | 41 #ifndef ELOOP |
178 #define ELOOP 10062 /* = WSAELOOP in winsock.h */ | 42 #define ELOOP 10062 /* = WSAELOOP in winsock.h */ |
179 #endif | 43 #endif |
44 #endif | |
45 | |
180 /* Length of start of absolute filename. */ | 46 /* Length of start of absolute filename. */ |
181 static int | 47 static int |
182 mswindows_abs_start (const Ibyte *name) | 48 abs_start (const Ibyte *name) |
183 { | 49 { |
50 #ifdef WIN32_ANY | |
184 if (isalpha (*name) && IS_DEVICE_SEP (name[1]) | 51 if (isalpha (*name) && IS_DEVICE_SEP (name[1]) |
185 && IS_DIRECTORY_SEP (name[2])) | 52 && IS_DIRECTORY_SEP (name[2])) |
186 return 3; | 53 return 3; |
187 else if (IS_DIRECTORY_SEP (*name)) | 54 else if (IS_DIRECTORY_SEP (*name)) |
188 return IS_DIRECTORY_SEP (name[1]) ? 2 : 1; | 55 return IS_DIRECTORY_SEP (name[1]) ? 2 : 1; |
189 else | 56 else |
190 return 0; | 57 return 0; |
58 #else /* not WIN32_ANY */ | |
59 return IS_DIRECTORY_SEP (*name) ? 1 : 0; | |
60 #endif | |
191 } | 61 } |
192 #endif /* WIN32_NATIVE */ | 62 |
63 /* Find real name of a file by resolving symbolic links and (under Windows) | |
64 looking up the correct case of the file as it appears on the file | |
65 system. | |
66 | |
67 Under Windows, UNC servers and shares are lower-cased. Directories must | |
68 be given without trailing '/'. One day, this could read Win2K's reparse | |
69 points. */ | |
70 | |
71 static int | |
72 readlink_and_correct_case (const Ibyte *name, Ibyte *buf, | |
73 int size) | |
74 { | |
75 #ifndef WIN32_ANY | |
76 return qxe_readlink (name, buf, size); | |
77 #else | |
78 # ifdef CYGWIN | |
79 Ibyte *tmp; | |
80 int n = qxe_readlink (name, buf, size); | |
81 if (n >= 0 || errno != EINVAL) | |
82 return n; | |
83 | |
84 /* The file may exist, but isn't a symlink. Try to find the | |
85 right name. */ | |
86 tmp = (Ibyte *) ALLOCA (cygwin_posix_to_win32_path_list_buf_size | |
87 ((char *) name)); | |
88 cygwin_posix_to_win32_path_list ((char *) name, (char *) tmp); | |
89 name = tmp; | |
90 # endif | |
91 | |
92 { | |
93 int len = 0; | |
94 int err = 0; | |
95 const Ibyte *lastname; | |
96 int count = 0; | |
97 const Ibyte *tmp; | |
98 DECLARE_EISTRING (result); | |
99 | |
100 assert (*name); | |
101 | |
102 /* Sort of check we have a valid filename. */ | |
103 if (qxestrpbrk (name, "*?|<>\"")) | |
104 { | |
105 errno = ENOENT; | |
106 return -1; | |
107 } | |
108 else if (qxestrlen (name) >= PATH_MAX) | |
109 { | |
110 errno = ENAMETOOLONG; | |
111 return -1; | |
112 } | |
113 | |
114 /* Find start of filename */ | |
115 lastname = name + qxestrlen (name); | |
116 while (lastname > name && !IS_DIRECTORY_SEP (lastname[-1])) | |
117 --lastname; | |
118 | |
119 /* Count slashes in unc path */ | |
120 if (abs_start (name) == 2) | |
121 for (tmp = name; *tmp; tmp++) | |
122 if (IS_DIRECTORY_SEP (*tmp)) | |
123 count++; | |
124 | |
125 if (count >= 2 && count < 4) | |
126 { | |
127 eicpy_rawz (result, lastname); | |
128 eilwr (result); | |
129 } | |
130 else | |
131 { | |
132 WIN32_FIND_DATAW find_data; | |
133 Extbyte *nameext; | |
134 HANDLE dir_handle; | |
135 | |
136 C_STRING_TO_TSTR (name, nameext); | |
137 dir_handle = qxeFindFirstFile (nameext, &find_data); | |
138 if (dir_handle == INVALID_HANDLE_VALUE) | |
139 { | |
140 errno = ENOENT; | |
141 return -1; | |
142 } | |
143 eicpy_ext (result, (Extbyte *) find_data.cFileName, Qmswindows_tstr); | |
144 FindClose (dir_handle); | |
145 } | |
146 | |
147 if ((len = eilen (result)) < size) | |
148 { | |
149 DECLARE_EISTRING (eilastname); | |
150 | |
151 eicpy_rawz (eilastname, lastname); | |
152 if (eicmp_ei (eilastname, result) == 0) | |
153 /* Signal that the name is already OK. */ | |
154 err = EINVAL; | |
155 else | |
156 memcpy (buf, eidata (result), len + 1); | |
157 } | |
158 else | |
159 err = ENAMETOOLONG; | |
160 | |
161 errno = err; | |
162 return err ? -1 : len; | |
163 } | |
164 #endif /* WIN32_ANY */ | |
165 } | |
193 | 166 |
194 /* Mule Note: This function works with and returns | 167 /* Mule Note: This function works with and returns |
195 internally-formatted strings. */ | 168 internally-formatted strings. */ |
196 | 169 |
197 Ibyte * | 170 Ibyte * |
198 qxe_realpath (const Ibyte *path, Ibyte *resolved_path) | 171 qxe_realpath (const Ibyte *path, Ibyte *resolved_path) |
199 { | 172 { |
200 Ibyte copy_path[PATH_MAX]; | 173 Ibyte copy_path[PATH_MAX]; |
201 Ibyte *new_path = resolved_path; | 174 Ibyte *new_path = resolved_path; |
202 Ibyte *max_path; | 175 Ibyte *max_path; |
203 #if defined (HAVE_READLINK) || defined (WIN32_NATIVE) | 176 #if defined (HAVE_READLINK) || defined (WIN32_ANY) |
204 int readlinks = 0; | 177 int readlinks = 0; |
205 Ibyte link_path[PATH_MAX]; | 178 Ibyte link_path[PATH_MAX]; |
206 int n; | 179 int n; |
207 int abslen = ABS_LENGTH (path); | 180 int abslen = abs_start (path); |
208 #endif | 181 #endif |
209 | 182 |
210 /* Make a copy of the source path since we may need to modify it. */ | 183 /* Make a copy of the source path since we may need to modify it. */ |
211 qxestrcpy (copy_path, path); | 184 qxestrcpy (copy_path, path); |
212 path = copy_path; | 185 path = copy_path; |
213 max_path = copy_path + PATH_MAX - 2; | 186 max_path = copy_path + PATH_MAX - 2; |
214 | 187 |
215 if (0) | 188 if (0) |
216 ; | 189 ; |
217 #ifdef WIN32_FILENAMES | 190 #ifdef WIN32_ANY |
218 /* Check for c:/... or //server/... */ | 191 /* Check for c:/... or //server/... */ |
219 else if (abslen == 3 || abslen == 2) | 192 else if (abslen == 3 || abslen == 2) |
220 { | 193 { |
221 /* Make sure drive letter is lowercased. */ | 194 /* Make sure drive letter is lowercased. */ |
222 if (abslen == 3) { | 195 if (abslen == 3) |
223 *new_path = tolower (*path); | 196 { |
224 new_path++; | 197 *new_path = tolower (*path); |
225 path++; | 198 new_path++; |
226 abslen--; | 199 path++; |
227 } | 200 abslen--; |
201 } | |
228 /* Coerce directory chars. */ | 202 /* Coerce directory chars. */ |
229 while (abslen-- > 0) { | 203 while (abslen-- > 0) |
230 if (IS_DIRECTORY_SEP (*path)) | 204 { |
231 *new_path++ = DIRECTORY_SEP; | 205 if (IS_DIRECTORY_SEP (*path)) |
232 else | 206 *new_path++ = DIRECTORY_SEP; |
233 *new_path++ = *path; | 207 else |
234 path++; | 208 *new_path++ = *path; |
235 } | 209 path++; |
210 } | |
236 } | 211 } |
237 #endif | 212 #endif |
238 #ifdef WIN32_NATIVE | 213 #ifdef WIN32_NATIVE |
239 /* No drive letter, but a beginning slash? Prepend drive letter. */ | 214 /* No drive letter, but a beginning slash? Prepend drive letter. */ |
240 else if (abslen == 1) | 215 else if (abslen == 1) |
242 get_initial_directory (new_path, PATH_MAX - 1); | 217 get_initial_directory (new_path, PATH_MAX - 1); |
243 new_path += 3; | 218 new_path += 3; |
244 path++; | 219 path++; |
245 } | 220 } |
246 /* Just a path name, prepend the current directory */ | 221 /* Just a path name, prepend the current directory */ |
247 else if (1) | 222 else |
248 { | 223 { |
249 get_initial_directory (new_path, PATH_MAX - 1); | 224 get_initial_directory (new_path, PATH_MAX - 1); |
250 new_path += qxestrlen (new_path); | 225 new_path += qxestrlen (new_path); |
251 if (!IS_DIRECTORY_SEP (new_path[-1])) | 226 if (!IS_DIRECTORY_SEP (new_path[-1])) |
252 *new_path++ = DIRECTORY_SEP; | 227 *new_path++ = DIRECTORY_SEP; |
292 (path[2] == '\0' || IS_DIRECTORY_SEP (path[2]))) | 267 (path[2] == '\0' || IS_DIRECTORY_SEP (path[2]))) |
293 { | 268 { |
294 path += 2; | 269 path += 2; |
295 | 270 |
296 /* Ignore ".." at root. */ | 271 /* Ignore ".." at root. */ |
297 if (new_path == ABS_START (resolved_path)) | 272 if (new_path == resolved_path + abs_start (resolved_path)) |
298 continue; | 273 continue; |
299 | 274 |
300 /* Handle ".." by backing up. */ | 275 /* Handle ".." by backing up. */ |
301 --new_path; | 276 --new_path; |
302 while (!IS_DIRECTORY_SEP (new_path[-1])) | 277 while (!IS_DIRECTORY_SEP (new_path[-1])) |
314 return NULL; | 289 return NULL; |
315 } | 290 } |
316 *new_path++ = *path++; | 291 *new_path++ = *path++; |
317 } | 292 } |
318 | 293 |
319 #if defined (HAVE_READLINK) || defined (WIN32_NATIVE) | 294 #if defined (HAVE_READLINK) || defined (WIN32_ANY) |
320 /* See if latest pathname component is a symlink or needs case | 295 /* See if latest pathname component is a symlink or needs case |
321 correction. */ | 296 correction. */ |
322 *new_path = '\0'; | 297 *new_path = '\0'; |
323 n = readlink_and_correct_case (resolved_path, link_path, PATH_MAX - 1); | 298 n = readlink_and_correct_case (resolved_path, link_path, PATH_MAX - 1); |
324 | 299 |
325 if (n < 0) | 300 if (n < 0) |
326 { | 301 { |
327 /* EINVAL means the file exists but isn't a symlink or doesn't | 302 /* EINVAL means the file exists but isn't a symlink or doesn't |
328 need case correction. */ | 303 need case correction. */ |
329 #ifdef CYGWIN | 304 #ifdef WIN32_ANY |
330 if (errno != EINVAL && errno != ENOENT) | 305 if (errno != EINVAL && errno != ENOENT) |
331 #else | 306 #else |
332 if (errno != EINVAL) | 307 if (errno != EINVAL) |
333 #endif | 308 #endif |
334 return NULL; | 309 return NULL; |
343 } | 318 } |
344 | 319 |
345 /* Note: readlink doesn't add the null byte. */ | 320 /* Note: readlink doesn't add the null byte. */ |
346 link_path[n] = '\0'; | 321 link_path[n] = '\0'; |
347 | 322 |
348 if (ABS_LENGTH (link_path) > 0) | 323 if (abs_start (link_path) > 0) |
349 /* Start over for an absolute symlink. */ | 324 /* Start over for an absolute symlink. */ |
350 new_path = resolved_path + ABS_LENGTH (link_path) - 1; | 325 new_path = resolved_path + abs_start (link_path) - 1; |
351 else | 326 else |
352 /* Otherwise back up over this component. */ | 327 /* Otherwise back up over this component. */ |
353 for (--new_path; !IS_DIRECTORY_SEP (*new_path); --new_path) | 328 for (--new_path; !IS_DIRECTORY_SEP (*new_path); --new_path) |
354 assert (new_path > resolved_path); | 329 assert (new_path > resolved_path); |
355 | 330 |
363 /* Insert symlink contents into path. */ | 338 /* Insert symlink contents into path. */ |
364 qxestrcat (link_path, path); | 339 qxestrcat (link_path, path); |
365 qxestrcpy (copy_path, link_path); | 340 qxestrcpy (copy_path, link_path); |
366 path = copy_path; | 341 path = copy_path; |
367 } | 342 } |
368 #endif /* HAVE_READLINK || WIN32_NATIVE */ | 343 #endif /* HAVE_READLINK || WIN32_ANY */ |
369 *new_path++ = DIRECTORY_SEP; | 344 *new_path++ = DIRECTORY_SEP; |
370 } | 345 } |
371 | 346 |
372 /* Delete trailing slash but don't whomp a lone slash. */ | 347 /* Delete trailing slash but don't whomp a lone slash. */ |
373 if (new_path != ABS_START (resolved_path) && | 348 if (new_path != resolved_path + abs_start (resolved_path) && |
374 IS_DIRECTORY_SEP (new_path[-1])) | 349 IS_DIRECTORY_SEP (new_path[-1])) |
375 new_path--; | 350 new_path--; |
376 | 351 |
377 /* Make sure it's null terminated. */ | 352 /* Make sure it's null terminated. */ |
378 *new_path = '\0'; | 353 *new_path = '\0'; |