1 /* Return the canonical absolute name of a given file. 2 Copyright (C) 1996-2005 Free Software Foundation, Inc. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2, or (at your option) 7 any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; see the file COPYING. 16 If not, write to the Free Software Foundation, 17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 18 19 #ifdef HAVE_CONFIG_H 20 # include <config.h> 21 #endif 22 23 #include "canonicalize.h" 24 25 #ifdef STDC_HEADERS 26 # include <stdlib.h> 27 #else 28 void free (); 29 #endif 30 31 #if defined STDC_HEADERS || defined HAVE_STRING_H 32 # include <string.h> 33 #else 34 # include <strings.h> 35 #endif 36 37 #if HAVE_SYS_PARAM_H 38 # include <sys/param.h> 39 #endif 40 41 #include <sys/stat.h> 42 43 #if HAVE_UNISTD_H 44 # include <unistd.h> 45 #endif 46 47 #include <errno.h> 48 #include <stddef.h> 49 50 #include "cycle-check.h" 51 #include "filenamecat.h" 52 #include "stat-macros.h" 53 #include "xalloc.h" 54 #include "xgetcwd.h" 55 56 #ifndef __set_errno 57 # define __set_errno(Val) errno = (Val) 58 #endif 59 60 #include "pathmax.h" 61 #include "xreadlink.h" 62 63 #if !HAVE_CANONICALIZE_FILE_NAME 64 /* Return the canonical absolute name of file NAME. A canonical name 65 does not contain any `.', `..' components nor any repeated file name 66 separators ('/') or symlinks. All components must exist. 67 The result is malloc'd. */ 68 69 char * 70 canonicalize_file_name (const char *name) 71 { 72 # if HAVE_RESOLVEPATH 73 74 char *resolved, *extra_buf = NULL; 75 size_t resolved_size; 76 ssize_t resolved_len; 77 78 if (name == NULL) 79 { 80 __set_errno (EINVAL); 81 return NULL; 82 } 83 84 if (name[0] == '\0') 85 { 86 __set_errno (ENOENT); 87 return NULL; 88 } 89 90 /* All known hosts with resolvepath (e.g. Solaris 7) don't turn 91 relative names into absolute ones, so prepend the working 92 directory if the file name is not absolute. */ 93 if (name[0] != '/') 94 { 95 char *wd; 96 97 if (!(wd = xgetcwd ())) 98 return NULL; 99 100 extra_buf = file_name_concat (wd, name, NULL); 101 name = extra_buf; 102 free (wd); 103 } 104 105 resolved_size = strlen (name); 106 while (1) 107 { 108 resolved_size = 2 * resolved_size + 1; 109 resolved = xmalloc (resolved_size); 110 resolved_len = resolvepath (name, resolved, resolved_size); 111 if (resolved_len < 0) 112 { 113 free (resolved); 114 free (extra_buf); 115 return NULL; 116 } 117 if (resolved_len < resolved_size) 118 break; 119 free (resolved); 120 } 121 122 free (extra_buf); 123 124 /* NUL-terminate the resulting name. */ 125 resolved[resolved_len] = '\0'; 126 127 return resolved; 128 129 # else 130 131 return canonicalize_filename_mode (name, CAN_EXISTING); 132 133 # endif /* !HAVE_RESOLVEPATH */ 134 } 135 #endif /* !HAVE_CANONICALIZE_FILE_NAME */ 136 137 /* Return the canonical absolute name of file NAME. A canonical name 138 does not contain any `.', `..' components nor any repeated file name 139 separators ('/') or symlinks. Whether components must exist 140 or not depends on canonicalize mode. The result is malloc'd. */ 141 142 char * 143 canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode) 144 { 145 char *rname, *dest, *extra_buf = NULL; 146 char const *start; 147 char const *end; 148 char const *rname_limit; 149 size_t extra_len = 0; 150 struct cycle_check_state cycle_state; 151 152 if (name == NULL) 153 { 154 __set_errno (EINVAL); 155 return NULL; 156 } 157 158 if (name[0] == '\0') 159 { 160 __set_errno (ENOENT); 161 return NULL; 162 } 163 164 if (name[0] != '/') 165 { 166 rname = xgetcwd (); 167 if (!rname) 168 return NULL; 169 dest = strchr (rname, '\0'); 170 if (dest - rname < PATH_MAX) 171 { 172 char *p = xrealloc (rname, PATH_MAX); 173 dest = p + (dest - rname); 174 rname = p; 175 rname_limit = rname + PATH_MAX; 176 } 177 else 178 { 179 rname_limit = dest; 180 } 181 } 182 else 183 { 184 rname = xmalloc (PATH_MAX); 185 rname_limit = rname + PATH_MAX; 186 rname[0] = '/'; 187 dest = rname + 1; 188 } 189 190 cycle_check_init (&cycle_state); 191 for (start = end = name; *start; start = end) 192 { 193 /* Skip sequence of multiple file name separators. */ 194 while (*start == '/') 195 ++start; 196 197 /* Find end of component. */ 198 for (end = start; *end && *end != '/'; ++end) 199 /* Nothing. */; 200 201 if (end - start == 0) 202 break; 203 else if (end - start == 1 && start[0] == '.') 204 /* nothing */; 205 else if (end - start == 2 && start[0] == '.' && start[1] == '.') 206 { 207 /* Back up to previous component, ignore if at root already. */ 208 if (dest > rname + 1) 209 while ((--dest)[-1] != '/'); 210 } 211 else 212 { 213 struct stat st; 214 215 if (dest[-1] != '/') 216 *dest++ = '/'; 217 218 if (dest + (end - start) >= rname_limit) 219 { 220 ptrdiff_t dest_offset = dest - rname; 221 size_t new_size = rname_limit - rname; 222 223 if (end - start + 1 > PATH_MAX) 224 new_size += end - start + 1; 225 else 226 new_size += PATH_MAX; 227 rname = xrealloc (rname, new_size); 228 rname_limit = rname + new_size; 229 230 dest = rname + dest_offset; 231 } 232 233 dest = memcpy (dest, start, end - start); 234 dest += end - start; 235 *dest = '\0'; 236 237 if (lstat (rname, &st) != 0) 238 { 239 if (can_mode == CAN_EXISTING) 240 goto error; 241 if (can_mode == CAN_ALL_BUT_LAST && *end) 242 goto error; 243 st.st_mode = 0; 244 } 245 246 if (S_ISLNK (st.st_mode)) 247 { 248 char *buf; 249 size_t n, len; 250 251 if (cycle_check (&cycle_state, &st)) 252 { 253 __set_errno (ELOOP); 254 if (can_mode == CAN_MISSING) 255 continue; 256 else 257 goto error; 258 } 259 260 buf = xreadlink (rname, st.st_size); 261 if (!buf) 262 { 263 if (can_mode == CAN_MISSING) 264 continue; 265 else 266 goto error; 267 } 268 269 n = strlen (buf); 270 len = strlen (end); 271 272 if (!extra_len) 273 { 274 extra_len = 275 ((n + len + 1) > PATH_MAX) ? (n + len + 1) : PATH_MAX; 276 extra_buf = xmalloc (extra_len); 277 } 278 else if ((n + len + 1) > extra_len) 279 { 280 extra_len = n + len + 1; 281 extra_buf = xrealloc (extra_buf, extra_len); 282 } 283 284 /* Careful here, end may be a pointer into extra_buf... */ 285 memmove (&extra_buf[n], end, len + 1); 286 name = end = memcpy (extra_buf, buf, n); 287 288 if (buf[0] == '/') 289 dest = rname + 1; /* It's an absolute symlink */ 290 else 291 /* Back up to previous component, ignore if at root already: */ 292 if (dest > rname + 1) 293 while ((--dest)[-1] != '/'); 294 295 free (buf); 296 } 297 else 298 { 299 if (!S_ISDIR (st.st_mode) && *end && (can_mode != CAN_MISSING)) 300 { 301 errno = ENOTDIR; 302 goto error; 303 } 304 } 305 } 306 } 307 if (dest > rname + 1 && dest[-1] == '/') 308 --dest; 309 *dest = '\0'; 310 311 free (extra_buf); 312 return rname; 313 314 error: 315 free (extra_buf); 316 free (rname); 317 return NULL; 318 } 319