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