Commit | Line | Data |
---|---|---|
bb0cfa24 | 1 | /* |
e2462278 | 2 | * Copyright (c) 1989, 1991 The Regents of the University of California. |
8a4b7e23 KB |
3 | * All rights reserved. |
4 | * | |
269a7923 | 5 | * %sccs.include.redist.c% |
bb0cfa24 DF |
6 | */ |
7 | ||
2ce81398 | 8 | #if defined(LIBC_SCCS) && !defined(lint) |
dee07d30 | 9 | static char sccsid[] = "@(#)getcwd.c 5.11 (Berkeley) %G%"; |
8a4b7e23 | 10 | #endif /* LIBC_SCCS and not lint */ |
9b6e6036 | 11 | |
87dccba9 SL |
12 | #include <sys/param.h> |
13 | #include <sys/stat.h> | |
e2462278 | 14 | #include <errno.h> |
8a4b7e23 | 15 | #include <dirent.h> |
e2462278 KB |
16 | #include <stdio.h> |
17 | #include <stdlib.h> | |
36226ba7 | 18 | #include <string.h> |
c5980113 | 19 | #include <unistd.h> |
36226ba7 KB |
20 | |
21 | #define ISDOT(dp) \ | |
22 | (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ | |
23 | dp->d_name[1] == '.' && dp->d_name[2] == '\0')) | |
9b6e6036 SL |
24 | |
25 | char * | |
e2462278 KB |
26 | getcwd(pt, size) |
27 | char *pt; | |
28 | size_t size; | |
9b6e6036 | 29 | { |
8a4b7e23 | 30 | register struct dirent *dp; |
36226ba7 | 31 | register DIR *dir; |
e2462278 | 32 | register dev_t dev; |
36226ba7 | 33 | register ino_t ino; |
36226ba7 | 34 | register int first; |
e2462278 | 35 | register char *bpt, *bup; |
36226ba7 | 36 | struct stat s; |
e2462278 | 37 | dev_t root_dev; |
8a4b7e23 | 38 | ino_t root_ino; |
e2462278 KB |
39 | size_t ptsize, upsize; |
40 | int save_errno; | |
41 | char *ept, *eup, *up; | |
42 | ||
43 | /* | |
44 | * If no buffer specified by the user, allocate one as necessary. | |
45 | * If a buffer is specified, the size has to be non-zero. The path | |
46 | * is built from the end of the buffer backwards. | |
47 | */ | |
48 | if (pt) { | |
49 | ptsize = 0; | |
50 | if (!size) { | |
51 | errno = EINVAL; | |
52 | return((char *)NULL); | |
53 | } | |
54 | ept = pt + size; | |
55 | } else { | |
56 | if (!(pt = (char *)malloc(ptsize = 1024 - 4))) | |
57 | return((char *)NULL); | |
58 | ept = pt + ptsize; | |
59 | } | |
60 | bpt = ept - 1; | |
dee07d30 | 61 | *bpt = '\0'; |
e2462278 KB |
62 | |
63 | /* | |
64 | * Allocate bytes (1024 - malloc space) for the string of "../"'s. | |
65 | * Should always be enough (it's 340 levels). If it's not, allocate | |
66 | * as necessary. Special * case the first stat, it's ".", not "..". | |
67 | */ | |
68 | if (!(up = (char *)malloc(upsize = 1024 - 4))) | |
69 | goto err; | |
70 | eup = up + MAXPATHLEN; | |
71 | bup = up; | |
72 | up[0] = '.'; | |
73 | up[1] = '\0'; | |
9b6e6036 | 74 | |
e2462278 KB |
75 | /* Save root values, so know when to stop. */ |
76 | if (stat("/", &s)) | |
8a4b7e23 KB |
77 | goto err; |
78 | root_dev = s.st_dev; | |
79 | root_ino = s.st_ino; | |
36226ba7 | 80 | |
e2462278 | 81 | errno = 0; /* XXX readdir has no error return. */ |
36226ba7 | 82 | |
e2462278 KB |
83 | for (first = 1;; first = 0) { |
84 | /* Stat the current level. */ | |
85 | if (lstat(up, &s)) | |
36226ba7 | 86 | goto err; |
36226ba7 | 87 | |
e2462278 | 88 | /* Save current node values. */ |
36226ba7 KB |
89 | ino = s.st_ino; |
90 | dev = s.st_dev; | |
91 | ||
e2462278 | 92 | /* Check for reaching root. */ |
36226ba7 | 93 | if (root_dev == dev && root_ino == ino) { |
e2462278 KB |
94 | *--bpt = '/'; |
95 | /* | |
96 | * It's unclear that it's a requirement to copy the | |
97 | * path to the beginning of the buffer, but it's always | |
98 | * been that way and stuff would probably break. | |
99 | */ | |
100 | (void)bcopy(bpt, pt, ept - bpt); | |
101 | free(up); | |
102 | return(pt); | |
90432f68 | 103 | } |
36226ba7 | 104 | |
e2462278 KB |
105 | /* |
106 | * Build pointer to the parent directory, allocating memory | |
107 | * as necessary. Max length is 3 for "../", the largest | |
108 | * possible component name, plus a trailing NULL. | |
109 | */ | |
110 | if (bup + 3 + MAXNAMLEN + 1 >= eup) { | |
111 | if (!(up = (char *)realloc(up, upsize *= 2))) | |
112 | goto err; | |
113 | eup = up + upsize; | |
114 | } | |
115 | *bup++ = '.'; | |
116 | *bup++ = '.'; | |
117 | *bup = '\0'; | |
36226ba7 | 118 | |
e2462278 KB |
119 | /* Open and stat parent directory. */ |
120 | if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) | |
1f8e47f9 | 121 | goto err; |
36226ba7 | 122 | |
e2462278 KB |
123 | /* Add trailing slash for next directory. */ |
124 | *bup++ = '/'; | |
36226ba7 KB |
125 | |
126 | /* | |
e2462278 | 127 | * If it's a mount point, have to stat each element because |
36226ba7 KB |
128 | * the inode number in the directory is for the entry in the |
129 | * parent directory, not the inode number of the mounted file. | |
130 | */ | |
e2462278 | 131 | save_errno = 0; |
36226ba7 | 132 | if (s.st_dev == dev) { |
e2462278 KB |
133 | for (;;) { |
134 | if (!(dp = readdir(dir))) | |
135 | goto notfound; | |
36226ba7 | 136 | if (dp->d_fileno == ino) |
e2462278 KB |
137 | break; |
138 | } | |
139 | } else | |
140 | for (;;) { | |
141 | if (!(dp = readdir(dir))) | |
142 | goto notfound; | |
36226ba7 KB |
143 | if (ISDOT(dp)) |
144 | continue; | |
e2462278 KB |
145 | bcopy(dp->d_name, bup, dp->d_namlen + 1); |
146 | ||
147 | /* Save the first error for later. */ | |
36226ba7 | 148 | if (lstat(up, &s)) { |
e2462278 KB |
149 | if (!save_errno) |
150 | save_errno = errno; | |
36226ba7 KB |
151 | errno = 0; |
152 | continue; | |
153 | } | |
e2462278 | 154 | if (s.st_dev == dev && s.st_ino == ino) |
36226ba7 | 155 | break; |
90432f68 | 156 | } |
36226ba7 | 157 | |
e2462278 KB |
158 | /* |
159 | * Check for length of the current name, preceding slash, | |
160 | * leading slash. | |
161 | */ | |
162 | if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) { | |
163 | size_t len, off; | |
36226ba7 | 164 | |
e2462278 KB |
165 | if (!ptsize) { |
166 | errno = ERANGE; | |
1f8e47f9 MK |
167 | goto err; |
168 | } | |
e2462278 KB |
169 | off = bpt - pt; |
170 | len = ept - bpt; | |
171 | if (!(pt = (char *)realloc(pt, ptsize *= 2))) | |
172 | goto err; | |
173 | bpt = pt + off; | |
174 | ept = pt + ptsize; | |
175 | (void)bcopy(bpt, ept - len, len); | |
176 | bpt = ept - len; | |
1f8e47f9 | 177 | } |
e2462278 KB |
178 | if (!first) |
179 | *--bpt = '/'; | |
180 | bpt -= dp->d_namlen; | |
181 | bcopy(dp->d_name, bpt, dp->d_namlen); | |
182 | (void)closedir(dir); | |
183 | ||
184 | /* Truncate any file name. */ | |
185 | *bup = '\0'; | |
90432f68 | 186 | } |
e2462278 KB |
187 | |
188 | notfound: | |
189 | /* | |
190 | * If readdir set errno, use it, not any saved error; otherwise, | |
191 | * didn't find the current directory in its parent directory, set | |
192 | * errno to ENOENT. | |
193 | */ | |
194 | if (!errno) | |
195 | errno = save_errno ? save_errno : ENOENT; | |
196 | /* FALLTHROUGH */ | |
1f8e47f9 | 197 | err: |
e2462278 KB |
198 | if (ptsize) |
199 | free(pt); | |
200 | free(up); | |
201 | return((char *)NULL); | |
9b6e6036 | 202 | } |