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