1 /* Copyright (C) 1991-2014 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, see
16 <http://www.gnu.org/licenses/>. */
19 #include <sys/types.h>
22 #include <hurd/port.h>
31 /* Get the canonical absolute name of the given directory port, and put it
32 in SIZE bytes of BUF. Returns NULL if the directory couldn't be
33 determined or SIZE was too small. If successful, returns BUF. In GNU,
34 if BUF is NULL, an array is allocated with `malloc'; the array is SIZE
35 bytes long, unless SIZE <= 0, in which case it is as big as necessary.
36 If our root directory cannot be reached, the result will not begin with
37 a slash to indicate that it is relative to some unknown root directory. */
40 _hurd_canonicalize_directory_name_internal (file_t thisdir
,
45 mach_port_t rootid
, thisid
, rootdevid
, thisdevid
;
46 ino64_t rootino
, thisino
;
51 unsigned int dirbufsize
= 0;
52 const size_t orig_size
= size
;
54 inline void cleanup (void)
56 if (parent
!= thisdir
)
57 __mach_port_deallocate (__mach_task_self (), parent
);
59 __mach_port_deallocate (__mach_task_self (), thisid
);
60 __mach_port_deallocate (__mach_task_self (), thisdevid
);
61 __mach_port_deallocate (__mach_task_self (), rootid
);
64 __vm_deallocate (__mach_task_self (),
65 (vm_address_t
) dirbuf
, dirbufsize
);
77 size
= FILENAME_MAX
* 4 + 1; /* Good starting guess. */
84 file_name
= malloc (size
);
85 if (file_name
== NULL
)
89 file_namep
= file_name
+ size
;
92 /* Get a port to our root directory and get its identity. */
94 if (err
= __USEPORT (CRDIR
, __io_identity (port
,
95 &rootid
, &rootdevid
, &rootino
)))
96 return __hurd_fail (err
), NULL
;
97 __mach_port_deallocate (__mach_task_self (), rootdevid
);
99 /* Stat the port to the directory of interest. */
101 if (err
= __io_identity (thisdir
, &thisid
, &thisdevid
, &thisino
))
103 __mach_port_deallocate (__mach_task_self (), rootid
);
104 return __hurd_fail (err
), NULL
;
108 while (thisid
!= rootid
)
110 /* PARENT is a port to the directory we are currently on;
111 THISID, THISDEV, and THISINO are its identity.
112 Look in its parent (..) for a file with the same file number. */
115 mach_port_t dotid
, dotdevid
;
121 int direntry
, nentries
;
124 /* Look at the parent directory. */
125 newp
= __file_name_lookup_under (parent
, "..", O_READ
, 0);
126 if (newp
== MACH_PORT_NULL
)
128 if (parent
!= thisdir
)
129 __mach_port_deallocate (__mach_task_self (), parent
);
132 /* Get this directory's identity and figure out if it's a mount
134 if (err
= __io_identity (parent
, &dotid
, &dotdevid
, &dotino
))
136 mount_point
= dotdevid
!= thisdevid
;
140 /* `..' == `.' but it is not our root directory. */
141 __mach_port_deallocate (__mach_task_self (), dotid
);
142 __mach_port_deallocate (__mach_task_self (), dotdevid
);
146 /* Search for the last directory. */
149 dirdatasize
= dirbufsize
;
150 while (!(err
= __dir_readdir (parent
, &dirdata
, &dirdatasize
,
151 direntry
, -1, 0, &nentries
)) &&
154 /* We have a block of directory entries. */
158 direntry
+= nentries
;
160 if (dirdata
!= dirbuf
)
162 /* The data was passed out of line, so our old buffer is no
163 longer useful. Deallocate the old buffer and reset our
164 information for the new buffer. */
165 __vm_deallocate (__mach_task_self (),
166 (vm_address_t
) dirbuf
, dirbufsize
);
168 dirbufsize
= round_page (dirdatasize
);
171 /* Iterate over the returned directory entries, looking for one
172 whose file number is THISINO. */
175 while (offset
< dirdatasize
)
177 d
= (struct dirent64
*) &dirdata
[offset
];
178 offset
+= d
->d_reclen
;
180 /* Ignore `.' and `..'. */
181 if (d
->d_name
[0] == '.' &&
183 (d
->d_namlen
== 2 && d
->d_name
[1] == '.')))
186 if (mount_point
|| d
->d_ino
== thisino
)
188 file_t
try = __file_name_lookup_under (parent
, d
->d_name
,
192 if (try == MACH_PORT_NULL
)
194 err
= __io_identity (try, &id
, &devid
, &fileno
);
195 __mach_port_deallocate (__mach_task_self (), try);
198 __mach_port_deallocate (__mach_task_self (), id
);
199 __mach_port_deallocate (__mach_task_self (), devid
);
208 inner_errlose
: /* Goto ERRLOSE: after cleaning up. */
209 __mach_port_deallocate (__mach_task_self (), dotid
);
210 __mach_port_deallocate (__mach_task_self (), dotdevid
);
213 else if (nentries
== 0)
215 /* We got to the end of the directory without finding anything!
216 We are in a directory that has been unlinked, or something is
224 /* Prepend the directory name just discovered. */
226 if (file_namep
- file_name
< d
->d_namlen
+ 1)
236 buf
= realloc (file_name
, size
);
242 file_namep
= &buf
[file_namep
- file_name
+ size
/ 2];
244 /* Move current contents up to the end of the buffer.
245 This is guaranteed to be non-overlapping. */
246 memcpy (file_namep
, file_namep
- size
/ 2,
247 file_name
+ size
- file_namep
);
250 file_namep
-= d
->d_namlen
;
251 (void) memcpy (file_namep
, d
->d_name
, d
->d_namlen
);
255 /* The next iteration will find the name of the directory we
256 just searched through. */
257 __mach_port_deallocate (__mach_task_self (), thisid
);
258 __mach_port_deallocate (__mach_task_self (), thisdevid
);
260 thisdevid
= dotdevid
;
264 if (file_namep
== &file_name
[size
- 1])
265 /* We found nothing and got all the way to the root.
266 So the root is our current directory. */
269 if (thisid
!= rootid
)
270 /* We did not get to our root directory. The returned name should
271 not begin with a slash. */
274 memmove (file_name
, file_namep
, file_name
+ size
- file_namep
);
280 (void) __hurd_fail (err
);
287 __canonicalize_directory_name_internal (thisdir
, buf
, size
)
293 file_t port
= __file_name_lookup (thisdir
, 0, 0);
294 if (port
== MACH_PORT_NULL
)
296 result
= _hurd_canonicalize_directory_name_internal (port
, buf
, size
);
297 __mach_port_deallocate (__mach_task_self (), port
);
301 /* Get the pathname of the current working directory, and put it in SIZE
302 bytes of BUF. Returns NULL if the directory couldn't be determined or
303 SIZE was too small. If successful, returns BUF. In GNU, if BUF is
304 NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
305 unless SIZE <= 0, in which case it is as big as necessary. */
307 __getcwd (char *buf
, size_t size
)
311 _hurd_canonicalize_directory_name_internal (port
,
313 if (cwd
&& cwd
[0] != '/')
315 /* `cwd' is an unknown root directory. */
318 return __hurd_fail (EGRATUITOUS
), NULL
;
322 weak_alias (__getcwd
, getcwd
)