]> andersk Git - gssapi-openssh.git/blob - openssh/openbsd-compat/getcwd.c
31d1cfe934a165fd6ad55b0f3b6e1f4ec0a6273e
[gssapi-openssh.git] / openssh / openbsd-compat / 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 "includes.h"
31
32 #if !defined(HAVE_GETCWD)
33
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char rcsid[] = "$OpenBSD: getcwd.c,v 1.9 2003/06/11 21:03:10 deraadt Exp $";
36 #endif /* LIBC_SCCS and not lint */
37
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #include <errno.h>
41 #include <dirent.h>
42 #include <sys/dir.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include "includes.h"
47
48 #define ISDOT(dp) \
49         (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
50             (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
51
52 char *
53 getcwd(char *pt, size_t size)
54 {
55         register struct dirent *dp;
56         register DIR *dir = NULL;
57         register dev_t dev;
58         register ino_t ino;
59         register int first;
60         register char *bpt, *bup;
61         struct stat s;
62         dev_t root_dev;
63         ino_t root_ino;
64         size_t ptsize, upsize;
65         int save_errno;
66         char *ept, *eup, *up;
67
68         /*
69          * If no buffer specified by the user, allocate one as necessary.
70          * If a buffer is specified, the size has to be non-zero.  The path
71          * is built from the end of the buffer backwards.
72          */
73         if (pt) {
74                 ptsize = 0;
75                 if (!size) {
76                         errno = EINVAL;
77                         return (NULL);
78                 }
79                 ept = pt + size;
80         } else {
81                 if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
82                         return (NULL);
83                 ept = pt + ptsize;
84         }
85         bpt = ept - 1;
86         *bpt = '\0';
87
88         /*
89          * Allocate bytes (1024 - malloc space) for the string of "../"'s.
90          * Should always be enough (it's 340 levels).  If it's not, allocate
91          * as necessary.  Special * case the first stat, it's ".", not "..".
92          */
93         if ((up = malloc(upsize = 1024 - 4)) == NULL)
94                 goto err;
95         eup = up + MAXPATHLEN;
96         bup = up;
97         up[0] = '.';
98         up[1] = '\0';
99
100         /* Save root values, so know when to stop. */
101         if (stat("/", &s))
102                 goto err;
103         root_dev = s.st_dev;
104         root_ino = s.st_ino;
105
106         errno = 0;                      /* XXX readdir has no error return. */
107
108         for (first = 1;; first = 0) {
109                 /* Stat the current level. */
110                 if (lstat(up, &s))
111                         goto err;
112
113                 /* Save current node values. */
114                 ino = s.st_ino;
115                 dev = s.st_dev;
116
117                 /* Check for reaching root. */
118                 if (root_dev == dev && root_ino == ino) {
119                         *--bpt = '/';
120                         /*
121                          * It's unclear that it's a requirement to copy the
122                          * path to the beginning of the buffer, but it's always
123                          * been that way and stuff would probably break.
124                          */
125                         memmove(pt, bpt, ept - bpt);
126                         free(up);
127                         return (pt);
128                 }
129
130                 /*
131                  * Build pointer to the parent directory, allocating memory
132                  * as necessary.  Max length is 3 for "../", the largest
133                  * possible component name, plus a trailing NUL.
134                  */
135                 if (bup + 3  + MAXNAMLEN + 1 >= eup) {
136                         char *nup;
137
138                         if ((nup = realloc(up, upsize *= 2)) == NULL)
139                                 goto err;
140                         up = nup;
141                         bup = up;
142                         eup = up + upsize;
143                 }
144                 *bup++ = '.';
145                 *bup++ = '.';
146                 *bup = '\0';
147
148                 /* Open and stat parent directory. 
149                  * RACE?? - replaced fstat(dirfd(dir), &s) w/ lstat(up,&s) 
150                  */
151                 if (!(dir = opendir(up)) || lstat(up,&s))
152                         goto err;
153
154                 /* Add trailing slash for next directory. */
155                 *bup++ = '/';
156
157                 /*
158                  * If it's a mount point, have to stat each element because
159                  * the inode number in the directory is for the entry in the
160                  * parent directory, not the inode number of the mounted file.
161                  */
162                 save_errno = 0;
163                 if (s.st_dev == dev) {
164                         for (;;) {
165                                 if (!(dp = readdir(dir)))
166                                         goto notfound;
167                                 if (dp->d_fileno == ino)
168                                         break;
169                         }
170                 } else
171                         for (;;) {
172                                 if (!(dp = readdir(dir)))
173                                         goto notfound;
174                                 if (ISDOT(dp))
175                                         continue;
176                                 memmove(bup, dp->d_name, dp->d_namlen + 1);
177
178                                 /* Save the first error for later. */
179                                 if (lstat(up, &s)) {
180                                         if (!save_errno)
181                                                 save_errno = errno;
182                                         errno = 0;
183                                         continue;
184                                 }
185                                 if (s.st_dev == dev && s.st_ino == ino)
186                                         break;
187                         }
188
189                 /*
190                  * Check for length of the current name, preceding slash,
191                  * leading slash.
192                  */
193                 if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
194                         size_t len, off;
195                         char *npt;
196
197                         if (!ptsize) {
198                                 errno = ERANGE;
199                                 goto err;
200                         }
201                         off = bpt - pt;
202                         len = ept - bpt;
203                         if ((npt = realloc(pt, ptsize *= 2)) == NULL)
204                                 goto err;
205                         pt = npt;
206                         bpt = pt + off;
207                         ept = pt + ptsize;
208                         memmove(ept - len, bpt, len);
209                         bpt = ept - len;
210                 }
211                 if (!first)
212                         *--bpt = '/';
213                 bpt -= dp->d_namlen;
214                 memmove(bpt, dp->d_name, dp->d_namlen);
215                 (void)closedir(dir);
216
217                 /* Truncate any file name. */
218                 *bup = '\0';
219         }
220
221 notfound:
222         /*
223          * If readdir set errno, use it, not any saved error; otherwise,
224          * didn't find the current directory in its parent directory, set
225          * errno to ENOENT.
226          */
227         if (!errno)
228                 errno = save_errno ? save_errno : ENOENT;
229         /* FALLTHROUGH */
230 err:
231         if (ptsize)
232                 free(pt);
233         if (up)
234                 free(up);
235         if (dir)
236                 (void)closedir(dir);
237         return (NULL);
238 }
239
240 #endif /* !defined(HAVE_GETCWD) */
This page took 0.146743 seconds and 3 git commands to generate.