2.9
[glibc/nacl-glibc.git] / sysdeps / mach / hurd / getcwd.c
blob7e07e6b404985322c0852ca2618ce6081a2026f2
1 /* Copyright (C) 1991,92,93,94,95,96,97,98,2002,04 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307 USA. */
19 #include <errno.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <hurd.h>
23 #include <hurd/port.h>
24 #include <dirent.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <fcntl.h>
32 /* Get the canonical absolute name of the given directory port, and put it
33 in SIZE bytes of BUF. Returns NULL if the directory couldn't be
34 determined or SIZE was too small. If successful, returns BUF. In GNU,
35 if BUF is NULL, an array is allocated with `malloc'; the array is SIZE
36 bytes long, unless SIZE <= 0, in which case it is as big as necessary.
37 If our root directory cannot be reached, the result will not begin with
38 a slash to indicate that it is relative to some unknown root directory. */
40 char *
41 _hurd_canonicalize_directory_name_internal (file_t thisdir,
42 char *buf,
43 size_t size)
45 error_t err;
46 mach_port_t rootid, thisid, rootdevid, thisdevid;
47 ino64_t rootino, thisino;
48 char *file_name;
49 register char *file_namep;
50 file_t parent;
51 char *dirbuf = NULL;
52 unsigned int dirbufsize = 0;
53 const size_t orig_size = size;
55 inline void cleanup (void)
57 if (parent != thisdir)
58 __mach_port_deallocate (__mach_task_self (), parent);
60 __mach_port_deallocate (__mach_task_self (), thisid);
61 __mach_port_deallocate (__mach_task_self (), thisdevid);
62 __mach_port_deallocate (__mach_task_self (), rootid);
63 __mach_port_deallocate (__mach_task_self (), rootdevid);
65 if (dirbuf != NULL)
66 __vm_deallocate (__mach_task_self (),
67 (vm_address_t) dirbuf, dirbufsize);
71 if (size <= 0)
73 if (buf != NULL)
75 errno = EINVAL;
76 return NULL;
79 size = FILENAME_MAX * 4 + 1; /* Good starting guess. */
82 if (buf != NULL)
83 file_name = buf;
84 else
86 file_name = malloc (size);
87 if (file_name == NULL)
88 return NULL;
91 file_namep = file_name + size;
92 *--file_namep = '\0';
94 /* Get a port to our root directory and get its identity. */
96 if (err = __USEPORT (CRDIR, __io_identity (port,
97 &rootid, &rootdevid, &rootino)))
98 return __hurd_fail (err), NULL;
99 __mach_port_deallocate (__mach_task_self (), rootdevid);
101 /* Stat the port to the directory of interest. */
103 if (err = __io_identity (thisdir, &thisid, &thisdevid, &thisino))
105 __mach_port_deallocate (__mach_task_self (), rootid);
106 return __hurd_fail (err), NULL;
109 parent = thisdir;
110 while (thisid != rootid)
112 /* PARENT is a port to the directory we are currently on;
113 THISID, THISDEV, and THISINO are its identity.
114 Look in its parent (..) for a file with the same file number. */
116 struct dirent64 *d;
117 mach_port_t dotid, dotdevid;
118 ino64_t dotino;
119 int mount_point;
120 file_t newp;
121 char *dirdata;
122 size_t dirdatasize;
123 int direntry, nentries;
126 /* Look at the parent directory. */
127 newp = __file_name_lookup_under (parent, "..", O_READ, 0);
128 if (newp == MACH_PORT_NULL)
129 goto lose;
130 if (parent != thisdir)
131 __mach_port_deallocate (__mach_task_self (), parent);
132 parent = newp;
134 /* Get this directory's identity and figure out if it's a mount
135 point. */
136 if (err = __io_identity (parent, &dotid, &dotdevid, &dotino))
137 goto errlose;
138 mount_point = dotdevid != thisdevid;
140 if (thisid == dotid)
142 /* `..' == `.' but it is not our root directory. */
143 __mach_port_deallocate (__mach_task_self (), dotid);
144 __mach_port_deallocate (__mach_task_self (), dotdevid);
145 break;
148 /* Search for the last directory. */
149 direntry = 0;
150 dirdata = dirbuf;
151 dirdatasize = dirbufsize;
152 while (!(err = __dir_readdir (parent, &dirdata, &dirdatasize,
153 direntry, -1, 0, &nentries)) &&
154 nentries != 0)
156 /* We have a block of directory entries. */
158 unsigned int offset;
160 direntry += nentries;
162 if (dirdata != dirbuf)
164 /* The data was passed out of line, so our old buffer is no
165 longer useful. Deallocate the old buffer and reset our
166 information for the new buffer. */
167 __vm_deallocate (__mach_task_self (),
168 (vm_address_t) dirbuf, dirbufsize);
169 dirbuf = dirdata;
170 dirbufsize = round_page (dirdatasize);
173 /* Iterate over the returned directory entries, looking for one
174 whose file number is THISINO. */
176 offset = 0;
177 while (offset < dirdatasize)
179 d = (struct dirent64 *) &dirdata[offset];
180 offset += d->d_reclen;
182 /* Ignore `.' and `..'. */
183 if (d->d_name[0] == '.' &&
184 (d->d_namlen == 1 ||
185 (d->d_namlen == 2 && d->d_name[1] == '.')))
186 continue;
188 if (mount_point || d->d_ino == thisino)
190 file_t try = __file_name_lookup_under (parent, d->d_name,
191 O_NOLINK, 0);
192 file_t id, devid;
193 ino64_t fileno;
194 if (try == MACH_PORT_NULL)
195 goto lose;
196 err = __io_identity (try, &id, &devid, &fileno);
197 __mach_port_deallocate (__mach_task_self (), try);
198 if (err)
199 goto inner_errlose;
200 __mach_port_deallocate (__mach_task_self (), id);
201 __mach_port_deallocate (__mach_task_self (), devid);
202 if (id == thisid)
203 goto found;
208 if (err)
210 inner_errlose: /* Goto ERRLOSE: after cleaning up. */
211 __mach_port_deallocate (__mach_task_self (), dotid);
212 __mach_port_deallocate (__mach_task_self (), dotdevid);
213 goto errlose;
215 else if (nentries == 0)
217 /* We got to the end of the directory without finding anything!
218 We are in a directory that has been unlinked, or something is
219 broken. */
220 err = ENOENT;
221 goto inner_errlose;
223 else
224 found:
226 /* Prepend the directory name just discovered. */
228 if (file_namep - file_name < d->d_namlen + 1)
230 if (orig_size > 0)
232 errno = ERANGE;
233 return NULL;
235 else
237 size *= 2;
238 buf = realloc (file_name, size);
239 if (buf == NULL)
241 free (file_name);
242 return NULL;
244 file_namep = &buf[file_namep - file_name + size / 2];
245 file_name = buf;
246 /* Move current contents up to the end of the buffer.
247 This is guaranteed to be non-overlapping. */
248 memcpy (file_namep, file_namep - size / 2,
249 file_name + size - file_namep);
252 file_namep -= d->d_namlen;
253 (void) memcpy (file_namep, d->d_name, d->d_namlen);
254 *--file_namep = '/';
257 /* The next iteration will find the name of the directory we
258 just searched through. */
259 __mach_port_deallocate (__mach_task_self (), thisid);
260 __mach_port_deallocate (__mach_task_self (), thisdevid);
261 thisid = dotid;
262 thisdevid = dotdevid;
263 thisino = dotino;
266 if (file_namep == &file_name[size - 1])
267 /* We found nothing and got all the way to the root.
268 So the root is our current directory. */
269 *--file_namep = '/';
271 if (thisid != rootid)
272 /* We did not get to our root directory. The returned name should
273 not begin with a slash. */
274 ++file_namep;
276 memmove (file_name, file_namep, file_name + size - file_namep);
277 cleanup ();
278 return file_name;
280 errlose:
281 /* Set errno. */
282 (void) __hurd_fail (err);
283 lose:
284 cleanup ();
285 return NULL;
288 char *
289 __canonicalize_directory_name_internal (thisdir, buf, size)
290 const char *thisdir;
291 char *buf;
292 size_t size;
294 char *result;
295 file_t port = __file_name_lookup (thisdir, 0, 0);
296 if (port == MACH_PORT_NULL)
297 return NULL;
298 result = _hurd_canonicalize_directory_name_internal (port, buf, size);
299 __mach_port_deallocate (__mach_task_self (), port);
300 return result;
303 /* Get the pathname of the current working directory, and put it in SIZE
304 bytes of BUF. Returns NULL if the directory couldn't be determined or
305 SIZE was too small. If successful, returns BUF. In GNU, if BUF is
306 NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
307 unless SIZE <= 0, in which case it is as big as necessary. */
308 char *
309 __getcwd (char *buf, size_t size)
311 char *cwd =
312 __USEPORT (CWDIR,
313 _hurd_canonicalize_directory_name_internal (port,
314 buf, size));
315 if (cwd && cwd[0] != '/')
317 /* `cwd' is an unknown root directory. */
318 if (buf == NULL)
319 free (cwd);
320 return __hurd_fail (EGRATUITOUS), NULL;
322 return cwd;
324 weak_alias (__getcwd, getcwd)