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