OSDN Git Service

* fhandler_disk_file.cc (fhandler_disk_file::link): Revert to checking
authorcorinna <corinna>
Tue, 31 Jul 2007 15:19:59 +0000 (15:19 +0000)
committercorinna <corinna>
Tue, 31 Jul 2007 15:19:59 +0000 (15:19 +0000)
for binary in case of .exe files.
* ntdll.h (RtlPrefixUnicodeString): Declare.
* path.cc (path_conv::is_binary): New method.
* path.h (path_conv::is_binary): Declare.
* syscalls.cc (rename_append_suffix): New static helper function for
rename.
(rename): Rewrite.  New suffix tests.  Use native NT functions.

winsup/cygwin/ChangeLog
winsup/cygwin/fhandler_disk_file.cc
winsup/cygwin/ntdll.h
winsup/cygwin/path.cc
winsup/cygwin/path.h
winsup/cygwin/syscalls.cc

index 9235005..fc03aea 100644 (file)
@@ -1,3 +1,14 @@
+2007-07-31  Corinna Vinschen  <corinna@vinschen.de>
+
+       * fhandler_disk_file.cc (fhandler_disk_file::link): Revert to checking
+       for binary in case of .exe files.
+       * ntdll.h (RtlPrefixUnicodeString): Declare.
+       * path.cc (path_conv::is_binary): New method.
+       * path.h (path_conv::is_binary): Declare.
+       * syscalls.cc (rename_append_suffix): New static helper function for
+       rename.
+       (rename): Rewrite.  New suffix tests.  Use native NT functions.
+
 2007-07-30  Corinna Vinschen  <corinna@vinschen.de>
 
        * fhandler_disk_file.cc (fhandler_disk_file::facl): If file can't be
index a5a7c4b..5df3c99 100644 (file)
@@ -1084,8 +1084,7 @@ fhandler_disk_file::link (const char *newpath)
          newpc.check (newpath, PC_SYM_NOFOLLOW);
        }
       else if (!pc.isdir ()
-              && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
-                                            L".exe", TRUE)
+              && pc.is_binary ()
               && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
                                              L".exe", TRUE))
        {
index 7882eb1..9e1606f 100644 (file)
@@ -797,6 +797,8 @@ extern "C"
   ULONG NTAPI RtlNtStatusToDosError (NTSTATUS);
   NTSTATUS NTAPI RtlOemStringToUnicodeString (PUNICODE_STRING, POEM_STRING,
                                               BOOLEAN);
+  BOOLEAN NTAPI RtlPrefixUnicodeString (PUNICODE_STRING, PUNICODE_STRING,
+                                       BOOLEAN);
   VOID NTAPI RtlSecondsSince1970ToTime (ULONG, PLARGE_INTEGER);
 
   /* A few Rtl functions are either actually macros, or they just don't
index 062f5ab..fe4f1d9 100644 (file)
@@ -1205,6 +1205,16 @@ path_conv::~path_conv ()
     }
 }
 
+bool
+path_conv::is_binary ()
+{
+  DWORD bin;
+  PBYTE bintest[get_nt_native_path ()->Length + sizeof (WCHAR)];
+  return exec_state () == is_executable
+        && RtlEqualUnicodePathSuffix (get_nt_native_path (), L".exe", TRUE)
+        && GetBinaryTypeW (get_wide_win32_path ((PWCHAR) bintest), &bin);
+}
+
 /* Return true if src_path is a valid, internally supported device name.
    In that case, win32_path gets the corresponding NT device name and
    dev is appropriately filled with device information. */
index c9ba9f3..3900418 100644 (file)
@@ -300,6 +300,7 @@ class path_conv
   {
     return (sizeof (*this) - sizeof (path)) + strlen (path) + 1 + normalized_path_size;
   }
+  bool is_binary ();
 
   unsigned __stdcall ndisk_links (DWORD);
   char *normalized_path;
index 290253c..6a3920a 100644 (file)
@@ -1321,184 +1321,199 @@ access (const char *fn, int flags)
   return res;
 }
 
-extern "C" int
-rename (const char *oldpath, const char *newpath)
+static void
+rename_append_suffix (path_conv &pc, const char *path, size_t len,
+                     const char *suffix)
 {
-  int res = 0;
-  char *lnk_suffix = NULL;
-  bool no_lnk_file_exists = false;
+  char buf[len + 5];
 
-  path_conv real_old (oldpath, PC_SYM_NOFOLLOW,
-                     transparent_exe ? stat_suffixes : NULL);
+  if (strcasematch (path + len - 4, ".lnk")
+      || strcasematch (path + len - 4, ".exe"))
+    len -= 4;
+  stpcpy (stpncpy (buf, path, len), suffix);
+  pc.check (buf, PC_SYM_NOFOLLOW);
+}
 
-  if (real_old.error)
+extern "C" int
+rename (const char *oldpath, const char *newpath)
+{
+  int res = -1;
+  path_conv oldpc, newpc, new2pc, *dstpc, *removepc = NULL;
+  bool old_explicit_suffix = false, new_explicit_suffix = false;
+  size_t olen, nlen;
+  NTSTATUS status;
+  HANDLE fh;
+  OBJECT_ATTRIBUTES attr;
+  IO_STATUS_BLOCK io;
+  ULONG size;
+  PFILE_RENAME_INFORMATION pfri;
+  
+  oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
+  if (oldpc.error)
     {
-      syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
-      set_errno (real_old.error);
-      return -1;
+      set_errno (oldpc.error);
+      goto out;
     }
-
-  if (!real_old.exists ()) /* file to move doesn't exist */
+  if (!oldpc.exists ())
     {
-      syscall_printf ("file to move doesn't exist");
       set_errno (ENOENT);
-      return -1;
+      goto out;
     }
+  olen = strlen (oldpath);
+  if (oldpc.known_suffix
+      && (strcasematch (oldpath + olen - 4, ".lnk")
+         || strcasematch (oldpath + olen - 4, ".exe")))
+    old_explicit_suffix = true;
 
-  path_conv real_new (newpath, PC_SYM_NOFOLLOW,
-                     transparent_exe ? stat_suffixes : NULL);
-
-  char new_buf[CYG_MAX_PATH + 5];
-  if (!real_new.error && !real_new.case_clash)
+  newpc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes);
+  if (newpc.error)
+    {
+      set_errno (newpc.error);
+      goto out;
+    }
+  if (newpc.isspecial ()) /* No renames out of the FS */
     {
-      DWORD bintype;
-      int len;
+      set_errno (EROFS);
+      goto out;
+    }
+  nlen = strlen (newpath);
+  if (newpc.known_suffix
+      && (strcasematch (newpath + nlen - 4, ".lnk")
+         || strcasematch (newpath + nlen - 4, ".exe")))
+    new_explicit_suffix = true;
 
-      if (real_old.is_lnk_special ())
+  if (oldpc.isdir ())
+    {
+      if (newpc.exists () && !newpc.isdir ())
        {
-         if (real_new.exists ())
-           {
-             /* This early directory test is necessary because the below test
-                tests against the name with attached .lnk suffix.  To avoid
-                name collisions, we shouldn't rename a file to "foo.lnk"
-                if a "foo" directory exists. */
-             if (real_new.isdir ())
-               {
-                 syscall_printf ("newpath is directory, but oldpath is not");
-                 set_errno (EISDIR);
-                 return -1;
-               }
-             /* Shortcut hack, No. 3, part 1 */
-             no_lnk_file_exists = true;
-           }
-         /* Shortcut hack. */
-         strcpy (new_buf, newpath);
-         strcat (new_buf, ".lnk");
-         newpath = new_buf;
-         real_new.check (newpath, PC_SYM_NOFOLLOW);
+         set_errno (ENOTDIR);
+         goto out;
        }
-      else if (transparent_exe
-              && !real_old.isdir ()
-              && GetBinaryType (real_old, &bintype)
-              && (len = strlen (real_new)) > 4
-              && !strcasematch ((const char *) real_new + len - 4, ".exe"))
+      /* Check for newpath being a subdir of oldpath. */
+      if (RtlPrefixUnicodeString (oldpc.get_nt_native_path (),
+                                 newpc.get_nt_native_path (),
+                                 TRUE)
+         && newpc.get_nt_native_path ()->Length >
+            oldpc.get_nt_native_path ()->Length
+         && *(PWCHAR) ((PBYTE) newpc.get_nt_native_path ()->Buffer
+                       + oldpc.get_nt_native_path ()->Length) == L'\\')
        {
-         /* Executable hack. */
-         strcpy (new_buf, newpath);
-         strcat (new_buf, ".exe");
-         newpath = new_buf;
-         real_new.check (newpath, PC_SYM_NOFOLLOW);
+         set_errno (EINVAL);
+         goto out;
        }
     }
-
-  if (real_new.error || real_new.case_clash)
+  else if (!newpc.exists ())
     {
-      syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
-      set_errno (real_new.case_clash ? ECASECLASH : real_new.error);
-      return -1;
-    }
-
-  if (real_new.isdir () && !real_old.isdir ())
+      if (RtlEqualUnicodeString (oldpc.get_nt_native_path (),
+                                newpc.get_nt_native_path (),
+                                TRUE)
+         && old_explicit_suffix != new_explicit_suffix)
+       {
+         newpc.check (newpath, PC_SYM_NOFOLLOW);
+         if (RtlEqualUnicodeString (oldpc.get_nt_native_path (),
+                                    newpc.get_nt_native_path (),
+                                    TRUE))
+           {
+             res = 0;
+             goto out;
+           }
+       }
+      else if (oldpc.is_lnk_symlink ()
+              && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
+                                             L".lnk", TRUE))
+       rename_append_suffix (newpc, newpath, nlen, ".lnk");
+      else if (oldpc.is_binary ()
+          && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
+                                         L".exe", TRUE))
+       /* NOTE: No way to rename an executable foo.exe to foo. */
+       rename_append_suffix (newpc, newpath, nlen, ".exe");
+    }
+  else if (newpc.isdir ())
     {
-      syscall_printf ("newpath is directory, but oldpath is not");
       set_errno (EISDIR);
-      return -1;
-    }
-
-  /* Destination file exists and is read only, change that or else
-     the rename won't work. */
-  if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY))
-    SetFileAttributes (real_new, (DWORD) real_new & ~FILE_ATTRIBUTE_READONLY);
-
-  /* Shortcut hack No. 2, part 1 */
-  if (!real_old.issymlink () && !real_new.error && real_new.is_lnk_special ()
-      && (lnk_suffix = strrchr (real_new.get_win32 (), '.')))
-     *lnk_suffix = '\0';
-
-  if (MoveFile (real_old, real_new))
-    goto done;
-
-  res = -1;
-
-  /* Test for an attempt to make a directory a subdirectory of itself first.
-     This test has to be made before any attempt to remove the potentially
-     existing file or directory real_new.  Otherwise we end up with a
-     non-moved directory *and* a deleted read_new path.  Also this case
-     has to generate an EINVAL in all circumstances,
-
-     NB: We could test this also before calling MoveFile but the idea is
-     that this is a somewhat seldom case and we like to avoid expensive
-     string comparison.  So we allow MoveFile to fail and test the error
-     code instead.
-
-     The order in the condition is (hopefully) trimmed for doing the least
-     expensive stuff first. */
-  int len;
-  DWORD lasterr;
-  lasterr = GetLastError ();
-  if (real_old.isdir ()
-      && lasterr == ERROR_SHARING_VIOLATION
-      && (len = strlen (real_old), strncasematch (real_old, real_new, len))
-      && real_new[len] == '\\')
-    SetLastError (ERROR_INVALID_PARAMETER);
-  else if (MoveFileEx (real_old.get_win32 (), real_new.get_win32 (),
-                      MOVEFILE_REPLACE_EXISTING))
-    res = 0;
-  else if ((lasterr = unlink_nt (real_new)))
-    {
-      SetLastError (lasterr);
-      syscall_printf ("Can't remove target file/dir, %E");
-    }
-  else if (MoveFile (real_old, real_new))
-    res = 0;
-
-done:
-  if (res)
-    {
-      __seterrno ();
-      /* Reset R/O attributes if neccessary. */
-      if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY))
-       SetFileAttributes (real_new, real_new);
+      goto out;
     }
   else
     {
-      /* make the new file have the permissions of the old one */
-      DWORD attr = real_old;
-#ifdef HIDDEN_DOT_FILES
-      char *c = strrchr (real_old.get_win32 (), '\\');
-      if ((c && c[1] == '.') || *real_old.get_win32 () == '.')
-       attr &= ~FILE_ATTRIBUTE_HIDDEN;
-      c = strrchr (real_new.get_win32 (), '\\');
-      if ((c && c[1] == '.') || *real_new.get_win32 () == '.')
-       attr |= FILE_ATTRIBUTE_HIDDEN;
-#endif
-      SetFileAttributes (real_new, attr);
-
-      /* Shortcut hack, No. 2, part 2 */
-      /* if the new filename was an existing shortcut, remove it now if the
-        new filename is equal to the shortcut name without .lnk suffix. */
-      if (lnk_suffix)
+      if (RtlEqualUnicodeString (oldpc.get_nt_native_path (),
+                                newpc.get_nt_native_path (),
+                                TRUE)
+         && old_explicit_suffix != new_explicit_suffix)
        {
-         *lnk_suffix = '.';
-         unlink_nt (real_new);
+         newpc.check (newpath, PC_SYM_NOFOLLOW);
+         if (RtlEqualUnicodeString (oldpc.get_nt_native_path (),
+                                    newpc.get_nt_native_path (),
+                                    TRUE))
+           {
+             res = 0;
+             goto out;
+           }
        }
-      /* Shortcut hack, No. 3, part 2 */
-      /* If a file with the given name exists, it must be deleted after the
-        symlink has been renamed.  Otherwise we end up with two files of
-        the same name in the directory, one file "newpath", which already
-        exited before rename has been called, and one file "newpath.lnk",
-        which is the result of the rename operation. */
-      else if (no_lnk_file_exists)
+      else if (oldpc.is_lnk_symlink ())
+        {
+         if (!newpc.is_lnk_symlink ()
+             && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
+                                            L".lnk", TRUE))
+           {
+             rename_append_suffix (new2pc, newpath, nlen, ".lnk");
+             removepc = &newpc;
+           }
+       }
+      else if (oldpc.is_binary ())
        {
-         lnk_suffix = strrchr (real_new.get_win32 (), '.');
-         *lnk_suffix = '\0';
-         unlink_nt (real_new);
+         if (!RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
+                                         L".exe", TRUE))
+           {
+             rename_append_suffix (new2pc, newpath, nlen, ".exe");
+             removepc = &newpc;
+           }
+       }
+      else
+        {
+         if ((RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
+                                         L".lnk", TRUE)
+              || RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
+                                            L".exe", TRUE))
+             && !new_explicit_suffix)
+           {
+             new2pc.check (newpath, PC_SYM_NOFOLLOW, stat_suffixes);
+             newpc.get_nt_native_path ()->Length -= 4 * sizeof (WCHAR);
+             if (newpc.is_binary () || newpc.is_lnk_symlink ())
+               removepc = &new2pc;
+           }
        }
     }
+  dstpc = (removepc == &newpc) ? &new2pc : &newpc;
+  
+  /* DELETE is required to rename a file. */
+  status = NtOpenFile (&fh, DELETE, oldpc.get_object_attr (attr, sec_none_nih),
+                    &io, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
+  if (!NT_SUCCESS (status))
+    {
+      __seterrno_from_nt_status (status);
+      goto out;
+    }
+  size = sizeof (FILE_RENAME_INFORMATION)
+        + dstpc->get_nt_native_path ()->Length;
+  pfri = (PFILE_RENAME_INFORMATION) alloca (size);
+  pfri->ReplaceIfExists = TRUE;
+  pfri->RootDirectory = NULL;
+  pfri->FileNameLength = dstpc->get_nt_native_path ()->Length;
+  memcpy (&pfri->FileName,  dstpc->get_nt_native_path ()->Buffer,
+         pfri->FileNameLength);
+  status = NtSetInformationFile (fh, &io, pfri, size, FileRenameInformation);
+  if (NT_SUCCESS (status))
+    {
+      if (removepc)
+       unlink_nt (*removepc);
+      res = 0;
+    }
+  else
+    __seterrno_from_nt_status (status);
+  NtClose (fh);
 
-  syscall_printf ("%d = rename (%s, %s)", res, (char *) real_old,
-                 (char *) real_new);
-
+out:
+  syscall_printf ("%d = rename (%s, %s)", res, oldpath, newpath);
   return res;
 }