]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/mach/hurd/getcwd.c
Wed May 22 21:21:15 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
[thirdparty/glibc.git] / sysdeps / mach / hurd / getcwd.c
CommitLineData
b20e47cb 1/* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
28f540f4
RM
2This file is part of the GNU C Library.
3
4The GNU C Library is free software; you can redistribute it and/or
5modify it under the terms of the GNU Library General Public License as
6published by the Free Software Foundation; either version 2 of the
7License, or (at your option) any later version.
8
9The GNU C Library is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12Library General Public License for more details.
13
14You should have received a copy of the GNU Library General Public
15License along with the GNU C Library; see the file COPYING.LIB. If
16not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17Cambridge, MA 02139, USA. */
18
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>
30
31
32/* Get the pathname of the current working directory, and put it in SIZE
33 bytes of BUF. Returns NULL if the directory couldn't be determined or
34 SIZE was too small. If successful, returns BUF. In GNU, if BUF is
35 NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
36 unless SIZE <= 0, in which case it is as big as necessary. */
37
38char *
3ec41e03 39__getcwd (char *buf, size_t size)
28f540f4
RM
40{
41 error_t err;
0e3426bb 42 mach_port_t rootid, thisid, rootdevid, thisdevid;
28f540f4
RM
43 ino_t rootino, thisino;
44 char *file_name;
45 register char *file_namep;
28f540f4
RM
46 file_t parent;
47 char *dirbuf = NULL;
48 unsigned int dirbufsize = 0;
28f540f4
RM
49
50 inline void cleanup (void)
51 {
28f540f4 52 __mach_port_deallocate (__mach_task_self (), parent);
0e3426bb
RM
53 __mach_port_deallocate (__mach_task_self (), thisid);
54 __mach_port_deallocate (__mach_task_self (), thisdevid);
55 __mach_port_deallocate (__mach_task_self (), rootid);
56 __mach_port_deallocate (__mach_task_self (), rootdevid);
28f540f4
RM
57
58 if (dirbuf != NULL)
59 __vm_deallocate (__mach_task_self (),
60 (vm_address_t) dirbuf, dirbufsize);
61 }
62
b20e47cb 63
28f540f4
RM
64 if (size == 0)
65 {
66 if (buf != NULL)
67 {
68 errno = EINVAL;
69 return NULL;
70 }
71
72 size = FILENAME_MAX * 4 + 1; /* Good starting guess. */
73 }
74
75 if (buf != NULL)
76 file_name = buf;
77 else
78 {
79 file_name = malloc (size);
80 if (file_name == NULL)
81 return NULL;
82 }
83
84 file_namep = file_name + size;
85 *--file_namep = '\0';
86
0e3426bb 87 /* Get a port to our root directory and get its identity. */
28f540f4 88
0e3426bb
RM
89 if (err = __USEPORT (CRDIR, __io_identity (port,
90 &rootid, &rootdevid, &rootino)))
924b9ff6 91 return __hurd_fail (err), NULL;
0e3426bb 92 __mach_port_deallocate (__mach_task_self (), rootdevid);
28f540f4
RM
93
94 /* Get a port to our current working directory and stat it. */
95
0e3426bb
RM
96 if (err = __USEPORT (CRDIR, __io_identity (port,
97 &thisid, &thisdevid, &thisino)))
28f540f4 98 {
0e3426bb 99 __mach_port_deallocate (__mach_task_self (), rootid);
28f540f4
RM
100 return __hurd_fail (err), NULL;
101 }
102
0e3426bb 103 while (thisid != rootid)
28f540f4
RM
104 {
105 /* PARENT is a port to the directory we are currently on;
0e3426bb
RM
106 THISID, THISDEV, and THISINO are its identity.
107 Look in its parent (..) for a file with the same file number. */
28f540f4
RM
108
109 struct dirent *d;
0e3426bb 110 mach_port_t dotid, dotdevid;
28f540f4
RM
111 ino_t dotino;
112 int mount_point;
113 file_t newp;
114 char *dirdata;
115 unsigned int dirdatasize;
116 int direntry, nentries;
117
072d1145 118
28f540f4 119 /* Look at the parent directory. */
924b9ff6
RM
120 newp = __file_name_lookup_under (parent, "..", O_READ, 0);
121 if (newp == MACH_PORT_NULL)
28f540f4
RM
122 goto lose;
123 __mach_port_deallocate (__mach_task_self (), parent);
124 parent = newp;
125
0e3426bb
RM
126 /* Get this directory's identity and figure out if it's a mount point. */
127 if (err = __io_identity (parent, &dotid, &dotdevid, &dotino))
924b9ff6 128 goto errlose;
0e3426bb
RM
129 __mach_port_deallocate (__mach_task_self (), dotid);
130 __mach_port_deallocate (__mach_task_self (), dotdevid);
131 mount_point = dotdevid != thisdevid;
28f540f4
RM
132
133 /* Search for the last directory. */
134 direntry = 0;
135 dirdata = dirbuf;
136 dirdatasize = dirbufsize;
137 while (!(err = __dir_readdir (parent, &dirdata, &dirdatasize,
138 direntry, -1, 0, &nentries)) &&
b20e47cb 139 nentries != 0)
28f540f4
RM
140 {
141 /* We have a block of directory entries. */
142
143 unsigned int offset;
144
145 direntry += nentries;
146
147 if (dirdata != dirbuf)
148 {
149 /* The data was passed out of line, so our old buffer is no
150 longer useful. Deallocate the old buffer and reset our
151 information for the new buffer. */
152 __vm_deallocate (__mach_task_self (),
153 (vm_address_t) dirbuf, dirbufsize);
154 dirbuf = dirdata;
155 dirbufsize = round_page (dirdatasize);
156 }
157
158 /* Iterate over the returned directory entries, looking for one
159 whose file number is THISINO. */
160
161 offset = 0;
162 while (offset < dirdatasize)
163 {
164 d = (struct dirent *) &dirdata[offset];
165 offset += d->d_reclen;
166
b20e47cb 167 /* Ignore `.' and `..'. */
28f540f4
RM
168 if (d->d_name[0] == '.' &&
169 (d->d_namlen == 1 ||
170 (d->d_namlen == 2 && d->d_name[1] == '.')))
171 continue;
172
173 if (mount_point || d->d_ino == thisino)
174 {
924b9ff6
RM
175 file_t try = __file_name_lookup_under (parent, d->d_name,
176 O_NOLINK, 0);
0e3426bb
RM
177 file_t id, devid;
178 ino_t fileno;
924b9ff6 179 if (try == MACH_PORT_NULL)
28f540f4 180 goto lose;
0e3426bb 181 err = __io_identity (try, &id, &devid, &fileno);
28f540f4
RM
182 __mach_port_deallocate (__mach_task_self (), try);
183 if (err)
924b9ff6 184 goto errlose;
0e3426bb
RM
185 __mach_port_deallocate (__mach_task_self (), id);
186 __mach_port_deallocate (__mach_task_self (), devid);
187 if (id == thisid)
f5936b69 188 goto found;
28f540f4
RM
189 }
190 }
191 }
192
193 if (err)
924b9ff6 194 goto errlose;
b20e47cb
RM
195 else if (nentries == 0)
196 {
197 /* We got to the end of the directory without finding anything!
198 We are in a directory that has been unlinked, or something is
199 broken. */
200 err = ENOENT;
201 goto errlose;
202 }
28f540f4 203 else
f5936b69 204 found:
28f540f4
RM
205 {
206 /* Prepend the directory name just discovered. */
207
208 if (file_namep - file_name < d->d_namlen + 1)
209 {
210 if (buf != NULL)
211 {
212 errno = ERANGE;
213 return NULL;
214 }
215 else
216 {
217 size *= 2;
218 buf = realloc (file_name, size);
219 if (buf == NULL)
220 {
221 free (file_name);
222 return NULL;
223 }
224 file_namep = &buf[file_namep - file_name];
225 file_name = buf;
226 }
227 }
228 file_namep -= d->d_namlen;
229 (void) memcpy (file_namep, d->d_name, d->d_namlen);
230 *--file_namep = '/';
231 }
232
233 /* The next iteration will find the name of the directory we
234 just searched through. */
0e3426bb
RM
235 __mach_port_deallocate (__mach_task_self (), thisid);
236 __mach_port_deallocate (__mach_task_self (), thisdevid);
237 thisid = dotid;
238 thisdevid = dotdevid;
28f540f4
RM
239 thisino = dotino;
240 }
241
242 if (file_namep == &file_name[size - 1])
243 /* We found nothing and got all the way to the root.
244 So the root is our current directory. */
245 *--file_namep = '/';
246
247 memmove (file_name, file_namep, file_name + size - file_namep);
248 cleanup ();
249 return file_name;
250
924b9ff6
RM
251 errlose:
252 /* Set errno. */
253 (void) __hurd_fail (err);
28f540f4
RM
254 lose:
255 cleanup ();
256 return NULL;
257}
3ec41e03 258weak_alias (__getcwd, getcwd)