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