xref: /onnv-gate/usr/src/cmd/ssh/libopenbsd-compat/common/getcwd.c (revision 0:68f95e015346)
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