Back to index

glibc  2.9
getcwd.c
Go to the documentation of this file.
00001 /* Copyright (C) 1991,92,93,94,95,96,97,98,2002,04 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003 
00004    The GNU C Library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Lesser General Public
00006    License as published by the Free Software Foundation; either
00007    version 2.1 of the License, or (at your option) any later version.
00008 
00009    The GNU C Library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Lesser General Public License for more details.
00013 
00014    You should have received a copy of the GNU Lesser General Public
00015    License along with the GNU C Library; if not, write to the Free
00016    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00017    02111-1307 USA.  */
00018 
00019 #include <errno.h>
00020 #include <sys/types.h>
00021 #include <sys/stat.h>
00022 #include <hurd.h>
00023 #include <hurd/port.h>
00024 #include <dirent.h>
00025 #include <unistd.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <stdio.h>
00029 #include <fcntl.h>
00030 
00031 
00032 /* Get the canonical absolute name of the given directory port, and put it
00033    in SIZE bytes of BUF.  Returns NULL if the directory couldn't be
00034    determined or SIZE was too small.  If successful, returns BUF.  In GNU,
00035    if BUF is NULL, an array is allocated with `malloc'; the array is SIZE
00036    bytes long, unless SIZE <= 0, in which case it is as big as necessary.
00037    If our root directory cannot be reached, the result will not begin with
00038    a slash to indicate that it is relative to some unknown root directory.  */
00039 
00040 char *
00041 _hurd_canonicalize_directory_name_internal (file_t thisdir,
00042                                        char *buf,
00043                                        size_t size)
00044 {
00045   error_t err;
00046   mach_port_t rootid, thisid, rootdevid, thisdevid;
00047   ino64_t rootino, thisino;
00048   char *file_name;
00049   register char *file_namep;
00050   file_t parent;
00051   char *dirbuf = NULL;
00052   unsigned int dirbufsize = 0;
00053   const size_t orig_size = size;
00054 
00055   inline void cleanup (void)
00056     {
00057       if (parent != thisdir)
00058        __mach_port_deallocate (__mach_task_self (), parent);
00059 
00060       __mach_port_deallocate (__mach_task_self (), thisid);
00061       __mach_port_deallocate (__mach_task_self (), thisdevid);
00062       __mach_port_deallocate (__mach_task_self (), rootid);
00063       __mach_port_deallocate (__mach_task_self (), rootdevid);
00064 
00065       if (dirbuf != NULL)
00066        __vm_deallocate (__mach_task_self (),
00067                       (vm_address_t) dirbuf, dirbufsize);
00068     }
00069 
00070 
00071   if (size <= 0)
00072     {
00073       if (buf != NULL)
00074        {
00075          errno = EINVAL;
00076          return NULL;
00077        }
00078 
00079       size = FILENAME_MAX * 4 + 1; /* Good starting guess.  */
00080     }
00081 
00082   if (buf != NULL)
00083     file_name = buf;
00084   else
00085     {
00086       file_name = malloc (size);
00087       if (file_name == NULL)
00088        return NULL;
00089     }
00090 
00091   file_namep = file_name + size;
00092   *--file_namep = '\0';
00093 
00094   /* Get a port to our root directory and get its identity.  */
00095 
00096   if (err = __USEPORT (CRDIR, __io_identity (port,
00097                                         &rootid, &rootdevid, &rootino)))
00098     return __hurd_fail (err), NULL;
00099   __mach_port_deallocate (__mach_task_self (), rootdevid);
00100 
00101   /* Stat the port to the directory of interest.  */
00102 
00103   if (err = __io_identity (thisdir, &thisid, &thisdevid, &thisino))
00104     {
00105       __mach_port_deallocate (__mach_task_self (), rootid);
00106       return __hurd_fail (err), NULL;
00107     }
00108 
00109   parent = thisdir;
00110   while (thisid != rootid)
00111     {
00112       /* PARENT is a port to the directory we are currently on;
00113         THISID, THISDEV, and THISINO are its identity.
00114         Look in its parent (..) for a file with the same file number.  */
00115 
00116       struct dirent64 *d;
00117       mach_port_t dotid, dotdevid;
00118       ino64_t dotino;
00119       int mount_point;
00120       file_t newp;
00121       char *dirdata;
00122       size_t dirdatasize;
00123       int direntry, nentries;
00124 
00125 
00126       /* Look at the parent directory.  */
00127       newp = __file_name_lookup_under (parent, "..", O_READ, 0);
00128       if (newp == MACH_PORT_NULL)
00129        goto lose;
00130       if (parent != thisdir)
00131        __mach_port_deallocate (__mach_task_self (), parent);
00132       parent = newp;
00133 
00134       /* Get this directory's identity and figure out if it's a mount
00135          point.  */
00136       if (err = __io_identity (parent, &dotid, &dotdevid, &dotino))
00137        goto errlose;
00138       mount_point = dotdevid != thisdevid;
00139 
00140       if (thisid == dotid)
00141        {
00142          /* `..' == `.' but it is not our root directory.  */
00143          __mach_port_deallocate (__mach_task_self (), dotid);
00144          __mach_port_deallocate (__mach_task_self (), dotdevid);
00145          break;
00146        }
00147 
00148       /* Search for the last directory.  */
00149       direntry = 0;
00150       dirdata = dirbuf;
00151       dirdatasize = dirbufsize;
00152       while (!(err = __dir_readdir (parent, &dirdata, &dirdatasize,
00153                                 direntry, -1, 0, &nentries)) &&
00154             nentries != 0)
00155        {
00156          /* We have a block of directory entries.  */
00157 
00158          unsigned int offset;
00159 
00160          direntry += nentries;
00161 
00162          if (dirdata != dirbuf)
00163            {
00164              /* The data was passed out of line, so our old buffer is no
00165                longer useful.  Deallocate the old buffer and reset our
00166                information for the new buffer.  */
00167              __vm_deallocate (__mach_task_self (),
00168                             (vm_address_t) dirbuf, dirbufsize);
00169              dirbuf = dirdata;
00170              dirbufsize = round_page (dirdatasize);
00171            }
00172 
00173          /* Iterate over the returned directory entries, looking for one
00174             whose file number is THISINO.  */
00175 
00176          offset = 0;
00177          while (offset < dirdatasize)
00178            {
00179              d = (struct dirent64 *) &dirdata[offset];
00180              offset += d->d_reclen;
00181 
00182              /* Ignore `.' and `..'.  */
00183              if (d->d_name[0] == '.' &&
00184                 (d->d_namlen == 1 ||
00185                  (d->d_namlen == 2 && d->d_name[1] == '.')))
00186               continue;
00187 
00188              if (mount_point || d->d_ino == thisino)
00189               {
00190                 file_t try = __file_name_lookup_under (parent, d->d_name,
00191                                                   O_NOLINK, 0);
00192                 file_t id, devid;
00193                 ino64_t fileno;
00194                 if (try == MACH_PORT_NULL)
00195                   goto lose;
00196                 err = __io_identity (try, &id, &devid, &fileno);
00197                 __mach_port_deallocate (__mach_task_self (), try);
00198                 if (err)
00199                   goto inner_errlose;
00200                 __mach_port_deallocate (__mach_task_self (), id);
00201                 __mach_port_deallocate (__mach_task_self (), devid);
00202                 if (id == thisid)
00203                   goto found;
00204               }
00205            }
00206        }
00207 
00208       if (err)
00209        {
00210        inner_errlose:              /* Goto ERRLOSE: after cleaning up.  */
00211          __mach_port_deallocate (__mach_task_self (), dotid);
00212          __mach_port_deallocate (__mach_task_self (), dotdevid);
00213          goto errlose;
00214        }
00215       else if (nentries == 0)
00216        {
00217          /* We got to the end of the directory without finding anything!
00218             We are in a directory that has been unlinked, or something is
00219             broken.  */
00220          err = ENOENT;
00221          goto inner_errlose;
00222        }
00223       else
00224       found:
00225        {
00226          /* Prepend the directory name just discovered.  */
00227 
00228          if (file_namep - file_name < d->d_namlen + 1)
00229            {
00230              if (orig_size > 0)
00231               {
00232                 errno = ERANGE;
00233                 return NULL;
00234               }
00235              else
00236               {
00237                 size *= 2;
00238                 buf = realloc (file_name, size);
00239                 if (buf == NULL)
00240                   {
00241                     free (file_name);
00242                     return NULL;
00243                   }
00244                 file_namep = &buf[file_namep - file_name + size / 2];
00245                 file_name = buf;
00246                 /* Move current contents up to the end of the buffer.
00247                    This is guaranteed to be non-overlapping.  */
00248                 memcpy (file_namep, file_namep - size / 2,
00249                        file_name + size - file_namep);
00250               }
00251            }
00252          file_namep -= d->d_namlen;
00253          (void) memcpy (file_namep, d->d_name, d->d_namlen);
00254          *--file_namep = '/';
00255        }
00256 
00257       /* The next iteration will find the name of the directory we
00258         just searched through.  */
00259       __mach_port_deallocate (__mach_task_self (), thisid);
00260       __mach_port_deallocate (__mach_task_self (), thisdevid);
00261       thisid = dotid;
00262       thisdevid = dotdevid;
00263       thisino = dotino;
00264     }
00265 
00266   if (file_namep == &file_name[size - 1])
00267     /* We found nothing and got all the way to the root.
00268        So the root is our current directory.  */
00269     *--file_namep = '/';
00270 
00271   if (thisid != rootid)
00272     /* We did not get to our root directory. The returned name should
00273        not begin with a slash.  */
00274     ++file_namep;
00275 
00276   memmove (file_name, file_namep, file_name + size - file_namep);
00277   cleanup ();
00278   return file_name;
00279 
00280  errlose:
00281   /* Set errno.  */
00282   (void) __hurd_fail (err);
00283  lose:
00284   cleanup ();
00285   return NULL;
00286 }
00287 
00288 char *
00289 __canonicalize_directory_name_internal (thisdir, buf, size)
00290      const char *thisdir;
00291      char *buf;
00292      size_t size;
00293 {
00294   char *result;
00295   file_t port = __file_name_lookup (thisdir, 0, 0);
00296   if (port == MACH_PORT_NULL)
00297     return NULL;
00298   result = _hurd_canonicalize_directory_name_internal (port, buf, size);
00299   __mach_port_deallocate (__mach_task_self (), port);
00300   return result;
00301 }
00302 
00303 /* Get the pathname of the current working directory, and put it in SIZE
00304    bytes of BUF.  Returns NULL if the directory couldn't be determined or
00305    SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
00306    NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
00307    unless SIZE <= 0, in which case it is as big as necessary.  */
00308 char *
00309 __getcwd (char *buf, size_t size)
00310 {
00311   char *cwd =
00312     __USEPORT (CWDIR,
00313               _hurd_canonicalize_directory_name_internal (port,
00314                                                     buf, size));
00315   if (cwd && cwd[0] != '/')
00316     {
00317       /* `cwd' is an unknown root directory.  */
00318       if (buf == NULL)
00319          free (cwd);
00320       return __hurd_fail (EGRATUITOUS), NULL;
00321     }
00322   return cwd;
00323 }
00324 weak_alias (__getcwd, getcwd)