Blame sysdeps/mach/hurd/getcwd.c

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