OSDN Git Service

* dtable.cc (build_fh_name_worker): Remove. Move all functionality
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / dir.cc
index df05152..641aabe 100644 (file)
@@ -1,6 +1,7 @@
 /* dir.cc: Posix directory-related routines
 
-   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2006, 2007,
+   2008, 2009, 2010 Red Hat, Inc.
 
 This file is part of Cygwin.
 
@@ -9,25 +10,25 @@ Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 details. */
 
 #include "winsup.h"
-#include <unistd.h>
 #include <stdlib.h>
-#include <sys/stat.h>
 
 #define _COMPILING_NEWLIB
 #include <dirent.h>
 
-#include "pinfo.h"
 #include "cygerrno.h"
 #include "security.h"
 #include "path.h"
 #include "fhandler.h"
 #include "dtable.h"
 #include "cygheap.h"
+#include "cygtls.h"
+#include "tls_pbuf.h"
 
 extern "C" int
 dirfd (DIR *dir)
 {
-  if (check_null_invalid_struct_errno (dir))
+  myfault efault;
+  if (efault.faulted (EFAULT))
     return -1;
   if (dir->__d_cookie != __DIRENT_COOKIE)
     {
@@ -35,7 +36,15 @@ dirfd (DIR *dir)
       syscall_printf ("-1 = dirfd (%p)", dir);
       return -1;
     }
-  return dir->__d_dirent->d_fd;
+  return dir->__d_fd;
+}
+
+/* Symbol kept for backward compatibility.  Don't remove.  Don't declare
+   in public header file. */
+extern "C" DIR *
+__opendir_with_d_ino (const char *name)
+{
+  return opendir (name);
 }
 
 /* opendir: POSIX 5.1.2.1 */
@@ -45,95 +54,135 @@ opendir (const char *name)
   fhandler_base *fh;
   DIR *res;
 
-  fh = build_fh_name (name, NULL, PC_SYM_FOLLOW);
+  fh = build_fh_name (name, PC_SYM_FOLLOW);
   if (!fh)
     res = NULL;
   else if (fh->exists ())
-      res = fh->opendir ();
+    res = fh->opendir (-1);
   else
     {
       set_errno (ENOENT);
       res = NULL;
     }
 
-  if (res)
-    res->__flags = 0;
-  else if (fh)
+  if (!res && fh)
     delete fh;
   return res;
 }
 
-/* readdir: POSIX 5.1.2.1 */
-extern "C" struct dirent *
-readdir (DIR *dir)
+extern "C" DIR *
+fdopendir (int fd)
 {
-  if (check_null_invalid_struct_errno (dir))
-    return NULL;
+  DIR *res = NULL;
+
+  cygheap_fdget cfd (fd);
+  if (cfd >= 0)
+    res = cfd->opendir (fd);
+  return res;
+}
+
+static int
+readdir_worker (DIR *dir, dirent *de)
+{
+  myfault efault;
+  if (efault.faulted ())
+    return EFAULT;
 
   if (dir->__d_cookie != __DIRENT_COOKIE)
     {
-      set_errno (EBADF);
       syscall_printf ("%p = readdir (%p)", NULL, dir);
-      return NULL;
+      return EBADF;
     }
 
-  dirent *res = ((fhandler_base *) dir->__fh)->readdir (dir);
+  de->d_ino = 0;
+  de->d_type = DT_UNKNOWN;
+  memset (&de->__d_unused1, 0, sizeof (de->__d_unused1));
 
-  if (!res)
+  int res = ((fhandler_base *) dir->__fh)->readdir (dir, de);
+
+  if (res == ENMFILE)
     {
       if (!(dir->__flags & dirent_saw_dot))
        {
-         res = dir->__d_dirent;
-         strcpy (res->d_name, ".");
+         strcpy (de->d_name, ".");
          dir->__flags |= dirent_saw_dot;
          dir->__d_position++;
+         res = 0;
        }
       else if (!(dir->__flags & dirent_saw_dot_dot))
        {
-         res = dir->__d_dirent;
-         strcpy (res->d_name, "..");
+         strcpy (de->d_name, "..");
          dir->__flags |= dirent_saw_dot_dot;
          dir->__d_position++;
+         res = 0;
        }
     }
 
-  if (res)
+  if (!res && !de->d_ino)
     {
-      /* Compute d_ino by combining filename hash with the directory hash
-        (which was stored in dir->__d_dirhash when opendir was called). */
-      if (res->d_name[0] == '.')
+      bool is_dot = false;
+      bool is_dot_dot = false;
+
+      if (de->d_name[0] == '.')
        {
-         if (res->d_name[1] == '\0')
-           {
-             dir->__d_dirent->d_ino = dir->__d_dirhash;
-             dir->__flags |= dirent_saw_dot;
-           }
-         else if (res->d_name[1] != '.' || res->d_name[2] != '\0')
-           goto hashit;
-         else
-           {
-             dir->__flags |= dirent_saw_dot_dot;
-             char *p, up[strlen (dir->__d_dirname) + 1];
-             strcpy (up, dir->__d_dirname);
-             if (!(p = strrchr (up, '\\')))
-               goto hashit;
-             *p = '\0';
-             if (!(p = strrchr (up, '\\')))
-               dir->__d_dirent->d_ino = hash_path_name (0, ".");
-             else
-               {
-                 *p = '\0';
-                 dir->__d_dirent->d_ino = hash_path_name (0, up);
-               }
-           }
+         if (de->d_name[1] == '\0')
+           is_dot = true;
+         else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
+           is_dot_dot = true;
        }
+
+      if (is_dot_dot && !(dir->__flags & dirent_isroot))
+       de->d_ino = readdir_get_ino (((fhandler_base *) dir->__fh)->get_name (),
+                                    true);
       else
        {
-      hashit:
-         __ino64_t dino = hash_path_name (dir->__d_dirhash, "\\");
-         dir->__d_dirent->d_ino = hash_path_name (dino, res->d_name);
+         /* Compute d_ino by combining filename hash with directory hash. */
+         de->d_ino = ((fhandler_base *) dir->__fh)->get_ino ();
+         if (!is_dot && !is_dot_dot)
+           {
+             PUNICODE_STRING w32name =
+                 ((fhandler_base *) dir->__fh)->pc.get_nt_native_path ();
+             DWORD devn = ((fhandler_base *) dir->__fh)->get_device ();
+             /* Paths below /proc don't have a Win32 pendant. */
+             if (isproc_dev (devn))
+               de->d_ino = hash_path_name (de->d_ino, L"/");
+             else if (w32name->Buffer[w32name->Length / sizeof (WCHAR) - 1]
+                      != L'\\')
+               de->d_ino = hash_path_name (de->d_ino, L"\\");
+             de->d_ino = hash_path_name (de->d_ino, de->d_name);
+           }
        }
-      res->__ino32 = dir->__d_dirent->d_ino;   // for legacy applications
+    }
+  /* This fills out the old 32 bit d_ino field for old applications,
+     build under Cygwin before 1.5.x. */
+  de->__d_internal1 = de->d_ino;
+
+  return res;
+}
+
+/* readdir: POSIX 5.1.2.1 */
+extern "C" struct dirent *
+readdir (DIR *dir)
+{
+  int res = readdir_worker (dir, dir->__d_dirent);
+  if (res == 0)
+    return dir->__d_dirent;
+  if (res != ENMFILE)
+    set_errno (res);
+  return NULL;
+}
+
+extern "C" int
+readdir_r (DIR *dir, dirent *de, dirent **ode)
+{
+  int res = readdir_worker (dir, de);
+  if (!res)
+    *ode = de;
+  else
+    {
+      *ode = NULL;
+      if (res == ENMFILE)
+       res = 0;
     }
   return res;
 }
@@ -141,7 +190,8 @@ readdir (DIR *dir)
 extern "C" _off64_t
 telldir64 (DIR *dir)
 {
-  if (check_null_invalid_struct_errno (dir))
+  myfault efault;
+  if (efault.faulted (EFAULT))
     return -1;
 
   if (dir->__d_cookie != __DIRENT_COOKIE)
@@ -159,12 +209,13 @@ telldir (DIR *dir)
 extern "C" void
 seekdir64 (DIR *dir, _off64_t loc)
 {
-  if (check_null_invalid_struct_errno (dir))
+  myfault efault;
+  if (efault.faulted (EFAULT))
     return;
 
   if (dir->__d_cookie != __DIRENT_COOKIE)
     return;
-  dir->__flags = 0;
+  dir->__flags &= dirent_info_mask;
   return ((fhandler_base *) dir->__fh)->seekdir (dir, loc);
 }
 
@@ -179,12 +230,13 @@ seekdir (DIR *dir, _off_t loc)
 extern "C" void
 rewinddir (DIR *dir)
 {
-  if (check_null_invalid_struct_errno (dir))
+  myfault efault;
+  if (efault.faulted (EFAULT))
     return;
 
   if (dir->__d_cookie != __DIRENT_COOKIE)
     return;
-  dir->__flags = 0;
+  dir->__flags &= dirent_info_mask;
   return ((fhandler_base *) dir->__fh)->rewinddir (dir);
 }
 
@@ -192,7 +244,8 @@ rewinddir (DIR *dir)
 extern "C" int
 closedir (DIR *dir)
 {
-  if (check_null_invalid_struct_errno (dir))
+  myfault efault;
+  if (efault.faulted (EFAULT))
     return -1;
 
   if (dir->__d_cookie != __DIRENT_COOKIE)
@@ -207,7 +260,7 @@ closedir (DIR *dir)
 
   int res = ((fhandler_base *) dir->__fh)->closedir (dir);
 
-  cygheap->fdtab.release (dir->__d_dirent->d_fd);
+  cygheap->fdtab.release (dir->__d_fd);
 
   free (dir->__d_dirname);
   free (dir->__d_dirent);
@@ -221,39 +274,46 @@ extern "C" int
 mkdir (const char *dir, mode_t mode)
 {
   int res = -1;
-  SECURITY_ATTRIBUTES sa = sec_none_nih;
-  security_descriptor sd;
+  fhandler_base *fh = NULL;
+  tmp_pathbuf tp;
+
+  myfault efault;
+  if (efault.faulted (EFAULT))
+    return -1;
 
-  path_conv real_dir (dir, PC_SYM_NOFOLLOW | PC_WRITABLE);
+  /* POSIX says mkdir("symlink-to-missing/") should create the
+     directory "missing", but Linux rejects it with EEXIST.  Copy
+     Linux behavior for now.  */
 
-  if (real_dir.error)
+  if (!*dir)
     {
-      set_errno (real_dir.case_clash ? ECASECLASH : real_dir.error);
+      set_errno (ENOENT);
       goto done;
     }
+  if (isdirsep (dir[strlen (dir) - 1]))
+    {
+      /* This converts // to /, but since both give EEXIST, we're okay.  */
+      char *buf;
+      char *p = stpcpy (buf = tp.c_get (), dir) - 1;
+      dir = buf;
+      while (p > dir && isdirsep (*p))
+        *p-- = '\0';
+    }
+  if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW)))
+    goto done;   /* errno already set */;
 
-  nofinalslash (real_dir.get_win32 (), real_dir.get_win32 ());
-
-  if (allow_ntsec && real_dir.has_acls ())
-    set_security_attribute (S_IFDIR | ((mode & 07777) & ~cygheap->umask),
-                           &sa, sd);
-
-  if (CreateDirectoryA (real_dir.get_win32 (), &sa))
+  if (fh->error ())
     {
-      if (!allow_ntsec && allow_ntea)
-       set_file_attribute (false, NULL, real_dir.get_win32 (),
-                           S_IFDIR | ((mode & 07777) & ~cygheap->umask));
-#ifdef HIDDEN_DOT_FILES
-      char *c = strrchr (real_dir.get_win32 (), '\\');
-      if ((c && c[1] == '.') || *real_dir.get_win32 () == '.')
-       SetFileAttributes (real_dir.get_win32 (), FILE_ATTRIBUTE_HIDDEN);
-#endif
-      res = 0;
+      debug_printf ("got %d error from build_fh_name", fh->error ());
+      set_errno (fh->error ());
     }
-  else
-    __seterrno ();
+  else if (has_dot_last_component (dir, true))
+    set_errno (fh->exists () ? EEXIST : ENOENT);
+  else if (!fh->mkdir (mode))
+    res = 0;
+  delete fh;
 
-done:
+ done:
   syscall_printf ("%d = mkdir (%s, %d)", res, dir, mode);
   return res;
 }
@@ -263,80 +323,30 @@ extern "C" int
 rmdir (const char *dir)
 {
   int res = -1;
+  fhandler_base *fh = NULL;
 
-  path_conv real_dir (dir, PC_SYM_NOFOLLOW | PC_WRITABLE);
+  myfault efault;
+  if (efault.faulted (EFAULT))
+    return -1;
 
-  if (real_dir.error)
-    set_errno (real_dir.error);
-  else if (!real_dir.exists ())
-    set_errno (ENOENT);
-  else if  (!real_dir.isdir ())
-    set_errno (ENOTDIR);
-  else
-    {
-      /* Even own directories can't be removed if R/O attribute is set. */
-      if (real_dir.has_attribute (FILE_ATTRIBUTE_READONLY))
-       SetFileAttributes (real_dir,
-                          (DWORD) real_dir & ~FILE_ATTRIBUTE_READONLY);
+  if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW)))
+    goto done;   /* errno already set */;
 
-      for (bool is_cwd = false; ; is_cwd = true)
-       {
-         DWORD err;
-         int rc = RemoveDirectory (real_dir);
-         DWORD att = GetFileAttributes (real_dir);
-
-         /* Sometimes smb indicates failure when it really succeeds, so check for
-            this case specifically. */
-         if (rc || att == INVALID_FILE_ATTRIBUTES)
-           {
-             /* RemoveDirectory on a samba drive doesn't return an error if the
-                directory can't be removed because it's not empty. Checking for
-                existence afterwards keeps us informed about success. */
-             if (att == INVALID_FILE_ATTRIBUTES)
-               {
-                 res = 0;
-                 break;
-               }
-             err = ERROR_DIR_NOT_EMPTY;
-           }
-         else
-           err = GetLastError ();
-
-         /* This kludge detects if we are attempting to remove the current working
-            directory.  If so, we will move elsewhere to potentially allow the
-            rmdir to succeed.  This means that cygwin's concept of the current working
-            directory != Windows concept but, hey, whaddaregonnado?
-            Note that this will not cause something like the following to work:
-                    $ cd foo
-                    $ rmdir .
-            since the shell will have foo "open" in the above case and so Windows will
-            not allow the deletion. (Actually it does on 9X.)
-            FIXME: A potential workaround for this is for cygwin apps to *never* call
-            SetCurrentDirectory. */
-
-         if (strcasematch (real_dir, cygheap->cwd.win32)
-             && !strcasematch ("c:\\", cygheap->cwd.win32)
-             && !is_cwd
-             && SetCurrentDirectory ("c:\\"))
-           continue;
-
-         /* On 9X ERROR_ACCESS_DENIED is returned
-            if you try to remove a non-empty directory. */
-         if (err == ERROR_ACCESS_DENIED
-             && wincap.access_denied_on_delete ())
-           err = ERROR_DIR_NOT_EMPTY;
-
-         __seterrno_from_win_error (err);
-
-         /* Directory still exists, restore its characteristics. */
-         if (real_dir.has_attribute (FILE_ATTRIBUTE_READONLY))
-           SetFileAttributes (real_dir, real_dir);
-         if (is_cwd)
-           SetCurrentDirectory (real_dir);
-         break;
-       }
+  if (fh->error ())
+    {
+      debug_printf ("got %d error from build_fh_name", fh->error ());
+      set_errno (fh->error ());
     }
+  else if (!fh->exists ())
+    set_errno (ENOENT);
+  else if (has_dot_last_component (dir, false))
+    set_errno (EINVAL);
+  else if (!fh->rmdir ())
+    res = 0;
+
+  delete fh;
 
+ done:
   syscall_printf ("%d = rmdir (%s)", res, dir);
   return res;
 }