121345Sdist /*
268218Sbostic * Copyright (c) 1989, 1991, 1993, 1995
361111Sbostic * The Regents of the University of California. All rights reserved.
437209Sbostic *
568223Spendry * This code is derived from software contributed to Berkeley by
668223Spendry * Jan-Simon Pendry.
768223Spendry *
842625Sbostic * %sccs.include.redist.c%
921345Sdist */
109986Ssam
1126565Sdonn #if defined(LIBC_SCCS) && !defined(lint)
12*68244Sbostic static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 02/07/95";
1337209Sbostic #endif /* LIBC_SCCS and not lint */
1421345Sdist
1510088Ssam #include <sys/param.h>
1610088Ssam #include <sys/stat.h>
1754722Sbostic
1868223Spendry #include <dirent.h>
1946483Sbostic #include <errno.h>
2068223Spendry #include <fcntl.h>
2146483Sbostic #include <stdio.h>
2246483Sbostic #include <stdlib.h>
2342396Sbostic #include <string.h>
2446597Sdonn #include <unistd.h>
259986Ssam
2668223Spendry static char *getcwd_physical __P((char *, size_t));
2768223Spendry
2842396Sbostic #define ISDOT(dp) \
2942396Sbostic (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
3042396Sbostic dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
3142396Sbostic
329986Ssam char *
getcwd(pt,size)3346483Sbostic getcwd(pt, size)
3446483Sbostic char *pt;
3546483Sbostic size_t size;
369986Ssam {
3768223Spendry char *pwd;
3868223Spendry size_t pwdlen;
3968223Spendry dev_t dev;
4068223Spendry ino_t ino;
4142396Sbostic struct stat s;
429986Ssam
4368218Sbostic /* Check $PWD -- if it's right, it's fast. */
4468219Sbostic if ((pwd = getenv("PWD")) != NULL && pwd[0] == '/' && !stat(pwd, &s)) {
4568218Sbostic dev = s.st_dev;
4668218Sbostic ino = s.st_ino;
4768218Sbostic if (!stat(".", &s) && dev == s.st_dev && ino == s.st_ino) {
4868218Sbostic pwdlen = strlen(pwd);
4968218Sbostic if (size != 0) {
5068218Sbostic if (pwdlen + 1 > size) {
5168218Sbostic errno = ERANGE;
5268218Sbostic return (NULL);
5368218Sbostic }
5468218Sbostic } else if ((pt = malloc(pwdlen + 1)) == NULL)
5568218Sbostic return (NULL);
5668218Sbostic memmove(pt, pwd, pwdlen);
57*68244Sbostic pt[pwdlen] = '\0';
5868218Sbostic return (pt);
5968218Sbostic }
6068218Sbostic }
6168218Sbostic
6268223Spendry return (getcwd_physical(pt, size));
6368223Spendry }
6468223Spendry
6568223Spendry /*
6668223Spendry * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
6768223Spendry *
6868223Spendry * Find the real name of path, by removing all ".", ".." and symlink
6968223Spendry * components. Returns (resolved) on success, or (NULL) on failure,
7068223Spendry * in which case the path which caused trouble is left in (resolved).
7168223Spendry */
7268223Spendry char *
realpath(path,resolved)7368223Spendry realpath(path, resolved)
7468223Spendry const char *path;
7568223Spendry char *resolved;
7668223Spendry {
7768223Spendry struct stat sb;
7868223Spendry int fd, n, rootd, serrno;
7968223Spendry char *p, *q, wbuf[MAXPATHLEN];
8068223Spendry
8168223Spendry /* Save the starting point. */
8268223Spendry if ((fd = open(".", O_RDONLY)) < 0) {
8368223Spendry (void)strcpy(resolved, ".");
8468223Spendry return (NULL);
8568223Spendry }
8668223Spendry
8746483Sbostic /*
8868223Spendry * Find the dirname and basename from the path to be resolved.
8968223Spendry * Change directory to the dirname component.
9068223Spendry * lstat the basename part.
9168223Spendry * if it is a symlink, read in the value and loop.
9268223Spendry * if it is a directory, then change to that directory.
9368223Spendry * get the current directory name and append the basename.
9468223Spendry */
9568223Spendry (void)strncpy(resolved, path, MAXPATHLEN - 1);
9668223Spendry resolved[MAXPATHLEN - 1] = '\0';
9768223Spendry loop:
9868223Spendry q = strrchr(resolved, '/');
9968223Spendry if (q != NULL) {
10068223Spendry p = q + 1;
10168223Spendry if (q == resolved)
10268223Spendry q = "/";
10368223Spendry else {
10468223Spendry do {
10568223Spendry --q;
10668223Spendry } while (q > resolved && *q == '/');
10768223Spendry q[1] = '\0';
10868223Spendry q = resolved;
10968223Spendry }
11068223Spendry if (chdir(q) < 0)
11168223Spendry goto err1;
11268223Spendry } else
11368223Spendry p = resolved;
11468223Spendry
11568223Spendry /* Deal with the last component. */
11668223Spendry if (lstat(p, &sb) == 0) {
11768223Spendry if (S_ISLNK(sb.st_mode)) {
11868223Spendry n = readlink(p, resolved, MAXPATHLEN);
11968223Spendry if (n < 0)
12068223Spendry goto err1;
12168223Spendry resolved[n] = '\0';
12268223Spendry goto loop;
12368223Spendry }
12468223Spendry if (S_ISDIR(sb.st_mode)) {
12568223Spendry if (chdir(p) < 0)
12668223Spendry goto err1;
12768223Spendry p = "";
12868223Spendry }
12968223Spendry }
13068223Spendry
13168223Spendry /*
13268223Spendry * Save the last component name and get the full pathname of
13368223Spendry * the current directory.
13468223Spendry */
13568223Spendry (void)strcpy(wbuf, p);
13668223Spendry
13768223Spendry /*
13868223Spendry * Call the inernal internal version of getcwd which
13968223Spendry * does a physical search rather than using the $PWD short-cut
14068223Spendry */
14168223Spendry if (getcwd_physical(resolved, MAXPATHLEN) == 0)
14268223Spendry goto err1;
14368223Spendry
14468223Spendry /*
14568223Spendry * Join the two strings together, ensuring that the right thing
14668223Spendry * happens if the last component is empty, or the dirname is root.
14768223Spendry */
14868223Spendry if (resolved[0] == '/' && resolved[1] == '\0')
14968223Spendry rootd = 1;
15068223Spendry else
15168223Spendry rootd = 0;
15268223Spendry
15368223Spendry if (*wbuf) {
15468223Spendry if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) {
15568223Spendry errno = ENAMETOOLONG;
15668223Spendry goto err1;
15768223Spendry }
15868223Spendry if (rootd == 0)
15968223Spendry (void)strcat(resolved, "/");
16068223Spendry (void)strcat(resolved, wbuf);
16168223Spendry }
16268223Spendry
16368223Spendry /* Go back to where we came from. */
16468223Spendry if (fchdir(fd) < 0) {
16568223Spendry serrno = errno;
16668223Spendry goto err2;
16768223Spendry }
16868223Spendry
16968223Spendry /* It's okay if the close fails, what's an fd more or less? */
17068223Spendry (void)close(fd);
17168223Spendry return (resolved);
17268223Spendry
17368223Spendry err1: serrno = errno;
17468223Spendry (void)fchdir(fd);
17568223Spendry err2: (void)close(fd);
17668223Spendry errno = serrno;
17768223Spendry return (NULL);
17868223Spendry }
17968223Spendry
18068223Spendry static char *
getcwd_physical(pt,size)18168223Spendry getcwd_physical(pt, size)
18268223Spendry char *pt;
18368223Spendry size_t size;
18468223Spendry {
18568223Spendry register struct dirent *dp;
18668223Spendry register DIR *dir;
18768223Spendry register dev_t dev;
18868223Spendry register ino_t ino;
18968223Spendry register int first;
19068223Spendry register char *bpt, *bup;
19168223Spendry struct stat s;
19268223Spendry dev_t root_dev;
19368223Spendry ino_t root_ino;
19468223Spendry size_t ptsize, upsize;
19568223Spendry int save_errno;
19668223Spendry char *ept, *eup, *up;
19768223Spendry
19868223Spendry /*
19946483Sbostic * If no buffer specified by the user, allocate one as necessary.
20046483Sbostic * If a buffer is specified, the size has to be non-zero. The path
20146483Sbostic * is built from the end of the buffer backwards.
20246483Sbostic */
20346483Sbostic if (pt) {
20446483Sbostic ptsize = 0;
20546483Sbostic if (!size) {
20646483Sbostic errno = EINVAL;
20754722Sbostic return (NULL);
20846483Sbostic }
20946483Sbostic ept = pt + size;
21046483Sbostic } else {
21154722Sbostic if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
21254722Sbostic return (NULL);
21346483Sbostic ept = pt + ptsize;
21446483Sbostic }
21546483Sbostic bpt = ept - 1;
21646645Sbostic *bpt = '\0';
21746483Sbostic
21846483Sbostic /*
21946483Sbostic * Allocate bytes (1024 - malloc space) for the string of "../"'s.
22046483Sbostic * Should always be enough (it's 340 levels). If it's not, allocate
22168218Sbostic * as necessary. Special case the first stat, it's ".", not "..".
22246483Sbostic */
22354722Sbostic if ((up = malloc(upsize = 1024 - 4)) == NULL)
22437209Sbostic goto err;
22546483Sbostic eup = up + MAXPATHLEN;
22646483Sbostic bup = up;
22746483Sbostic up[0] = '.';
22846483Sbostic up[1] = '\0';
22946483Sbostic
23046483Sbostic /* Save root values, so know when to stop. */
23146483Sbostic if (stat("/", &s))
23246483Sbostic goto err;
23337209Sbostic root_dev = s.st_dev;
23437209Sbostic root_ino = s.st_ino;
23542396Sbostic
23646483Sbostic errno = 0; /* XXX readdir has no error return. */
23742396Sbostic
23846483Sbostic for (first = 1;; first = 0) {
23946483Sbostic /* Stat the current level. */
24046483Sbostic if (lstat(up, &s))
24142396Sbostic goto err;
24242396Sbostic
24346483Sbostic /* Save current node values. */
24442396Sbostic ino = s.st_ino;
24542396Sbostic dev = s.st_dev;
24642396Sbostic
24746483Sbostic /* Check for reaching root. */
24842396Sbostic if (root_dev == dev && root_ino == ino) {
24946483Sbostic *--bpt = '/';
25046483Sbostic /*
25146483Sbostic * It's unclear that it's a requirement to copy the
25246483Sbostic * path to the beginning of the buffer, but it's always
25346483Sbostic * been that way and stuff would probably break.
25446483Sbostic */
25546483Sbostic (void)bcopy(bpt, pt, ept - bpt);
25646483Sbostic free(up);
25754722Sbostic return (pt);
25810155Ssam }
25942396Sbostic
26046483Sbostic /*
26146483Sbostic * Build pointer to the parent directory, allocating memory
26246483Sbostic * as necessary. Max length is 3 for "../", the largest
26346483Sbostic * possible component name, plus a trailing NULL.
26446483Sbostic */
26546483Sbostic if (bup + 3 + MAXNAMLEN + 1 >= eup) {
26654722Sbostic if ((up = realloc(up, upsize *= 2)) == NULL)
26746483Sbostic goto err;
26854722Sbostic bup = up;
26946483Sbostic eup = up + upsize;
27046483Sbostic }
27146483Sbostic *bup++ = '.';
27246483Sbostic *bup++ = '.';
27346483Sbostic *bup = '\0';
27442396Sbostic
27546483Sbostic /* Open and stat parent directory. */
27646483Sbostic if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
27740556Skarels goto err;
27842396Sbostic
27946483Sbostic /* Add trailing slash for next directory. */
28046483Sbostic *bup++ = '/';
28142396Sbostic
28242396Sbostic /*
28346483Sbostic * If it's a mount point, have to stat each element because
28442396Sbostic * the inode number in the directory is for the entry in the
28542396Sbostic * parent directory, not the inode number of the mounted file.
28642396Sbostic */
28746483Sbostic save_errno = 0;
28842396Sbostic if (s.st_dev == dev) {
28946483Sbostic for (;;) {
29046483Sbostic if (!(dp = readdir(dir)))
29146483Sbostic goto notfound;
29242396Sbostic if (dp->d_fileno == ino)
29346483Sbostic break;
29446483Sbostic }
29546483Sbostic } else
29646483Sbostic for (;;) {
29746483Sbostic if (!(dp = readdir(dir)))
29846483Sbostic goto notfound;
29942396Sbostic if (ISDOT(dp))
30042396Sbostic continue;
30146483Sbostic bcopy(dp->d_name, bup, dp->d_namlen + 1);
30246483Sbostic
30346483Sbostic /* Save the first error for later. */
30442396Sbostic if (lstat(up, &s)) {
30546483Sbostic if (!save_errno)
30646483Sbostic save_errno = errno;
30742396Sbostic errno = 0;
30842396Sbostic continue;
30942396Sbostic }
31046483Sbostic if (s.st_dev == dev && s.st_ino == ino)
31142396Sbostic break;
31242396Sbostic }
31342396Sbostic
31446483Sbostic /*
31546483Sbostic * Check for length of the current name, preceding slash,
31646483Sbostic * leading slash.
31746483Sbostic */
31846483Sbostic if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) {
31946483Sbostic size_t len, off;
32042396Sbostic
32146483Sbostic if (!ptsize) {
32246483Sbostic errno = ERANGE;
32340556Skarels goto err;
32440556Skarels }
32546483Sbostic off = bpt - pt;
32646483Sbostic len = ept - bpt;
32754722Sbostic if ((pt = realloc(pt, ptsize *= 2)) == NULL)
32846483Sbostic goto err;
32946483Sbostic bpt = pt + off;
33046483Sbostic ept = pt + ptsize;
33146483Sbostic (void)bcopy(bpt, ept - len, len);
33246483Sbostic bpt = ept - len;
33340556Skarels }
33446483Sbostic if (!first)
33546483Sbostic *--bpt = '/';
33646483Sbostic bpt -= dp->d_namlen;
33746483Sbostic bcopy(dp->d_name, bpt, dp->d_namlen);
33846483Sbostic (void)closedir(dir);
33946483Sbostic
34046483Sbostic /* Truncate any file name. */
34146483Sbostic *bup = '\0';
3429986Ssam }
34346483Sbostic
34446483Sbostic notfound:
34546483Sbostic /*
34646483Sbostic * If readdir set errno, use it, not any saved error; otherwise,
34746483Sbostic * didn't find the current directory in its parent directory, set
34846483Sbostic * errno to ENOENT.
34946483Sbostic */
35046483Sbostic if (!errno)
35146483Sbostic errno = save_errno ? save_errno : ENOENT;
35246483Sbostic /* FALLTHROUGH */
35340556Skarels err:
35446483Sbostic if (ptsize)
35546483Sbostic free(pt);
35646483Sbostic free(up);
35754722Sbostic return (NULL);
3589986Ssam }
359