1 | /* pathphys.c -- Return pathname with all symlinks expanded. */
|
---|
2 |
|
---|
3 | /* Copyright (C) 2000 Free Software Foundation, Inc.
|
---|
4 |
|
---|
5 | This file is part of GNU Bash, the Bourne Again SHell.
|
---|
6 |
|
---|
7 | Bash is free software; you can redistribute it and/or modify it under
|
---|
8 | the terms of the GNU General Public License as published by the Free
|
---|
9 | Software Foundation; either version 2, or (at your option) any later
|
---|
10 | version.
|
---|
11 |
|
---|
12 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY
|
---|
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
---|
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
---|
15 | for more details.
|
---|
16 |
|
---|
17 | You should have received a copy of the GNU General Public License along
|
---|
18 | with Bash; see the file COPYING. If not, write to the Free Software
|
---|
19 | Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
|
---|
20 |
|
---|
21 | #include <config.h>
|
---|
22 |
|
---|
23 | #include <bashtypes.h>
|
---|
24 | #ifndef _MINIX
|
---|
25 | # include <sys/param.h>
|
---|
26 | #endif
|
---|
27 | #include <posixstat.h>
|
---|
28 |
|
---|
29 | #if defined (HAVE_UNISTD_H)
|
---|
30 | # include <unistd.h>
|
---|
31 | #endif
|
---|
32 |
|
---|
33 | #include <filecntl.h>
|
---|
34 | #include <bashansi.h>
|
---|
35 | #include <stdio.h>
|
---|
36 | #include <chartypes.h>
|
---|
37 | #include <errno.h>
|
---|
38 |
|
---|
39 | #include "shell.h"
|
---|
40 |
|
---|
41 | #if !defined (MAXSYMLINKS)
|
---|
42 | # define MAXSYMLINKS 32
|
---|
43 | #endif
|
---|
44 |
|
---|
45 | #if !defined (errno)
|
---|
46 | extern int errno;
|
---|
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.
|
---|
71 | * This always gets an absolute pathname.
|
---|
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 |
|
---|
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 |
|
---|
91 | nlink = 0;
|
---|
92 | q = result = (char *)xmalloc (PATH_MAX + 1);
|
---|
93 |
|
---|
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 | }
|
---|
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). (Ditto for OS/2. --bird) */
|
---|
108 | #if defined (__CYGWIN__) || defined (__OS2__)
|
---|
109 | qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
|
---|
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))
|
---|
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 | }
|
---|
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;
|
---|
181 | #else
|
---|
182 | errno = EINVAL;
|
---|
183 | #endif
|
---|
184 | error:
|
---|
185 | free (result);
|
---|
186 | free (workpath);
|
---|
187 | return ((char *)NULL);
|
---|
188 | }
|
---|
189 |
|
---|
190 | linkbuf[linklen] = '\0';
|
---|
191 |
|
---|
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 |
|
---|
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__) || defined (__OS2__)
|
---|
218 | qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
|
---|
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);
|
---|
272 | tdir = sh_makepath ((char *)pathname, wd, 0);
|
---|
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';
|
---|
288 | free (wd);
|
---|
289 | return resolved;
|
---|
290 | }
|
---|
291 | else
|
---|
292 | {
|
---|
293 | resolved[0] = '\0';
|
---|
294 | return wd;
|
---|
295 | }
|
---|
296 | }
|
---|