Bash  5.0-beta2
Bash - Bourne Again shell
pathphys.c
Go to the documentation of this file.
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
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.
11 
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.
16 
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 */
20 
21 #include <config.h>
22 
23 #include <bashtypes.h>
24 #if defined (HAVE_SYS_PARAM_H)
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 *
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). */
108 #if defined (__CYGWIN__)
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__)
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  memmove (result, result + 1, strlen (result + 1) + 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 (wd, (char *)pathname, 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 }
#define PATHSEP(c)
Definition: general.h:288
#define ISDIRSEP(c)
Definition: general.h:284
#define ABSPATH(x)
Definition: general.h:273
#define DOUBLE_SLASH(p)
Definition: pathphys.c:67
char *get_working_directory __P((char *))
char * sh_makepath(char *path, char *dir, int flags) const
Definition: makepath.c:70
p
Definition: glob_loop.c:31
#define MAXSYMLINKS
Definition: pathphys.c:42
static char tdir[PATH_MAX]
Definition: general.c:805
char * savestring(const char *s)
Definition: savestring.c:33
char * strcpy()
#define PATH_MAX
Definition: maxpath.h:63
char * sh_realpath(char *pathname, char *resolved) const
Definition: pathphys.c:255
void free()
#define DIRSEP
Definition: general.h:282
int errno
char * get_working_directory(char *for_whom)
Definition: common.c:561
#define NULL
Definition: general.h:53
int flags
Definition: gm_loop.c:47
char * sh_physpath(char *path, int flags)
Definition: pathphys.c:75
static int _path_readlink(char *path, char *buf, int bufsiz)
Definition: pathphys.c:52
static char * xmalloc()
#define ISALPHA(c)
Definition: chartypes.h:73