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