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