428
+ − 1 /*
+ − 2 * realpath.c -- canonicalize pathname by removing symlinks
+ − 3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ − 4 *
+ − 5
+ − 6 This file is part of XEmacs.
+ − 7
+ − 8 XEmacs is free software; you can redistribute it and/or modify it
+ − 9 under the terms of the GNU General Public License as published by the
+ − 10 Free Software Foundation; either version 2, or (at your option) any
+ − 11 later version.
+ − 12
+ − 13 XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ − 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ − 15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ − 16 for more details.
+ − 17
+ − 18 You should have received a copy of the GNU General Public License
+ − 19 along with XEmacs; see the file COPYING. If not, write to
+ − 20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ − 21 Boston, MA 02111-1307, USA. */
+ − 22
+ − 23 /* Synched up with: Not in FSF. */
+ − 24
+ − 25 #include <config.h>
446
+ − 26 #include "lisp.h"
442
+ − 27
558
+ − 28 #include "sysfile.h"
428
+ − 29
446
+ − 30 /* First char after start of absolute filename. */
+ − 31 #define ABS_START(name) (name + ABS_LENGTH (name))
+ − 32
+ − 33 #if defined (WIN32_NATIVE)
+ − 34 /* Length of start of absolute filename. */
+ − 35 # define ABS_LENGTH(name) (win32_abs_start (name))
+ − 36 static int win32_abs_start (const char * name);
+ − 37 /* System dependent version of readlink. */
+ − 38 # define system_readlink win32_readlink
+ − 39 #else
+ − 40 # ifdef CYGWIN
+ − 41 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? \
+ − 42 (IS_DIRECTORY_SEP (name[1]) ? 2 : 1) : 0)
+ − 43 # define system_readlink cygwin_readlink
+ − 44 # else
+ − 45 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? 1 : 0)
+ − 46 # define system_readlink readlink
+ − 47 # endif /* CYGWIN */
+ − 48 #endif /* WIN32_NATIVE */
+ − 49
+ − 50 #if defined (WIN32_NATIVE) || defined (CYGWIN)
+ − 51 #include "syswindows.h"
+ − 52 /* Emulate readlink on win32 - finds real name (i.e. correct case) of
+ − 53 a file. UNC servers and shares are lower-cased. Directories must be
+ − 54 given without trailing '/'. One day, this could read Win2K's
+ − 55 reparse points. */
+ − 56 static int
+ − 57 win32_readlink (const char * name, char * buf, int size)
+ − 58 {
+ − 59 WIN32_FIND_DATA find_data;
+ − 60 HANDLE dir_handle = NULL;
+ − 61 int len = 0;
+ − 62 int err = 0;
+ − 63 const char* lastname;
+ − 64 int count = 0;
+ − 65 const char* tmp;
+ − 66 char* res = NULL;
+ − 67
+ − 68 assert (*name);
+ − 69
+ − 70 /* Sort of check we have a valid filename. */
558
+ − 71 if (strpbrk (name, "*?|<>\"") || strlen (name) >= PATH_MAX)
446
+ − 72 {
+ − 73 errno = EIO;
+ − 74 return -1;
+ − 75 }
+ − 76
+ − 77 /* Find start of filename */
+ − 78 lastname = name + strlen (name);
+ − 79 while (lastname > name && !IS_DIRECTORY_SEP (lastname[-1]))
+ − 80 --lastname;
+ − 81
+ − 82 /* Count slashes in unc path */
+ − 83 if (ABS_LENGTH (name) == 2)
+ − 84 for (tmp = name; *tmp; tmp++)
+ − 85 if (IS_DIRECTORY_SEP (*tmp))
+ − 86 count++;
+ − 87
+ − 88 if (count >= 2 && count < 4)
+ − 89 {
+ − 90 /* UNC server or share name: just copy lowercased name. */
+ − 91 res = find_data.cFileName;
+ − 92 for (tmp = lastname; *tmp; tmp++)
+ − 93 *res++ = tolower (*tmp);
+ − 94 *res = '\0';
+ − 95 }
+ − 96 else
+ − 97 dir_handle = FindFirstFile (name, &find_data);
+ − 98
+ − 99 if (res || dir_handle != INVALID_HANDLE_VALUE)
+ − 100 {
+ − 101 if ((len = strlen (find_data.cFileName)) < size)
+ − 102 {
+ − 103 if (strcmp (lastname, find_data.cFileName) == 0)
+ − 104 /* Signal that the name is already OK. */
+ − 105 err = EINVAL;
+ − 106 else
+ − 107 memcpy (buf, find_data.cFileName, len + 1);
+ − 108 }
+ − 109 else
+ − 110 err = ENAMETOOLONG;
+ − 111 if (!res) FindClose (dir_handle);
+ − 112 }
+ − 113 else
+ − 114 err = ENOENT;
+ − 115
+ − 116 errno = err;
+ − 117 return err ? -1 : len;
+ − 118 }
+ − 119 #endif /* WIN32_NATIVE || CYGWIN */
+ − 120
+ − 121 #ifdef CYGWIN
+ − 122 /* Call readlink and try to find out the correct case for the file. */
+ − 123 static int
+ − 124 cygwin_readlink (const char * name, char * buf, int size)
+ − 125 {
+ − 126 int n = readlink (name, buf, size);
462
+ − 127 if (n < 0 && errno == EINVAL)
446
+ − 128 {
+ − 129 /* The file may exist, but isn't a symlink. Try to find the
+ − 130 right name. */
+ − 131 char* tmp = alloca (cygwin_posix_to_win32_path_list_buf_size (name));
+ − 132 cygwin_posix_to_win32_path_list (name, tmp);
+ − 133 n = win32_readlink (tmp, buf, size);
+ − 134 }
+ − 135 return n;
+ − 136 }
+ − 137 #endif /* CYGWIN */
+ − 138
+ − 139 #ifdef WIN32_NATIVE
+ − 140 #ifndef ELOOP
+ − 141 #define ELOOP 10062 /* = WSAELOOP in winsock.h */
+ − 142 #endif
+ − 143 /* Length of start of absolute filename. */
+ − 144 static int
+ − 145 win32_abs_start (const char * name)
+ − 146 {
+ − 147 if (isalpha (*name) && IS_DEVICE_SEP (name[1])
+ − 148 && IS_DIRECTORY_SEP (name[2]))
+ − 149 return 3;
+ − 150 else if (IS_DIRECTORY_SEP (*name))
+ − 151 return IS_DIRECTORY_SEP (name[1]) ? 2 : 1;
+ − 152 else
+ − 153 return 0;
+ − 154 }
+ − 155 #endif /* WIN32_NATIVE */
+ − 156
440
+ − 157 #if !defined (HAVE_GETCWD) && defined (HAVE_GETWD)
+ − 158 #undef getcwd
+ − 159 #define getcwd(buffer, len) getwd (buffer)
+ − 160 #endif
+ − 161
428
+ − 162 #ifndef PATH_MAX
440
+ − 163 # if defined (_POSIX_PATH_MAX)
+ − 164 # define PATH_MAX _POSIX_PATH_MAX
+ − 165 # elif defined (MAXPATHLEN)
+ − 166 # define PATH_MAX MAXPATHLEN
+ − 167 # else
+ − 168 # define PATH_MAX 1024
+ − 169 # endif
428
+ − 170 #endif
+ − 171
+ − 172 #define MAX_READLINKS 32
+ − 173
440
+ − 174 char * xrealpath (const char *path, char resolved_path []);
428
+ − 175 char *
+ − 176 xrealpath (const char *path, char resolved_path [])
+ − 177 {
+ − 178 char copy_path[PATH_MAX];
+ − 179 char *new_path = resolved_path;
+ − 180 char *max_path;
446
+ − 181 #if defined (S_IFLNK) || defined (WIN32_NATIVE)
428
+ − 182 int readlinks = 0;
+ − 183 char link_path[PATH_MAX];
+ − 184 int n;
446
+ − 185 int abslen = ABS_LENGTH (path);
428
+ − 186 #endif
+ − 187
+ − 188 /* Make a copy of the source path since we may need to modify it. */
442
+ − 189 strcpy (copy_path, path);
428
+ − 190 path = copy_path;
+ − 191 max_path = copy_path + PATH_MAX - 2;
446
+ − 192
442
+ − 193 #ifdef WIN32_NATIVE
446
+ − 194 /* Check for c:/... or //server/... */
+ − 195 if (abslen == 2 || abslen == 3)
428
+ − 196 {
446
+ − 197 strncpy (new_path, path, abslen);
462
+ − 198 /* Make sure drive letter is lowercased. */
+ − 199 if (abslen == 3)
+ − 200 *new_path = tolower (*new_path);
446
+ − 201 new_path += abslen;
+ − 202 path += abslen;
428
+ − 203 }
446
+ − 204 /* No drive letter, but a beginning slash? Prepend drive letter. */
+ − 205 else if (abslen == 1)
428
+ − 206 {
440
+ − 207 getcwd (new_path, PATH_MAX - 1);
428
+ − 208 new_path += 3;
+ − 209 path++;
+ − 210 }
446
+ − 211 /* Just a path name, prepend the current directory */
428
+ − 212 else
+ − 213 {
440
+ − 214 getcwd (new_path, PATH_MAX - 1);
446
+ − 215 new_path += strlen (new_path);
+ − 216 if (!IS_DIRECTORY_SEP (new_path[-1]))
+ − 217 *new_path++ = DIRECTORY_SEP;
428
+ − 218 }
+ − 219 #else
440
+ − 220 /* If it's a relative pathname use getcwd for starters. */
446
+ − 221 if (abslen == 0)
428
+ − 222 {
440
+ − 223 getcwd (new_path, PATH_MAX - 1);
446
+ − 224 new_path += strlen (new_path);
+ − 225 if (!IS_DIRECTORY_SEP (new_path[-1]))
+ − 226 *new_path++ = DIRECTORY_SEP;
428
+ − 227 }
+ − 228 else
+ − 229 {
446
+ − 230 /* Copy first directory sep. May have two on cygwin. */
+ − 231 strncpy (new_path, path, abslen);
+ − 232 new_path += abslen;
+ − 233 path += abslen;
428
+ − 234 }
+ − 235 #endif
+ − 236 /* Expand each slash-separated pathname component. */
+ − 237 while (*path != '\0')
+ − 238 {
+ − 239 /* Ignore stray "/". */
446
+ − 240 if (IS_DIRECTORY_SEP (*path))
428
+ − 241 {
+ − 242 path++;
+ − 243 continue;
+ − 244 }
+ − 245
+ − 246 if (*path == '.')
+ − 247 {
+ − 248 /* Ignore ".". */
446
+ − 249 if (path[1] == '\0' || IS_DIRECTORY_SEP (path[1]))
428
+ − 250 {
+ − 251 path++;
+ − 252 continue;
+ − 253 }
+ − 254
442
+ − 255 /* Handle ".." */
+ − 256 if (path[1] == '.' &&
446
+ − 257 (path[2] == '\0' || IS_DIRECTORY_SEP (path[2])))
428
+ − 258 {
442
+ − 259 path += 2;
428
+ − 260
442
+ − 261 /* Ignore ".." at root. */
446
+ − 262 if (new_path == ABS_START (resolved_path))
442
+ − 263 continue;
428
+ − 264
442
+ − 265 /* Handle ".." by backing up. */
446
+ − 266 --new_path;
+ − 267 while (!IS_DIRECTORY_SEP (new_path[-1]))
+ − 268 --new_path;
442
+ − 269 continue;
428
+ − 270 }
+ − 271 }
+ − 272
+ − 273 /* Safely copy the next pathname component. */
446
+ − 274 while (*path != '\0' && !IS_DIRECTORY_SEP (*path))
428
+ − 275 {
+ − 276 if (path > max_path)
+ − 277 {
+ − 278 errno = ENAMETOOLONG;
+ − 279 return NULL;
+ − 280 }
+ − 281 *new_path++ = *path++;
+ − 282 }
+ − 283
446
+ − 284 #if defined (S_IFLNK) || defined (WIN32_NATIVE)
428
+ − 285 /* See if latest pathname component is a symlink. */
+ − 286 *new_path = '\0';
446
+ − 287 n = system_readlink (resolved_path, link_path, PATH_MAX - 1);
428
+ − 288
+ − 289 if (n < 0)
+ − 290 {
+ − 291 /* EINVAL means the file exists but isn't a symlink. */
462
+ − 292 #ifdef CYGWIN
+ − 293 if (errno != EINVAL && errno != ENOENT)
+ − 294 #else
+ − 295 if (errno != EINVAL)
+ − 296 #endif
428
+ − 297 return NULL;
+ − 298 }
+ − 299 else
+ − 300 {
+ − 301 /* Protect against infinite loops. */
+ − 302 if (readlinks++ > MAX_READLINKS)
+ − 303 {
+ − 304 errno = ELOOP;
+ − 305 return NULL;
+ − 306 }
+ − 307
+ − 308 /* Note: readlink doesn't add the null byte. */
+ − 309 link_path[n] = '\0';
446
+ − 310
+ − 311 if (ABS_LENGTH (link_path) > 0)
428
+ − 312 /* Start over for an absolute symlink. */
446
+ − 313 new_path = resolved_path + ABS_LENGTH (link_path) - 1;
428
+ − 314 else
+ − 315 /* Otherwise back up over this component. */
446
+ − 316 for (--new_path; !IS_DIRECTORY_SEP (*new_path); --new_path)
+ − 317 assert (new_path > resolved_path);
428
+ − 318
+ − 319 /* Safe sex check. */
+ − 320 if (strlen(path) + n >= PATH_MAX)
+ − 321 {
+ − 322 errno = ENAMETOOLONG;
+ − 323 return NULL;
+ − 324 }
+ − 325
+ − 326 /* Insert symlink contents into path. */
+ − 327 strcat(link_path, path);
+ − 328 strcpy(copy_path, link_path);
+ − 329 path = copy_path;
+ − 330 }
446
+ − 331 #endif /* S_IFLNK || WIN32_NATIVE */
+ − 332 *new_path++ = DIRECTORY_SEP;
428
+ − 333 }
+ − 334
+ − 335 /* Delete trailing slash but don't whomp a lone slash. */
446
+ − 336 if (new_path != ABS_START (resolved_path) && IS_DIRECTORY_SEP (new_path[-1]))
428
+ − 337 new_path--;
+ − 338
+ − 339 /* Make sure it's null terminated. */
+ − 340 *new_path = '\0';
446
+ − 341
428
+ − 342 return resolved_path;
+ − 343 }