Replace FSF snail mail address with URLs.
[glibc.git] / sysdeps / mach / hurd / getcwd.c
blobe4cea5c287d76fffedc2c8e5c9e9d8511b6b4ab3
1 /* Copyright (C) 1991,92,93,94,95,96,97,98,2002,2004,2009
2 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
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);
64 if (dirbuf != NULL)
65 __vm_deallocate (__mach_task_self (),
66 (vm_address_t) dirbuf, dirbufsize);
70 if (size <= 0)
72 if (buf != NULL)
74 errno = EINVAL;
75 return NULL;
78 size = FILENAME_MAX * 4 + 1; /* Good starting guess. */
81 if (buf != NULL)
82 file_name = buf;
83 else
85 file_name = malloc (size);
86 if (file_name == NULL)
87 return NULL;
90 file_namep = file_name + size;
91 *--file_namep = '\0';
93 /* Get a port to our root directory and get its identity. */
95 if (err = __USEPORT (CRDIR, __io_identity (port,
96 &rootid, &rootdevid, &rootino)))
97 return __hurd_fail (err), NULL;
98 __mach_port_deallocate (__mach_task_self (), rootdevid);
100 /* Stat the port to the directory of interest. */
102 if (err = __io_identity (thisdir, &thisid, &thisdevid, &thisino))
104 __mach_port_deallocate (__mach_task_self (), rootid);
105 return __hurd_fail (err), NULL;
108 parent = thisdir;
109 while (thisid != rootid)
111 /* PARENT is a port to the directory we are currently on;
112 THISID, THISDEV, and THISINO are its identity.
113 Look in its parent (..) for a file with the same file number. */
115 struct dirent64 *d;
116 mach_port_t dotid, dotdevid;
117 ino64_t dotino;
118 int mount_point;
119 file_t newp;
120 char *dirdata;
121 size_t dirdatasize;
122 int direntry, nentries;
125 /* Look at the parent directory. */
126 newp = __file_name_lookup_under (parent, "..", O_READ, 0);
127 if (newp == MACH_PORT_NULL)
128 goto lose;
129 if (parent != thisdir)
130 __mach_port_deallocate (__mach_task_self (), parent);
131 parent = newp;
133 /* Get this directory's identity and figure out if it's a mount
134 point. */
135 if (err = __io_identity (parent, &dotid, &dotdevid, &dotino))
136 goto errlose;
137 mount_point = dotdevid != thisdevid;
139 if (thisid == dotid)
141 /* `..' == `.' but it is not our root directory. */
142 __mach_port_deallocate (__mach_task_self (), dotid);
143 __mach_port_deallocate (__mach_task_self (), dotdevid);
144 break;
147 /* Search for the last directory. */
148 direntry = 0;
149 dirdata = dirbuf;
150 dirdatasize = dirbufsize;
151 while (!(err = __dir_readdir (parent, &dirdata, &dirdatasize,
152 direntry, -1, 0, &nentries)) &&
153 nentries != 0)
155 /* We have a block of directory entries. */
157 unsigned int offset;
159 direntry += nentries;
161 if (dirdata != dirbuf)
163 /* The data was passed out of line, so our old buffer is no
164 longer useful. Deallocate the old buffer and reset our
165 information for the new buffer. */
166 __vm_deallocate (__mach_task_self (),
167 (vm_address_t) dirbuf, dirbufsize);
168 dirbuf = dirdata;
169 dirbufsize = round_page (dirdatasize);
172 /* Iterate over the returned directory entries, looking for one
173 whose file number is THISINO. */
175 offset = 0;
176 while (offset < dirdatasize)
178 d = (struct dirent64 *) &dirdata[offset];
179 offset += d->d_reclen;
181 /* Ignore `.' and `..'. */
182 if (d->d_name[0] == '.' &&
183 (d->d_namlen == 1 ||
184 (d->d_namlen == 2 && d->d_name[1] == '.')))
185 continue;
187 if (mount_point || d->d_ino == thisino)
189 file_t try = __file_name_lookup_under (parent, d->d_name,
190 O_NOLINK, 0);
191 file_t id, devid;
192 ino64_t fileno;
193 if (try == MACH_PORT_NULL)
194 goto lose;
195 err = __io_identity (try, &id, &devid, &fileno);
196 __mach_port_deallocate (__mach_task_self (), try);
197 if (err)
198 goto inner_errlose;
199 __mach_port_deallocate (__mach_task_self (), id);
200 __mach_port_deallocate (__mach_task_self (), devid);
201 if (id == thisid)
202 goto found;
207 if (err)
209 inner_errlose: /* Goto ERRLOSE: after cleaning up. */
210 __mach_port_deallocate (__mach_task_self (), dotid);
211 __mach_port_deallocate (__mach_task_self (), dotdevid);
212 goto errlose;
214 else if (nentries == 0)
216 /* We got to the end of the directory without finding anything!
217 We are in a directory that has been unlinked, or something is
218 broken. */
219 err = ENOENT;
220 goto inner_errlose;
222 else
223 found:
225 /* Prepend the directory name just discovered. */
227 if (file_namep - file_name < d->d_namlen + 1)
229 if (orig_size > 0)
231 errno = ERANGE;
232 return NULL;
234 else
236 size *= 2;
237 buf = realloc (file_name, size);
238 if (buf == NULL)
240 free (file_name);
241 return NULL;
243 file_namep = &buf[file_namep - file_name + size / 2];
244 file_name = buf;
245 /* Move current contents up to the end of the buffer.
246 This is guaranteed to be non-overlapping. */
247 memcpy (file_namep, file_namep - size / 2,
248 file_name + size - file_namep);
251 file_namep -= d->d_namlen;
252 (void) memcpy (file_namep, d->d_name, d->d_namlen);
253 *--file_namep = '/';
256 /* The next iteration will find the name of the directory we
257 just searched through. */
258 __mach_port_deallocate (__mach_task_self (), thisid);
259 __mach_port_deallocate (__mach_task_self (), thisdevid);
260 thisid = dotid;
261 thisdevid = dotdevid;
262 thisino = dotino;
265 if (file_namep == &file_name[size - 1])
266 /* We found nothing and got all the way to the root.
267 So the root is our current directory. */
268 *--file_namep = '/';
270 if (thisid != rootid)
271 /* We did not get to our root directory. The returned name should
272 not begin with a slash. */
273 ++file_namep;
275 memmove (file_name, file_namep, file_name + size - file_namep);
276 cleanup ();
277 return file_name;
279 errlose:
280 /* Set errno. */
281 (void) __hurd_fail (err);
282 lose:
283 cleanup ();
284 return NULL;
287 char *
288 __canonicalize_directory_name_internal (thisdir, buf, size)
289 const char *thisdir;
290 char *buf;
291 size_t size;
293 char *result;
294 file_t port = __file_name_lookup (thisdir, 0, 0);
295 if (port == MACH_PORT_NULL)
296 return NULL;
297 result = _hurd_canonicalize_directory_name_internal (port, buf, size);
298 __mach_port_deallocate (__mach_task_self (), port);
299 return result;
302 /* Get the pathname of the current working directory, and put it in SIZE
303 bytes of BUF. Returns NULL if the directory couldn't be determined or
304 SIZE was too small. If successful, returns BUF. In GNU, if BUF is
305 NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
306 unless SIZE <= 0, in which case it is as big as necessary. */
307 char *
308 __getcwd (char *buf, size_t size)
310 char *cwd =
311 __USEPORT (CWDIR,
312 _hurd_canonicalize_directory_name_internal (port,
313 buf, size));
314 if (cwd && cwd[0] != '/')
316 /* `cwd' is an unknown root directory. */
317 if (buf == NULL)
318 free (cwd);
319 return __hurd_fail (EGRATUITOUS), NULL;
321 return cwd;
323 weak_alias (__getcwd, getcwd)