blob: 783e8c5bd00ece25346f2faa27d0c0ecaf9f1d77 [file] [log] [blame]
Jari Aalto31859422009-01-12 13:36:28 +00001/* pathphys.c -- return pathname with all symlinks expanded. */
Jari Aalto28ef6c32001-04-06 19:14:31 +00002
3/* Copyright (C) 2000 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
Jari Aalto31859422009-01-12 13:36:28 +00007 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 Aalto28ef6c32001-04-06 19:14:31 +000011
Jari Aalto31859422009-01-12 13:36:28 +000012 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 Aalto28ef6c32001-04-06 19:14:31 +000016
Jari Aalto31859422009-01-12 13:36:28 +000017 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 Aalto28ef6c32001-04-06 19:14:31 +000020
Jari Aaltof73dda02001-11-13 17:56:06 +000021#include <config.h>
Jari Aalto28ef6c32001-04-06 19:14:31 +000022
Jari Aaltof73dda02001-11-13 17:56:06 +000023#include <bashtypes.h>
Chet Rameyac50fba2014-02-26 09:36:43 -050024#if defined (HAVE_SYS_PARAM_H)
Jari Aalto28ef6c32001-04-06 19:14:31 +000025# include <sys/param.h>
26#endif
Jari Aaltof73dda02001-11-13 17:56:06 +000027#include <posixstat.h>
Jari Aalto28ef6c32001-04-06 19:14:31 +000028
29#if defined (HAVE_UNISTD_H)
30# include <unistd.h>
31#endif
32
Jari Aaltof73dda02001-11-13 17:56:06 +000033#include <filecntl.h>
34#include <bashansi.h>
Jari Aalto28ef6c32001-04-06 19:14:31 +000035#include <stdio.h>
Jari Aaltof73dda02001-11-13 17:56:06 +000036#include <chartypes.h>
Jari Aalto28ef6c32001-04-06 19:14:31 +000037#include <errno.h>
38
39#include "shell.h"
40
Jari Aalto28ef6c32001-04-06 19:14:31 +000041#if !defined (MAXSYMLINKS)
42# define MAXSYMLINKS 32
43#endif
44
45#if !defined (errno)
Ricardo Cerqueiraa02fbff2013-07-25 22:35:34 +010046#include <errno.h>
Jari Aalto28ef6c32001-04-06 19:14:31 +000047#endif /* !errno */
48
49extern char *get_working_directory __P((char *));
50
51static 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 Aaltob80f6442004-07-27 13:29:18 +000071 * This always gets an absolute pathname.
Jari Aalto28ef6c32001-04-06 19:14:31 +000072 */
73
74char *
75sh_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 Aaltob80f6442004-07-27 13:29:18 +000083 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 Aalto28ef6c32001-04-06 19:14:31 +000091 nlink = 0;
Jari Aaltof73dda02001-11-13 17:56:06 +000092 q = result = (char *)xmalloc (PATH_MAX + 1);
Jari Aalto28ef6c32001-04-06 19:14:31 +000093
Jari Aaltob80f6442004-07-27 13:29:18 +000094 /* 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 Aalto28ef6c32001-04-06 19:14:31 +0000103
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 Aaltof73dda02001-11-13 17:56:06 +0000109 qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000110#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 Aaltob80f6442004-07-27 13:29:18 +0000151 {
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 Aalto28ef6c32001-04-06 19:14:31 +0000164
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 Aaltob80f6442004-07-27 13:29:18 +0000181#else
182 errno = EINVAL;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000183#endif
184error:
185 free (result);
186 free (workpath);
187 return ((char *)NULL);
188 }
189
190 linkbuf[linklen] = '\0';
191
Jari Aaltob80f6442004-07-27 13:29:18 +0000192 /* 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 Aalto28ef6c32001-04-06 19:14:31 +0000203 /* 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 Aaltof73dda02001-11-13 17:56:06 +0000218 qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000219#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
254char *
255sh_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 Rameyac50fba2014-02-26 09:36:43 -0500272 tdir = sh_makepath (wd, (char *)pathname, 0);
Jari Aalto28ef6c32001-04-06 19:14:31 +0000273 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 Aalto95732b42005-12-07 14:08:12 +0000288 free (wd);
Jari Aalto28ef6c32001-04-06 19:14:31 +0000289 return resolved;
290 }
291 else
292 {
293 resolved[0] = '\0';
294 return wd;
295 }
296}