1 /* getcwd() - get the name of the current working directory.
2 * Author: Kees J. Bot
3 * 30 Apr 1989
4 */
5
6 #include <sys/cdefs.h>
7 #include "namespace.h"
8
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <errno.h>
12 #include <unistd.h>
13 #include <dirent.h>
14 #include <limits.h>
15 #include <string.h>
16
17 /* libc-private interface */
18 int __getcwd(char *, size_t);
19
addpath(const char * path,char ** ap,const char * entry)20 static int addpath(const char *path, char **ap, const char *entry)
21 /* Add the name of a directory entry at the front of the path being built.
22 * Note that the result always starts with a slash.
23 */
24 {
25 const char *e= entry;
26 char *p= *ap;
27
28 while (*e != 0) e++;
29
30 while (e > entry && p > path) *--p = *--e;
31
32 if (p == path) return -1;
33 *--p = '/';
34 *ap= p;
35 return 0;
36 }
37
recover(char * p)38 static int recover(char *p)
39 /* Undo all those chdir("..")'s that have been recorded by addpath. This
40 * has to be done entry by entry, because the whole pathname may be too long.
41 */
42 {
43 int e= errno, slash;
44 char *p0;
45
46 while (*p != 0) {
47 p0= ++p;
48
49 do p++; while (*p != 0 && *p != '/');
50 slash= *p; *p= 0;
51
52 if (chdir(p0) < 0) return -1;
53 *p= slash;
54 }
55 errno= e;
56 return 0;
57 }
58
__getcwd(char * path,size_t size)59 int __getcwd(char *path, size_t size)
60 {
61 struct stat above, current, tmp;
62 struct dirent *entry;
63 DIR *d;
64 char *p, *up;
65 const char *dotdot = "..";
66 int cycle;
67
68 if (path == NULL || size <= 1) { errno= EINVAL; return -1; }
69
70 p= path + size;
71 *--p = 0;
72
73 if (stat(".", ¤t) < 0) return -1;
74
75 while (1) {
76 if (stat(dotdot, &above) < 0) { recover(p); return -1; }
77
78 if (above.st_dev == current.st_dev
79 && above.st_ino == current.st_ino)
80 break; /* Root dir found */
81
82 if ((d= opendir(dotdot)) == NULL) { recover(p); return -1; }
83
84 /* Cycle is 0 for a simple inode nr search, or 1 for a search
85 * for inode *and* device nr.
86 */
87 cycle= above.st_dev == current.st_dev ? 0 : 1;
88
89 do {
90 char name[3 + NAME_MAX + 1];
91
92 tmp.st_ino= 0;
93 if ((entry= readdir(d)) == NULL) {
94 switch (++cycle) {
95 case 1:
96 rewinddir(d);
97 continue;
98 case 2:
99 closedir(d);
100 errno= ENOENT;
101 recover(p);
102 return -1;
103 }
104 }
105 if (strcmp(entry->d_name, ".") == 0) continue;
106 if (strcmp(entry->d_name, "..") == 0) continue;
107
108 switch (cycle) {
109 case 0:
110 /* Simple test on inode nr. */
111 if (entry->d_ino != current.st_ino) continue;
112 /*FALL THROUGH*/
113
114 case 1:
115 /* Current is mounted. */
116 strcpy(name, "../");
117 strcpy(name+3, entry->d_name);
118 if (stat(name, &tmp) < 0) continue;
119 break;
120 }
121 } while (tmp.st_ino != current.st_ino
122 || tmp.st_dev != current.st_dev);
123
124 up= p;
125 if (addpath(path, &up, entry->d_name) < 0) {
126 closedir(d);
127 errno = ERANGE;
128 recover(p);
129 return -1;
130 }
131 closedir(d);
132
133 if (chdir(dotdot) < 0) { recover(p); return -1; }
134 p= up;
135
136 current= above;
137 }
138 if (recover(p) < 0) return -1; /* Undo all those chdir("..")'s. */
139 if (*p == 0) *--p = '/'; /* Cwd is "/" if nothing added */
140 if (p > path) strcpy(path, p); /* Move string to start of path. */
141 return 0;
142 }
143