Jari Aalto | 3185942 | 2009-01-12 13:36:28 +0000 | [diff] [blame] | 1 | /* pathphys.c -- return pathname with all symlinks expanded. */ |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 2 | |
| 3 | /* Copyright (C) 2000 Free Software Foundation, Inc. |
| 4 | |
| 5 | This file is part of GNU Bash, the Bourne Again SHell. |
| 6 | |
Jari Aalto | 3185942 | 2009-01-12 13:36:28 +0000 | [diff] [blame] | 7 | Bash is free software: you can redistribute it and/or modify |
| 8 | it under the terms of the GNU General Public License as published by |
| 9 | the Free Software Foundation, either version 3 of the License, or |
| 10 | (at your option) any later version. |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 11 | |
Jari Aalto | 3185942 | 2009-01-12 13:36:28 +0000 | [diff] [blame] | 12 | Bash is distributed in the hope that it will be useful, |
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | GNU General Public License for more details. |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 16 | |
Jari Aalto | 3185942 | 2009-01-12 13:36:28 +0000 | [diff] [blame] | 17 | You should have received a copy of the GNU General Public License |
| 18 | along with Bash. If not, see <http://www.gnu.org/licenses/>. |
| 19 | */ |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 20 | |
Jari Aalto | f73dda0 | 2001-11-13 17:56:06 +0000 | [diff] [blame] | 21 | #include <config.h> |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 22 | |
Jari Aalto | f73dda0 | 2001-11-13 17:56:06 +0000 | [diff] [blame] | 23 | #include <bashtypes.h> |
Chet Ramey | ac50fba | 2014-02-26 09:36:43 -0500 | [diff] [blame] | 24 | #if defined (HAVE_SYS_PARAM_H) |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 25 | # include <sys/param.h> |
| 26 | #endif |
Jari Aalto | f73dda0 | 2001-11-13 17:56:06 +0000 | [diff] [blame] | 27 | #include <posixstat.h> |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 28 | |
| 29 | #if defined (HAVE_UNISTD_H) |
| 30 | # include <unistd.h> |
| 31 | #endif |
| 32 | |
Jari Aalto | f73dda0 | 2001-11-13 17:56:06 +0000 | [diff] [blame] | 33 | #include <filecntl.h> |
| 34 | #include <bashansi.h> |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 35 | #include <stdio.h> |
Jari Aalto | f73dda0 | 2001-11-13 17:56:06 +0000 | [diff] [blame] | 36 | #include <chartypes.h> |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 37 | #include <errno.h> |
| 38 | |
| 39 | #include "shell.h" |
| 40 | |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 41 | #if !defined (MAXSYMLINKS) |
| 42 | # define MAXSYMLINKS 32 |
| 43 | #endif |
| 44 | |
| 45 | #if !defined (errno) |
Ricardo Cerqueira | a02fbff | 2013-07-25 22:35:34 +0100 | [diff] [blame] | 46 | #include <errno.h> |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 47 | #endif /* !errno */ |
| 48 | |
| 49 | extern char *get_working_directory __P((char *)); |
| 50 | |
| 51 | static int |
| 52 | _path_readlink (path, buf, bufsiz) |
| 53 | char *path; |
| 54 | char *buf; |
| 55 | int bufsiz; |
| 56 | { |
| 57 | #ifdef HAVE_READLINK |
| 58 | return readlink (path, buf, bufsiz); |
| 59 | #else |
| 60 | errno = EINVAL; |
| 61 | return -1; |
| 62 | #endif |
| 63 | } |
| 64 | |
| 65 | /* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */ |
| 66 | |
| 67 | #define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/') |
| 68 | |
| 69 | /* |
| 70 | * Return PATH with all symlinks expanded in newly-allocated memory. |
Jari Aalto | b80f644 | 2004-07-27 13:29:18 +0000 | [diff] [blame] | 71 | * This always gets an absolute pathname. |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 72 | */ |
| 73 | |
| 74 | char * |
| 75 | sh_physpath (path, flags) |
| 76 | char *path; |
| 77 | int flags; |
| 78 | { |
| 79 | char tbuf[PATH_MAX+1], linkbuf[PATH_MAX+1]; |
| 80 | char *result, *p, *q, *qsave, *qbase, *workpath; |
| 81 | int double_slash_path, linklen, nlink; |
| 82 | |
Jari Aalto | b80f644 | 2004-07-27 13:29:18 +0000 | [diff] [blame] | 83 | linklen = strlen (path); |
| 84 | |
| 85 | #if 0 |
| 86 | /* First sanity check -- punt immediately if the name is too long. */ |
| 87 | if (linklen >= PATH_MAX) |
| 88 | return (savestring (path)); |
| 89 | #endif |
| 90 | |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 91 | nlink = 0; |
Jari Aalto | f73dda0 | 2001-11-13 17:56:06 +0000 | [diff] [blame] | 92 | q = result = (char *)xmalloc (PATH_MAX + 1); |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 93 | |
Jari Aalto | b80f644 | 2004-07-27 13:29:18 +0000 | [diff] [blame] | 94 | /* Even if we get something longer than PATH_MAX, we might be able to |
| 95 | shorten it, so we try. */ |
| 96 | if (linklen >= PATH_MAX) |
| 97 | workpath = savestring (path); |
| 98 | else |
| 99 | { |
| 100 | workpath = (char *)xmalloc (PATH_MAX + 1); |
| 101 | strcpy (workpath, path); |
| 102 | } |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 103 | |
| 104 | /* This always gets an absolute pathname. */ |
| 105 | |
| 106 | /* POSIX.2 says to leave a leading `//' alone. On cygwin, we skip over any |
| 107 | leading `x:' (dos drive name). */ |
| 108 | #if defined (__CYGWIN__) |
Jari Aalto | f73dda0 | 2001-11-13 17:56:06 +0000 | [diff] [blame] | 109 | qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1; |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 110 | #else |
| 111 | qbase = workpath + 1; |
| 112 | #endif |
| 113 | double_slash_path = DOUBLE_SLASH (workpath); |
| 114 | qbase += double_slash_path; |
| 115 | |
| 116 | for (p = workpath; p < qbase; ) |
| 117 | *q++ = *p++; |
| 118 | qbase = q; |
| 119 | |
| 120 | /* |
| 121 | * invariants: |
| 122 | * qbase points to the portion of the result path we want to modify |
| 123 | * p points at beginning of path element we're considering. |
| 124 | * q points just past the last path element we wrote (no slash). |
| 125 | * |
| 126 | * XXX -- need to fix error checking for too-long pathnames |
| 127 | */ |
| 128 | |
| 129 | while (*p) |
| 130 | { |
| 131 | if (ISDIRSEP(p[0])) /* null element */ |
| 132 | p++; |
| 133 | else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */ |
| 134 | p += 1; /* don't count the separator in case it is nul */ |
| 135 | else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */ |
| 136 | { |
| 137 | p += 2; /* skip `..' */ |
| 138 | if (q > qbase) |
| 139 | { |
| 140 | while (--q > qbase && ISDIRSEP(*q) == 0) |
| 141 | ; |
| 142 | } |
| 143 | } |
| 144 | else /* real path element */ |
| 145 | { |
| 146 | /* add separator if not at start of work portion of result */ |
| 147 | qsave = q; |
| 148 | if (q != qbase) |
| 149 | *q++ = DIRSEP; |
| 150 | while (*p && (ISDIRSEP(*p) == 0)) |
Jari Aalto | b80f644 | 2004-07-27 13:29:18 +0000 | [diff] [blame] | 151 | { |
| 152 | if (q - result >= PATH_MAX) |
| 153 | { |
| 154 | #ifdef ENAMETOOLONG |
| 155 | errno = ENAMETOOLONG; |
| 156 | #else |
| 157 | errno = EINVAL; |
| 158 | #endif |
| 159 | goto error; |
| 160 | } |
| 161 | |
| 162 | *q++ = *p++; |
| 163 | } |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 164 | |
| 165 | *q = '\0'; |
| 166 | |
| 167 | linklen = _path_readlink (result, linkbuf, PATH_MAX); |
| 168 | if (linklen < 0) /* if errno == EINVAL, it's not a symlink */ |
| 169 | { |
| 170 | if (errno != EINVAL) |
| 171 | goto error; |
| 172 | continue; |
| 173 | } |
| 174 | |
| 175 | /* It's a symlink, and the value is in LINKBUF. */ |
| 176 | nlink++; |
| 177 | if (nlink > MAXSYMLINKS) |
| 178 | { |
| 179 | #ifdef ELOOP |
| 180 | errno = ELOOP; |
Jari Aalto | b80f644 | 2004-07-27 13:29:18 +0000 | [diff] [blame] | 181 | #else |
| 182 | errno = EINVAL; |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 183 | #endif |
| 184 | error: |
| 185 | free (result); |
| 186 | free (workpath); |
| 187 | return ((char *)NULL); |
| 188 | } |
| 189 | |
| 190 | linkbuf[linklen] = '\0'; |
| 191 | |
Jari Aalto | b80f644 | 2004-07-27 13:29:18 +0000 | [diff] [blame] | 192 | /* If the new path length would overrun PATH_MAX, punt now. */ |
| 193 | if ((strlen (p) + linklen + 2) >= PATH_MAX) |
| 194 | { |
| 195 | #ifdef ENAMETOOLONG |
| 196 | errno = ENAMETOOLONG; |
| 197 | #else |
| 198 | errno = EINVAL; |
| 199 | #endif |
| 200 | goto error; |
| 201 | } |
| 202 | |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 203 | /* Form the new pathname by copying the link value to a temporary |
| 204 | buffer and appending the rest of `workpath'. Reset p to point |
| 205 | to the start of the rest of the path. If the link value is an |
| 206 | absolute pathname, reset p, q, and qbase. If not, reset p |
| 207 | and q. */ |
| 208 | strcpy (tbuf, linkbuf); |
| 209 | tbuf[linklen] = '/'; |
| 210 | strcpy (tbuf + linklen, p); |
| 211 | strcpy (workpath, tbuf); |
| 212 | |
| 213 | if (ABSPATH(linkbuf)) |
| 214 | { |
| 215 | q = result; |
| 216 | /* Duplicating some code here... */ |
| 217 | #if defined (__CYGWIN__) |
Jari Aalto | f73dda0 | 2001-11-13 17:56:06 +0000 | [diff] [blame] | 218 | qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1; |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 219 | #else |
| 220 | qbase = workpath + 1; |
| 221 | #endif |
| 222 | double_slash_path = DOUBLE_SLASH (workpath); |
| 223 | qbase += double_slash_path; |
| 224 | |
| 225 | for (p = workpath; p < qbase; ) |
| 226 | *q++ = *p++; |
| 227 | qbase = q; |
| 228 | } |
| 229 | else |
| 230 | { |
| 231 | p = workpath; |
| 232 | q = qsave; |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | *q = '\0'; |
| 238 | free (workpath); |
| 239 | |
| 240 | /* If the result starts with `//', but the original path does not, we |
| 241 | can turn the // into /. Because of how we set `qbase', this should never |
| 242 | be true, but it's a sanity check. */ |
| 243 | if (DOUBLE_SLASH(result) && double_slash_path == 0) |
| 244 | { |
| 245 | if (result[2] == '\0') /* short-circuit for bare `//' */ |
| 246 | result[1] = '\0'; |
| 247 | else |
| 248 | strcpy (result, result + 1); |
| 249 | } |
| 250 | |
| 251 | return (result); |
| 252 | } |
| 253 | |
| 254 | char * |
| 255 | sh_realpath (pathname, resolved) |
| 256 | const char *pathname; |
| 257 | char *resolved; |
| 258 | { |
| 259 | char *tdir, *wd; |
| 260 | |
| 261 | if (pathname == 0 || *pathname == '\0') |
| 262 | { |
| 263 | errno = (pathname == 0) ? EINVAL : ENOENT; |
| 264 | return ((char *)NULL); |
| 265 | } |
| 266 | |
| 267 | if (ABSPATH (pathname) == 0) |
| 268 | { |
| 269 | wd = get_working_directory ("sh_realpath"); |
| 270 | if (wd == 0) |
| 271 | return ((char *)NULL); |
Chet Ramey | ac50fba | 2014-02-26 09:36:43 -0500 | [diff] [blame] | 272 | tdir = sh_makepath (wd, (char *)pathname, 0); |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 273 | free (wd); |
| 274 | } |
| 275 | else |
| 276 | tdir = savestring (pathname); |
| 277 | |
| 278 | wd = sh_physpath (tdir, 0); |
| 279 | free (tdir); |
| 280 | |
| 281 | if (resolved == 0) |
| 282 | return (wd); |
| 283 | |
| 284 | if (wd) |
| 285 | { |
| 286 | strncpy (resolved, wd, PATH_MAX - 1); |
| 287 | resolved[PATH_MAX - 1] = '\0'; |
Jari Aalto | 95732b4 | 2005-12-07 14:08:12 +0000 | [diff] [blame] | 288 | free (wd); |
Jari Aalto | 28ef6c3 | 2001-04-06 19:14:31 +0000 | [diff] [blame] | 289 | return resolved; |
| 290 | } |
| 291 | else |
| 292 | { |
| 293 | resolved[0] = '\0'; |
| 294 | return wd; |
| 295 | } |
| 296 | } |