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