summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2007-07-31 15:20:00 +0000
committerCorinna Vinschen <corinna@vinschen.de>2007-07-31 15:20:00 +0000
commit9235f3ead1cc91bd1a864ecf635717823f1da610 (patch)
tree4b7ffdaf98112e1f9715805b01c00d5e1b185daf
parent378692ee42bb29280c439600832e84a0ddb7471a (diff)
downloadcygnal-9235f3ead1cc91bd1a864ecf635717823f1da610.tar.gz
cygnal-9235f3ead1cc91bd1a864ecf635717823f1da610.tar.bz2
cygnal-9235f3ead1cc91bd1a864ecf635717823f1da610.zip
* 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.
-rw-r--r--winsup/cygwin/ChangeLog11
-rw-r--r--winsup/cygwin/fhandler_disk_file.cc3
-rw-r--r--winsup/cygwin/ntdll.h2
-rw-r--r--winsup/cygwin/path.cc10
-rw-r--r--winsup/cygwin/path.h1
-rw-r--r--winsup/cygwin/syscalls.cc315
6 files changed, 190 insertions, 152 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index 923500513..fc03aea72 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -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
diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
index a5a7c4b53..5df3c9945 100644
--- a/winsup/cygwin/fhandler_disk_file.cc
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -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))
{
diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h
index 7882eb15f..9e1606f34 100644
--- a/winsup/cygwin/ntdll.h
+++ b/winsup/cygwin/ntdll.h
@@ -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
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index 062f5ab7e..fe4f1d9e0 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -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. */
diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h
index c9ba9f311..390041813 100644
--- a/winsup/cygwin/path.h
+++ b/winsup/cygwin/path.h
@@ -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;
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
index 290253cd8..6a3920af6 100644
--- a/winsup/cygwin/syscalls.cc
+++ b/winsup/cygwin/syscalls.cc
@@ -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;
}