1240075Sdes/*	from OpenBSD: getcwd.c,v 1.14 2005/08/08 08:05:34 espie Exp */
298937Sdes/*
398937Sdes * Copyright (c) 1989, 1991, 1993
498937Sdes *	The Regents of the University of California.  All rights reserved.
598937Sdes *
698937Sdes * Redistribution and use in source and binary forms, with or without
798937Sdes * modification, are permitted provided that the following conditions
898937Sdes * are met:
998937Sdes * 1. Redistributions of source code must retain the above copyright
1098937Sdes *    notice, this list of conditions and the following disclaimer.
1198937Sdes * 2. Redistributions in binary form must reproduce the above copyright
1298937Sdes *    notice, this list of conditions and the following disclaimer in the
1398937Sdes *    documentation and/or other materials provided with the distribution.
14124208Sdes * 3. Neither the name of the University nor the names of its contributors
15124208Sdes *    may be used to endorse or promote products derived from this software
16124208Sdes *    without specific prior written permission.
1798937Sdes *
1898937Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1998937Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2098937Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2198937Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2298937Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2398937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2498937Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2598937Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2698937Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2798937Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2898937Sdes * SUCH DAMAGE.
2998937Sdes */
3098937Sdes
31157016Sdes/* OPENBSD ORIGINAL: lib/libc/gen/getcwd.c */
32157016Sdes
33106121Sdes#include "includes.h"
3498937Sdes
3598937Sdes#if !defined(HAVE_GETCWD)
3698937Sdes
3798937Sdes#include <sys/param.h>
3898937Sdes#include <sys/stat.h>
3998937Sdes#include <errno.h>
4098937Sdes#include <dirent.h>
4198937Sdes#include <sys/dir.h>
4298937Sdes#include <stdio.h>
4398937Sdes#include <stdlib.h>
4498937Sdes#include <string.h>
4598937Sdes#include "includes.h"
4698937Sdes
4798937Sdes#define	ISDOT(dp) \
4898937Sdes	(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
4998937Sdes	    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
5098937Sdes
5198937Sdeschar *
52124208Sdesgetcwd(char *pt, size_t size)
5398937Sdes{
54157016Sdes	struct dirent *dp;
55157016Sdes	DIR *dir = NULL;
56157016Sdes	dev_t dev;
57157016Sdes	ino_t ino;
58157016Sdes	int first;
59157016Sdes	char *bpt, *bup;
6098937Sdes	struct stat s;
6198937Sdes	dev_t root_dev;
6298937Sdes	ino_t root_ino;
6398937Sdes	size_t ptsize, upsize;
6498937Sdes	int save_errno;
6598937Sdes	char *ept, *eup, *up;
6698937Sdes
6798937Sdes	/*
6898937Sdes	 * If no buffer specified by the user, allocate one as necessary.
6998937Sdes	 * If a buffer is specified, the size has to be non-zero.  The path
7098937Sdes	 * is built from the end of the buffer backwards.
7198937Sdes	 */
7298937Sdes	if (pt) {
7398937Sdes		ptsize = 0;
7498937Sdes		if (!size) {
7598937Sdes			errno = EINVAL;
7698937Sdes			return (NULL);
7798937Sdes		}
7898937Sdes		ept = pt + size;
7998937Sdes	} else {
80157016Sdes		if ((pt = malloc(ptsize = MAXPATHLEN)) == NULL)
8198937Sdes			return (NULL);
8298937Sdes		ept = pt + ptsize;
8398937Sdes	}
8498937Sdes	bpt = ept - 1;
8598937Sdes	*bpt = '\0';
8698937Sdes
8798937Sdes	/*
88157016Sdes	 * Allocate bytes for the string of "../"'s.
8998937Sdes	 * Should always be enough (it's 340 levels).  If it's not, allocate
9098937Sdes	 * as necessary.  Special * case the first stat, it's ".", not "..".
9198937Sdes	 */
92157016Sdes	if ((up = malloc(upsize = MAXPATHLEN)) == NULL)
9398937Sdes		goto err;
94157016Sdes	eup = up + upsize;
9598937Sdes	bup = up;
9698937Sdes	up[0] = '.';
9798937Sdes	up[1] = '\0';
9898937Sdes
9998937Sdes	/* Save root values, so know when to stop. */
10098937Sdes	if (stat("/", &s))
10198937Sdes		goto err;
10298937Sdes	root_dev = s.st_dev;
10398937Sdes	root_ino = s.st_ino;
10498937Sdes
10598937Sdes	errno = 0;			/* XXX readdir has no error return. */
10698937Sdes
10798937Sdes	for (first = 1;; first = 0) {
10898937Sdes		/* Stat the current level. */
10998937Sdes		if (lstat(up, &s))
11098937Sdes			goto err;
11198937Sdes
11298937Sdes		/* Save current node values. */
11398937Sdes		ino = s.st_ino;
11498937Sdes		dev = s.st_dev;
11598937Sdes
11698937Sdes		/* Check for reaching root. */
11798937Sdes		if (root_dev == dev && root_ino == ino) {
11898937Sdes			*--bpt = '/';
11998937Sdes			/*
12098937Sdes			 * It's unclear that it's a requirement to copy the
12198937Sdes			 * path to the beginning of the buffer, but it's always
12298937Sdes			 * been that way and stuff would probably break.
12398937Sdes			 */
12498937Sdes			memmove(pt, bpt, ept - bpt);
12598937Sdes			free(up);
12698937Sdes			return (pt);
12798937Sdes		}
12898937Sdes
12998937Sdes		/*
13098937Sdes		 * Build pointer to the parent directory, allocating memory
13198937Sdes		 * as necessary.  Max length is 3 for "../", the largest
132113908Sdes		 * possible component name, plus a trailing NUL.
13398937Sdes		 */
13498937Sdes		if (bup + 3  + MAXNAMLEN + 1 >= eup) {
13598937Sdes			char *nup;
13698937Sdes
13798937Sdes			if ((nup = realloc(up, upsize *= 2)) == NULL)
13898937Sdes				goto err;
139157016Sdes			bup = nup + (bup - up);
14098937Sdes			up = nup;
14198937Sdes			eup = up + upsize;
14298937Sdes		}
14398937Sdes		*bup++ = '.';
14498937Sdes		*bup++ = '.';
14598937Sdes		*bup = '\0';
14698937Sdes
147157016Sdes		/* Open and stat parent directory. */
148157016Sdes		if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
14998937Sdes			goto err;
15098937Sdes
15198937Sdes		/* Add trailing slash for next directory. */
15298937Sdes		*bup++ = '/';
15398937Sdes
15498937Sdes		/*
15598937Sdes		 * If it's a mount point, have to stat each element because
15698937Sdes		 * the inode number in the directory is for the entry in the
15798937Sdes		 * parent directory, not the inode number of the mounted file.
15898937Sdes		 */
15998937Sdes		save_errno = 0;
16098937Sdes		if (s.st_dev == dev) {
16198937Sdes			for (;;) {
16298937Sdes				if (!(dp = readdir(dir)))
16398937Sdes					goto notfound;
16498937Sdes				if (dp->d_fileno == ino)
16598937Sdes					break;
16698937Sdes			}
16798937Sdes		} else
16898937Sdes			for (;;) {
16998937Sdes				if (!(dp = readdir(dir)))
17098937Sdes					goto notfound;
17198937Sdes				if (ISDOT(dp))
17298937Sdes					continue;
173157016Sdes				memcpy(bup, dp->d_name, dp->d_namlen + 1);
17498937Sdes
17598937Sdes				/* Save the first error for later. */
17698937Sdes				if (lstat(up, &s)) {
17798937Sdes					if (!save_errno)
17898937Sdes						save_errno = errno;
17998937Sdes					errno = 0;
18098937Sdes					continue;
18198937Sdes				}
18298937Sdes				if (s.st_dev == dev && s.st_ino == ino)
18398937Sdes					break;
18498937Sdes			}
18598937Sdes
18698937Sdes		/*
18798937Sdes		 * Check for length of the current name, preceding slash,
18898937Sdes		 * leading slash.
18998937Sdes		 */
19098937Sdes		if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
191157016Sdes			size_t len;
19298937Sdes			char *npt;
19398937Sdes
19498937Sdes			if (!ptsize) {
19598937Sdes				errno = ERANGE;
19698937Sdes				goto err;
19798937Sdes			}
19898937Sdes			len = ept - bpt;
19998937Sdes			if ((npt = realloc(pt, ptsize *= 2)) == NULL)
20098937Sdes				goto err;
201157016Sdes			bpt = npt + (bpt - pt);
20298937Sdes			pt = npt;
20398937Sdes			ept = pt + ptsize;
20498937Sdes			memmove(ept - len, bpt, len);
20598937Sdes			bpt = ept - len;
20698937Sdes		}
20798937Sdes		if (!first)
20898937Sdes			*--bpt = '/';
20998937Sdes		bpt -= dp->d_namlen;
210157016Sdes		memcpy(bpt, dp->d_name, dp->d_namlen);
21198937Sdes		(void)closedir(dir);
21298937Sdes
21398937Sdes		/* Truncate any file name. */
21498937Sdes		*bup = '\0';
21598937Sdes	}
21698937Sdes
21798937Sdesnotfound:
21898937Sdes	/*
21998937Sdes	 * If readdir set errno, use it, not any saved error; otherwise,
22098937Sdes	 * didn't find the current directory in its parent directory, set
22198937Sdes	 * errno to ENOENT.
22298937Sdes	 */
22398937Sdes	if (!errno)
22498937Sdes		errno = save_errno ? save_errno : ENOENT;
22598937Sdes	/* FALLTHROUGH */
22698937Sdeserr:
227157016Sdes	save_errno = errno;
228157016Sdes
22998937Sdes	if (ptsize)
23098937Sdes		free(pt);
231157016Sdes	free(up);
23298937Sdes	if (dir)
23398937Sdes		(void)closedir(dir);
234157016Sdes
235157016Sdes	errno = save_errno;
236157016Sdes
23798937Sdes	return (NULL);
23898937Sdes}
23998937Sdes
24098937Sdes#endif /* !defined(HAVE_GETCWD) */
241