xref: /netbsd-src/lib/libc/gen/getcwd.c (revision b305b415b44bda69bdbe2146f64c1341217e8528)
1*b305b415Senami /*	$NetBSD: getcwd.c,v 1.53 2012/06/21 23:29:23 enami Exp $	*/
2b585e843Scgd 
361f28255Scgd /*
408b0946fSperry  * Copyright (c) 1989, 1991, 1993, 1995
5b585e843Scgd  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
708b0946fSperry  * This code is derived from software contributed to Berkeley by
808b0946fSperry  * Jan-Simon Pendry.
908b0946fSperry  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18eb7c1594Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35d177cac3Schristos #include <sys/cdefs.h>
3661f28255Scgd #if defined(LIBC_SCCS) && !defined(lint)
37b585e843Scgd #if 0
3808b0946fSperry static char sccsid[] = "@(#)getcwd.c	8.5 (Berkeley) 2/7/95";
39b585e843Scgd #else
40*b305b415Senami __RCSID("$NetBSD: getcwd.c,v 1.53 2012/06/21 23:29:23 enami Exp $");
41b585e843Scgd #endif
4261f28255Scgd #endif /* LIBC_SCCS and not lint */
4361f28255Scgd 
4443fa6fe3Sjtc #include "namespace.h"
4561f28255Scgd #include <sys/param.h>
4661f28255Scgd #include <sys/stat.h>
4708b0946fSperry 
48b48252f3Slukem #include <assert.h>
4908b0946fSperry #include <errno.h>
5061f28255Scgd #include <stdlib.h>
5161f28255Scgd #include <string.h>
5261f28255Scgd #include <unistd.h>
534bf46019Sjoerg #include <ssp/ssp.h>
5461f28255Scgd 
55bb8c6c86Ssommerfe #include "extern.h"
56bb8c6c86Ssommerfe 
5743fa6fe3Sjtc #ifdef __weak_alias
__weak_alias(getcwd,_getcwd)584bf46019Sjoerg __weak_alias(getcwd,_getcwd)
594bf46019Sjoerg __weak_alias(_sys_getcwd,_getcwd)
6060549036Smycroft __weak_alias(realpath,_realpath)
61f1f20d6aStron #endif
6243fa6fe3Sjtc 
6308b0946fSperry /*
6453094c09Schristos  * char *realpath(const char *path, char *resolved);
6508b0946fSperry  *
6608b0946fSperry  * Find the real name of path, by removing all ".", ".." and symlink
6708b0946fSperry  * components.  Returns (resolved) on success, or (NULL) on failure,
6808b0946fSperry  * in which case the path which caused trouble is left in (resolved).
6908b0946fSperry  */
7008b0946fSperry char *
7153094c09Schristos realpath(const char * __restrict path, char * __restrict resolved)
7208b0946fSperry {
7308b0946fSperry 	struct stat sb;
74c5e820caSchristos 	int idx = 0, nlnk = 0;
7522e3442eSenami 	const char *q;
7653094c09Schristos 	char *p, wbuf[2][MAXPATHLEN], *fres;
7722e3442eSenami 	size_t len;
78c5e820caSchristos 	ssize_t n;
7908b0946fSperry 
80c281fbb7Sdholland 	/* POSIX sez we must test for this */
81c281fbb7Sdholland 	if (path == NULL) {
82c281fbb7Sdholland 		errno = EINVAL;
83c281fbb7Sdholland 		return NULL;
84c281fbb7Sdholland 	}
85c281fbb7Sdholland 
8653094c09Schristos 	if (resolved == NULL) {
8753094c09Schristos 		fres = resolved = malloc(MAXPATHLEN);
8853094c09Schristos 		if (resolved == NULL)
8953094c09Schristos 			return NULL;
9053094c09Schristos 	} else
9153094c09Schristos 		fres = NULL;
9253094c09Schristos 
9353094c09Schristos 
9422e3442eSenami 	/*
9522e3442eSenami 	 * Build real path one by one with paying an attention to .,
9622e3442eSenami 	 * .. and symbolic link.
9722e3442eSenami 	 */
9822e3442eSenami 
9922e3442eSenami 	/*
10022e3442eSenami 	 * `p' is where we'll put a new component with prepending
10122e3442eSenami 	 * a delimiter.
10222e3442eSenami 	 */
10322e3442eSenami 	p = resolved;
10422e3442eSenami 
10553094c09Schristos 	if (*path == '\0') {
10653094c09Schristos 		*p = '\0';
10722e3442eSenami 		errno = ENOENT;
10853094c09Schristos 		goto out;
10908b0946fSperry 	}
11008b0946fSperry 
11122e3442eSenami 	/* If relative path, start from current working directory. */
11222e3442eSenami 	if (*path != '/') {
11314ebd4f3Schristos 		/* check for resolved pointer to appease coverity */
11414ebd4f3Schristos 		if (resolved && getcwd(resolved, MAXPATHLEN) == NULL) {
11522e3442eSenami 			p[0] = '.';
11653094c09Schristos 			p[1] = '\0';
11753094c09Schristos 			goto out;
1182e483120Sitojun 		}
11922e3442eSenami 		len = strlen(resolved);
12022e3442eSenami 		if (len > 1)
12122e3442eSenami 			p += len;
12208b0946fSperry 	}
12308b0946fSperry 
12422e3442eSenami loop:
12522e3442eSenami 	/* Skip any slash. */
12622e3442eSenami 	while (*path == '/')
12722e3442eSenami 		path++;
12822e3442eSenami 
12953094c09Schristos 	if (*path == '\0') {
13022e3442eSenami 		if (p == resolved)
13122e3442eSenami 			*p++ = '/';
13253094c09Schristos 		*p = '\0';
13353094c09Schristos 		return resolved;
13422e3442eSenami 	}
13522e3442eSenami 
13622e3442eSenami 	/* Find the end of this component. */
13722e3442eSenami 	q = path;
13822e3442eSenami 	do
13922e3442eSenami 		q++;
14053094c09Schristos 	while (*q != '/' && *q != '\0');
14122e3442eSenami 
14222e3442eSenami 	/* Test . or .. */
14322e3442eSenami 	if (path[0] == '.') {
14422e3442eSenami 		if (q - path == 1) {
14522e3442eSenami 			path = q;
14622e3442eSenami 			goto loop;
14722e3442eSenami 		}
14822e3442eSenami 		if (path[1] == '.' && q - path == 2) {
14922e3442eSenami 			/* Trim the last component. */
15022e3442eSenami 			if (p != resolved)
15122e3442eSenami 				while (*--p != '/')
15253094c09Schristos 					continue;
15322e3442eSenami 			path = q;
15422e3442eSenami 			goto loop;
15522e3442eSenami 		}
15622e3442eSenami 	}
15722e3442eSenami 
15822e3442eSenami 	/* Append this component. */
15922e3442eSenami 	if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) {
16022e3442eSenami 		errno = ENAMETOOLONG;
16122e3442eSenami 		if (p == resolved)
16222e3442eSenami 			*p++ = '/';
16353094c09Schristos 		*p = '\0';
16453094c09Schristos 		goto out;
16522e3442eSenami 	}
16622e3442eSenami 	p[0] = '/';
16722e3442eSenami 	memcpy(&p[1], path,
16822e3442eSenami 	    /* LINTED We know q > path. */
16922e3442eSenami 	    q - path);
17053094c09Schristos 	p[1 + q - path] = '\0';
17122e3442eSenami 
17222e3442eSenami 	/*
17322e3442eSenami 	 * If this component is a symlink, toss it and prepend link
17422e3442eSenami 	 * target to unresolved path.
17522e3442eSenami 	 */
17653094c09Schristos 	if (lstat(resolved, &sb) == -1)
17753094c09Schristos 		goto out;
17853094c09Schristos 
17908b0946fSperry 	if (S_ISLNK(sb.st_mode)) {
1800536609bSfvdl 		if (nlnk++ >= MAXSYMLINKS) {
1810536609bSfvdl 			errno = ELOOP;
18253094c09Schristos 			goto out;
1830536609bSfvdl 		}
18422e3442eSenami 		n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1);
18508b0946fSperry 		if (n < 0)
186*b305b415Senami 			goto out;
18722e3442eSenami 		if (n == 0) {
18822e3442eSenami 			errno = ENOENT;
18953094c09Schristos 			goto out;
19022e3442eSenami 		}
19122e3442eSenami 
19222e3442eSenami 		/* Append unresolved path to link target and switch to it. */
19322e3442eSenami 		if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) {
19422e3442eSenami 			errno = ENAMETOOLONG;
19553094c09Schristos 			goto out;
19622e3442eSenami 		}
19722e3442eSenami 		memcpy(&wbuf[idx][n], q, len + 1);
19822e3442eSenami 		path = wbuf[idx];
19922e3442eSenami 		idx ^= 1;
20022e3442eSenami 
20122e3442eSenami 		/* If absolute symlink, start from root. */
20222e3442eSenami 		if (*path == '/')
20322e3442eSenami 			p = resolved;
20408b0946fSperry 		goto loop;
20508b0946fSperry 	}
2068bf4305eSenami 	if (*q == '/' && !S_ISDIR(sb.st_mode)) {
2078bf4305eSenami 		errno = ENOTDIR;
20853094c09Schristos 		goto out;
2098bf4305eSenami 	}
21008b0946fSperry 
21122e3442eSenami 	/* Advance both resolved and unresolved path. */
21222e3442eSenami 	p += 1 + q - path;
21322e3442eSenami 	path = q;
21422e3442eSenami 	goto loop;
21553094c09Schristos out:
21653094c09Schristos 	free(fres);
21753094c09Schristos 	return NULL;
21808b0946fSperry }
21908b0946fSperry 
22027a01cc1Ssommerfe char *
__ssp_real(getcwd)2214bf46019Sjoerg __ssp_real(getcwd)(char *pt, size_t size)
22227a01cc1Ssommerfe {
223b2ed183eSenami 	char *npt;
22427a01cc1Ssommerfe 
22527a01cc1Ssommerfe 	/*
226b2ed183eSenami 	 * If a buffer is specified, the size has to be non-zero.
22727a01cc1Ssommerfe 	 */
228b2ed183eSenami 	if (pt != NULL) {
229b2ed183eSenami 		if (size == 0) {
230b2ed183eSenami 			/* __getcwd(pt, 0) results ERANGE. */
23127a01cc1Ssommerfe 			errno = EINVAL;
23227a01cc1Ssommerfe 			return (NULL);
23327a01cc1Ssommerfe 		}
234b2ed183eSenami 		if (__getcwd(pt, size) >= 0)
235b2ed183eSenami 			return (pt);
23627a01cc1Ssommerfe 		return (NULL);
23727a01cc1Ssommerfe 	}
238b2ed183eSenami 
239b2ed183eSenami 	/*
240b2ed183eSenami 	 * If no buffer specified by the user, allocate one as necessary.
241b2ed183eSenami 	 */
242b2ed183eSenami 	size = 1024 >> 1;
243b2ed183eSenami 	do {
244b2ed183eSenami 		if ((npt = realloc(pt, size <<= 1)) == NULL)
245c52c8cc0Schristos 			break;
246b2ed183eSenami 		pt = npt;
247b2ed183eSenami 		if (__getcwd(pt, size) >= 0)
248b2ed183eSenami 			return (pt);
249b2ed183eSenami 	} while (size <= MAXPATHLEN * 4 && errno == ERANGE);
250b2ed183eSenami 
251b2ed183eSenami 	free(pt);
252b2ed183eSenami 	return (NULL);
25327a01cc1Ssommerfe }
254