Merge commit 'upstream/1.7.6p1'
[debian/sudo] / getcwd.c
1 /*
2  * Copyright (c) 1989, 1991, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <config.h>
31
32 #include <sys/param.h>
33 #include <sys/stat.h>
34
35 #include <errno.h>
36 #include <stdio.h>
37 #ifdef STDC_HEADERS
38 # include <stdlib.h>
39 # include <stddef.h>
40 #else
41 # ifdef HAVE_STDLIB_H
42 #  include <stdlib.h>
43 # endif
44 #endif /* STDC_HEADERS */
45 #ifdef HAVE_STRING_H
46 # include <string.h>
47 #endif /* HAVE_STRING_H */
48 #ifdef HAVE_STRINGS_H
49 # include <strings.h>
50 #endif /* HAVE_STRINGS_H */
51 #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
52 # include <malloc.h>
53 #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
54 #ifdef HAVE_UNISTD_H
55 # include <unistd.h>
56 #endif /* HAVE_UNISTD_H */
57 #ifdef HAVE_DIRENT_H
58 # include <dirent.h>
59 # define NAMLEN(dirent) strlen((dirent)->d_name)
60 #else
61 # define dirent direct
62 # define NAMLEN(dirent) (dirent)->d_namlen
63 # ifdef HAVE_SYS_NDIR_H
64 #  include <sys/ndir.h>
65 # endif
66 # ifdef HAVE_SYS_DIR_H
67 #  include <sys/dir.h>
68 # endif
69 # ifdef HAVE_NDIR_H
70 #  include <ndir.h>
71 # endif
72 #endif
73
74 #include "missing.h"
75
76 #define ISDOT(dp) \
77         (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
78             (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
79
80 char *
81 getcwd(pt, size)
82         char *pt;
83         size_t size;
84 {
85         struct dirent *dp;
86         DIR *dir = NULL;
87         dev_t dev;
88         ino_t ino;
89         int first;
90         char *bpt, *bup;
91         struct stat s;
92         dev_t root_dev;
93         ino_t root_ino;
94         size_t ptsize, upsize;
95         int save_errno;
96         char *ept, *eup, *up;
97
98         /*
99          * If no buffer specified by the user, allocate one as necessary.
100          * If a buffer is specified, the size has to be non-zero.  The path
101          * is built from the end of the buffer backwards.
102          */
103         if (pt) {
104                 ptsize = 0;
105                 if (!size) {
106                         errno = EINVAL;
107                         return NULL;
108                 }
109                 ept = pt + size;
110         } else {
111                 if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
112                         return NULL;
113                 ept = pt + ptsize;
114         }
115         bpt = ept - 1;
116         *bpt = '\0';
117
118         /*
119          * Allocate bytes (1024 - malloc space) for the string of "../"'s.
120          * Should always be enough (it's 340 levels).  If it's not, allocate
121          * as necessary.  Special * case the first stat, it's ".", not "..".
122          */
123         if ((up = malloc(upsize = 1024 - 4)) == NULL)
124                 goto err;
125         eup = up + PATH_MAX;
126         bup = up;
127         up[0] = '.';
128         up[1] = '\0';
129
130         /* Save root values, so know when to stop. */
131         if (stat("/", &s))
132                 goto err;
133         root_dev = s.st_dev;
134         root_ino = s.st_ino;
135
136         errno = 0;                      /* XXX readdir has no error return. */
137
138         for (first = 1;; first = 0) {
139                 /* Stat the current level. */
140                 if (lstat(up, &s))
141                         goto err;
142
143                 /* Save current node values. */
144                 ino = s.st_ino;
145                 dev = s.st_dev;
146
147                 /* Check for reaching root. */
148                 if (root_dev == dev && root_ino == ino) {
149                         *--bpt = '/';
150                         /*
151                          * It's unclear that it's a requirement to copy the
152                          * path to the beginning of the buffer, but it's always
153                          * been that way and stuff would probably break.
154                          */
155                         bcopy(bpt, pt, ept - bpt);
156                         free(up);
157                         return pt;
158                 }
159
160                 /*
161                  * Build pointer to the parent directory, allocating memory
162                  * as necessary.  Max length is 3 for "../", the largest
163                  * possible component name, plus a trailing NULL.
164                  */
165                 if (bup + 3  + MAXNAMLEN + 1 >= eup) {
166                         char *nup;
167
168                         if ((nup = realloc(up, upsize *= 2)) == NULL)
169                                 goto err;
170                         up = nup;
171                         bup = up;
172                         eup = up + upsize;
173                 }
174                 *bup++ = '.';
175                 *bup++ = '.';
176                 *bup = '\0';
177
178                 /* Open and stat parent directory. */
179                 if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
180                         goto err;
181
182                 /* Add trailing slash for next directory. */
183                 *bup++ = '/';
184
185                 /*
186                  * If it's a mount point, have to stat each element because
187                  * the inode number in the directory is for the entry in the
188                  * parent directory, not the inode number of the mounted file.
189                  */
190                 save_errno = 0;
191                 if (s.st_dev == dev) {
192                         for (;;) {
193                                 if (!(dp = readdir(dir)))
194                                         goto notfound;
195                                 if (dp->d_fileno == ino)
196                                         break;
197                         }
198                 } else
199                         for (;;) {
200                                 if (!(dp = readdir(dir)))
201                                         goto notfound;
202                                 if (ISDOT(dp))
203                                         continue;
204                                 bcopy(dp->d_name, bup, NAMLEN(dp) + 1);
205
206                                 /* Save the first error for later. */
207                                 if (lstat(up, &s)) {
208                                         if (!save_errno)
209                                                 save_errno = errno;
210                                         errno = 0;
211                                         continue;
212                                 }
213                                 if (s.st_dev == dev && s.st_ino == ino)
214                                         break;
215                         }
216
217                 /*
218                  * Check for length of the current name, preceding slash,
219                  * leading slash.
220                  */
221                 if (bpt - pt <= NAMLEN(dp) + (first ? 1 : 2)) {
222                         size_t len, off;
223                         char *npt;
224
225                         if (!ptsize) {
226                                 errno = ERANGE;
227                                 goto err;
228                         }
229                         off = bpt - pt;
230                         len = ept - bpt;
231                         if ((npt = realloc(pt, ptsize *= 2)) == NULL)
232                                 goto err;
233                         pt = npt;
234                         bpt = pt + off;
235                         ept = pt + ptsize;
236                         bcopy(bpt, ept - len, len);
237                         bpt = ept - len;
238                 }
239                 if (!first)
240                         *--bpt = '/';
241                 bpt -= NAMLEN(dp);
242                 bcopy(dp->d_name, bpt, NAMLEN(dp));
243                 (void)closedir(dir);
244
245                 /* Truncate any file name. */
246                 *bup = '\0';
247         }
248
249 notfound:
250         /*
251          * If readdir set errno, use it, not any saved error; otherwise,
252          * didn't find the current directory in its parent directory, set
253          * errno to ENOENT.
254          */
255         if (!errno)
256                 errno = save_errno ? save_errno : ENOENT;
257         /* FALLTHROUGH */
258 err:
259         if (ptsize)
260                 free(pt);
261         if (up)
262                 free(up);
263         if (dir)
264                 (void)closedir(dir);
265         return NULL;
266 }