diff options
Diffstat (limited to 'REORG.TODO/sysdeps/mach/hurd/getcwd.c')
-rw-r--r-- | REORG.TODO/sysdeps/mach/hurd/getcwd.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/REORG.TODO/sysdeps/mach/hurd/getcwd.c b/REORG.TODO/sysdeps/mach/hurd/getcwd.c new file mode 100644 index 0000000000..9b371b041a --- /dev/null +++ b/REORG.TODO/sysdeps/mach/hurd/getcwd.c @@ -0,0 +1,320 @@ +/* Copyright (C) 1991-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <hurd.h> +#include <hurd/port.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> + + +/* Get the canonical absolute name of the given directory port, and put it + in SIZE bytes of BUF. Returns NULL if the directory couldn't be + determined or SIZE was too small. If successful, returns BUF. In GNU, + if BUF is NULL, an array is allocated with `malloc'; the array is SIZE + bytes long, unless SIZE <= 0, in which case it is as big as necessary. + If our root directory cannot be reached, the result will not begin with + a slash to indicate that it is relative to some unknown root directory. */ + +char * +_hurd_canonicalize_directory_name_internal (file_t thisdir, + char *buf, + size_t size) +{ + error_t err; + mach_port_t rootid, thisid, rootdevid, thisdevid; + ino64_t rootino, thisino; + char *file_name; + char *file_namep; + file_t parent; + char *dirbuf = NULL; + unsigned int dirbufsize = 0; + const size_t orig_size = size; + + inline void cleanup (void) + { + if (parent != thisdir) + __mach_port_deallocate (__mach_task_self (), parent); + + __mach_port_deallocate (__mach_task_self (), thisid); + __mach_port_deallocate (__mach_task_self (), thisdevid); + __mach_port_deallocate (__mach_task_self (), rootid); + + if (dirbuf != NULL) + __vm_deallocate (__mach_task_self (), + (vm_address_t) dirbuf, dirbufsize); + } + + + if (size <= 0) + { + if (buf != NULL) + { + errno = EINVAL; + return NULL; + } + + size = FILENAME_MAX * 4 + 1; /* Good starting guess. */ + } + + if (buf != NULL) + file_name = buf; + else + { + file_name = malloc (size); + if (file_name == NULL) + return NULL; + } + + file_namep = file_name + size; + *--file_namep = '\0'; + + /* Get a port to our root directory and get its identity. */ + + if (err = __USEPORT (CRDIR, __io_identity (port, + &rootid, &rootdevid, &rootino))) + return __hurd_fail (err), NULL; + __mach_port_deallocate (__mach_task_self (), rootdevid); + + /* Stat the port to the directory of interest. */ + + if (err = __io_identity (thisdir, &thisid, &thisdevid, &thisino)) + { + __mach_port_deallocate (__mach_task_self (), rootid); + return __hurd_fail (err), NULL; + } + + parent = thisdir; + while (thisid != rootid) + { + /* PARENT is a port to the directory we are currently on; + THISID, THISDEV, and THISINO are its identity. + Look in its parent (..) for a file with the same file number. */ + + struct dirent64 *d; + mach_port_t dotid, dotdevid; + ino64_t dotino; + int mount_point; + file_t newp; + char *dirdata; + size_t dirdatasize; + int direntry, nentries; + + + /* Look at the parent directory. */ + newp = __file_name_lookup_under (parent, "..", O_READ, 0); + if (newp == MACH_PORT_NULL) + goto lose; + if (parent != thisdir) + __mach_port_deallocate (__mach_task_self (), parent); + parent = newp; + + /* Get this directory's identity and figure out if it's a mount + point. */ + if (err = __io_identity (parent, &dotid, &dotdevid, &dotino)) + goto errlose; + mount_point = dotdevid != thisdevid; + + if (thisid == dotid) + { + /* `..' == `.' but it is not our root directory. */ + __mach_port_deallocate (__mach_task_self (), dotid); + __mach_port_deallocate (__mach_task_self (), dotdevid); + break; + } + + /* Search for the last directory. */ + direntry = 0; + dirdata = dirbuf; + dirdatasize = dirbufsize; + while (!(err = __dir_readdir (parent, &dirdata, &dirdatasize, + direntry, -1, 0, &nentries)) && + nentries != 0) + { + /* We have a block of directory entries. */ + + unsigned int offset; + + direntry += nentries; + + if (dirdata != dirbuf) + { + /* The data was passed out of line, so our old buffer is no + longer useful. Deallocate the old buffer and reset our + information for the new buffer. */ + __vm_deallocate (__mach_task_self (), + (vm_address_t) dirbuf, dirbufsize); + dirbuf = dirdata; + dirbufsize = round_page (dirdatasize); + } + + /* Iterate over the returned directory entries, looking for one + whose file number is THISINO. */ + + offset = 0; + while (offset < dirdatasize) + { + d = (struct dirent64 *) &dirdata[offset]; + offset += d->d_reclen; + + /* Ignore `.' and `..'. */ + if (d->d_name[0] == '.' && + (d->d_namlen == 1 || + (d->d_namlen == 2 && d->d_name[1] == '.'))) + continue; + + if (mount_point || d->d_ino == thisino) + { + file_t try = __file_name_lookup_under (parent, d->d_name, + O_NOLINK, 0); + file_t id, devid; + ino64_t fileno; + if (try == MACH_PORT_NULL) + goto lose; + err = __io_identity (try, &id, &devid, &fileno); + __mach_port_deallocate (__mach_task_self (), try); + if (err) + goto inner_errlose; + __mach_port_deallocate (__mach_task_self (), id); + __mach_port_deallocate (__mach_task_self (), devid); + if (id == thisid) + goto found; + } + } + } + + if (err) + { + inner_errlose: /* Goto ERRLOSE: after cleaning up. */ + __mach_port_deallocate (__mach_task_self (), dotid); + __mach_port_deallocate (__mach_task_self (), dotdevid); + goto errlose; + } + else if (nentries == 0) + { + /* We got to the end of the directory without finding anything! + We are in a directory that has been unlinked, or something is + broken. */ + err = ENOENT; + goto inner_errlose; + } + else + found: + { + /* Prepend the directory name just discovered. */ + + if (file_namep - file_name < d->d_namlen + 1) + { + if (orig_size > 0) + { + errno = ERANGE; + return NULL; + } + else + { + size *= 2; + buf = realloc (file_name, size); + if (buf == NULL) + { + free (file_name); + return NULL; + } + file_namep = &buf[file_namep - file_name + size / 2]; + file_name = buf; + /* Move current contents up to the end of the buffer. + This is guaranteed to be non-overlapping. */ + memcpy (file_namep, file_namep - size / 2, + file_name + size - file_namep); + } + } + file_namep -= d->d_namlen; + (void) memcpy (file_namep, d->d_name, d->d_namlen); + *--file_namep = '/'; + } + + /* The next iteration will find the name of the directory we + just searched through. */ + __mach_port_deallocate (__mach_task_self (), thisid); + __mach_port_deallocate (__mach_task_self (), thisdevid); + thisid = dotid; + thisdevid = dotdevid; + thisino = dotino; + } + + if (file_namep == &file_name[size - 1]) + /* We found nothing and got all the way to the root. + So the root is our current directory. */ + *--file_namep = '/'; + + if (thisid != rootid) + /* We did not get to our root directory. The returned name should + not begin with a slash. */ + ++file_namep; + + memmove (file_name, file_namep, file_name + size - file_namep); + cleanup (); + return file_name; + + errlose: + /* Set errno. */ + (void) __hurd_fail (err); + lose: + cleanup (); + return NULL; +} + +char * +__canonicalize_directory_name_internal (const char *thisdir, char *buf, + size_t size) +{ + char *result; + file_t port = __file_name_lookup (thisdir, 0, 0); + if (port == MACH_PORT_NULL) + return NULL; + result = _hurd_canonicalize_directory_name_internal (port, buf, size); + __mach_port_deallocate (__mach_task_self (), port); + return result; +} + +/* Get the pathname of the current working directory, and put it in SIZE + bytes of BUF. Returns NULL if the directory couldn't be determined or + SIZE was too small. If successful, returns BUF. In GNU, if BUF is + NULL, an array is allocated with `malloc'; the array is SIZE bytes long, + unless SIZE <= 0, in which case it is as big as necessary. */ +char * +__getcwd (char *buf, size_t size) +{ + char *cwd = + __USEPORT (CWDIR, + _hurd_canonicalize_directory_name_internal (port, + buf, size)); + if (cwd && cwd[0] != '/') + { + /* `cwd' is an unknown root directory. */ + if (buf == NULL) + free (cwd); + return __hurd_fail (EGRATUITOUS), NULL; + } + return cwd; +} +weak_alias (__getcwd, getcwd) |