* syscalls.cc (start_transaction): Make inline function. Move up to be

more generally available.
	(stop_transaction): Ditto.
	(unlink_nt): Potentially start transaction when trying to delete file
	with DOS R/O attribute set.  If file is .lnk symlink, check for number
	of hardlinks.  Add "out" label and only return via "out".  Rearrange
	reversion of DOS R/O attribute and, on success, only revert R/O
	attribute if file is .lnk symlink with more than one hardlink.  Add
	length comment to explain why.
This commit is contained in:
Corinna Vinschen 2010-09-12 11:41:56 +00:00
parent 2981e56e29
commit 18df393573
2 changed files with 97 additions and 42 deletions

View File

@ -1,3 +1,15 @@
2010-09-12 Corinna Vinschen <corinna@vinschen.de>
* syscalls.cc (start_transaction): Make inline function. Move up to be
more generally available.
(stop_transaction): Ditto.
(unlink_nt): Potentially start transaction when trying to delete file
with DOS R/O attribute set. If file is .lnk symlink, check for number
of hardlinks. Add "out" label and only return via "out". Rearrange
reversion of DOS R/O attribute and, on success, only revert R/O
attribute if file is .lnk symlink with more than one hardlink. Add
length comment to explain why.
2010-09-11 Corinna Vinschen <corinna@vinschen.de> 2010-09-11 Corinna Vinschen <corinna@vinschen.de>
* fhandler_disk_file.cc (fhandler_disk_file::rmdir): More thoroughly * fhandler_disk_file.cc (fhandler_disk_file::rmdir): More thoroughly

View File

@ -164,6 +164,36 @@ dup3 (int oldfd, int newfd, int flags)
return cygheap->fdtab.dup3 (oldfd, newfd, flags); return cygheap->fdtab.dup3 (oldfd, newfd, flags);
} }
static inline void
start_transaction (HANDLE &old_trans, HANDLE &trans)
{
NTSTATUS status = NtCreateTransaction (&trans,
SYNCHRONIZE | TRANSACTION_ALL_ACCESS,
NULL, NULL, NULL, 0, 0, 0, NULL, NULL);
if (NT_SUCCESS (status))
{
old_trans = RtlGetCurrentTransaction ();
RtlSetCurrentTransaction (trans);
}
else
{
debug_printf ("NtCreateTransaction failed, %p", status);
old_trans = trans = NULL;
}
}
static inline NTSTATUS
stop_transaction (NTSTATUS status, HANDLE old_trans, HANDLE trans)
{
RtlSetCurrentTransaction (old_trans);
if (NT_SUCCESS (status))
status = NtCommitTransaction (trans, TRUE);
else
status = NtRollbackTransaction (trans, TRUE);
NtClose (trans);
return status;
}
static char desktop_ini[] = static char desktop_ini[] =
"[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n"; "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n";
static BYTE info2[] = static BYTE info2[] =
@ -503,6 +533,9 @@ unlink_nt (path_conv &pc)
HANDLE fh, fh_ro = NULL; HANDLE fh, fh_ro = NULL;
OBJECT_ATTRIBUTES attr; OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
HANDLE old_trans = NULL, trans = NULL;
ULONG num_links = 1;
FILE_DISPOSITION_INFORMATION disp = { TRUE };
bin_status bin_stat = dont_move; bin_status bin_stat = dont_move;
@ -522,7 +555,14 @@ unlink_nt (path_conv &pc)
attribute, just re-open the file for DELETE and go ahead. */ attribute, just re-open the file for DELETE and go ahead. */
if (pc.file_attributes () & FILE_ATTRIBUTE_READONLY) if (pc.file_attributes () & FILE_ATTRIBUTE_READONLY)
{ {
access |= FILE_WRITE_ATTRIBUTES; FILE_STANDARD_INFORMATION fsi;
/* If possible, hide the non-atomicity of the "remove R/O flag, remove
link to file" operation behind a transaction. */
if (wincap.has_transactions ()
&& (pc.fs_flags () & FILE_SUPPORTS_TRANSACTIONS))
start_transaction (old_trans, trans);
status = NtOpenFile (&fh_ro, FILE_WRITE_ATTRIBUTES, &attr, &io, status = NtOpenFile (&fh_ro, FILE_WRITE_ATTRIBUTES, &attr, &io,
FILE_SHARE_VALID_FLAGS, flags); FILE_SHARE_VALID_FLAGS, flags);
if (NT_SUCCESS (status)) if (NT_SUCCESS (status))
@ -532,6 +572,14 @@ unlink_nt (path_conv &pc)
InitializeObjectAttributes (&attr, &ro_u_empty, InitializeObjectAttributes (&attr, &ro_u_empty,
pc.objcaseinsensitive (), fh_ro, NULL); pc.objcaseinsensitive (), fh_ro, NULL);
} }
if (pc.is_lnk_symlink ())
{
status = NtQueryInformationFile (fh_ro, &io, &fsi, sizeof fsi,
FileStandardInformation);
if (NT_SUCCESS (status))
num_links = fsi.NumberOfLinks;
}
access |= FILE_WRITE_ATTRIBUTES;
} }
/* First try to open the file with only allowing sharing for delete. If /* First try to open the file with only allowing sharing for delete. If
the file has an open handle on it, other than just for deletion, this the file has an open handle on it, other than just for deletion, this
@ -589,7 +637,7 @@ unlink_nt (path_conv &pc)
NtClose (fh); NtClose (fh);
if (fh_ro) if (fh_ro)
NtClose (fh_ro); NtClose (fh_ro);
return status; goto out;
} }
} }
} }
@ -601,18 +649,21 @@ unlink_nt (path_conv &pc)
if (status == STATUS_DELETE_PENDING) if (status == STATUS_DELETE_PENDING)
{ {
syscall_printf ("Delete already pending"); syscall_printf ("Delete already pending");
return 0; status = 0;
goto out;
} }
syscall_printf ("Opening file for delete failed, status = %p", status); syscall_printf ("Opening file for delete failed, status = %p", status);
return status; goto out;
} }
/* Try to move to bin if a sharing violation occured. If that worked, /* Try to move to bin if a sharing violation occured. If that worked,
we're done. */ we're done. */
if (bin_stat == move_to_bin if (bin_stat == move_to_bin
&& (bin_stat = try_to_bin (pc, fh, access)) == has_been_moved) && (bin_stat = try_to_bin (pc, fh, access)) == has_been_moved)
return 0; {
status = 0;
goto out;
}
/* Try to set delete disposition. */ /* Try to set delete disposition. */
FILE_DISPOSITION_INFORMATION disp = { TRUE };
status = NtSetInformationFile (fh, &io, &disp, sizeof disp, status = NtSetInformationFile (fh, &io, &disp, sizeof disp,
FileDispositionInformation); FileDispositionInformation);
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
@ -661,14 +712,36 @@ unlink_nt (path_conv &pc)
} }
if (fh) if (fh)
{ {
/* Restore R/O attribute to accommodate hardlinks. Don't try this if (access & FILE_WRITE_ATTRIBUTES)
with directories! For some reason the below NtSetInformationFile {
changes the delete disposition back to FALSE, at least on XP. */ /* Restore R/O attribute if setting the delete dispostion failed. */
if ((access & FILE_WRITE_ATTRIBUTES) if (!NT_SUCCESS (status))
&& (!NT_SUCCESS (status) || !pc.isdir ())) NtSetAttributesFile (fh, pc.file_attributes ());
/* If we succeeded, restore R/O attribute to accommodate hardlinks.
Only ever try to do this for our own winsymlinks, because there's
a problem with setting the delete disposition:
http://msdn.microsoft.com/en-us/library/ff545765%28VS.85%29.aspx
"Subsequently, the only legal operation by such a caller is
to close the open file handle."
FIXME? On Vista and later, we could use FILE_HARD_LINK_INFORMATION
to find all hardlinks and use one of them to restore the R/O bit,
after the NtClose, but before we stop the transaction. This
avoids the aforementioned problem entirely . */
else if (pc.is_lnk_symlink () && num_links > 1)
NtSetAttributesFile (fh, pc.file_attributes ()); NtSetAttributesFile (fh, pc.file_attributes ());
NtClose (fh);
} }
NtClose (fh);
}
out:
/* Stop transaction if we started one. */
if ((access & FILE_WRITE_ATTRIBUTES)
&& wincap.has_transactions ()
&& (pc.fs_flags () & FILE_SUPPORTS_TRANSACTIONS))
stop_transaction (status, old_trans, trans);
return status; return status;
} }
@ -1658,36 +1731,6 @@ rename_append_suffix (path_conv &pc, const char *path, size_t len,
pc.check (buf, PC_SYM_NOFOLLOW); pc.check (buf, PC_SYM_NOFOLLOW);
} }
static void
start_transaction (HANDLE &old_trans, HANDLE &trans)
{
NTSTATUS status = NtCreateTransaction (&trans,
SYNCHRONIZE | TRANSACTION_ALL_ACCESS,
NULL, NULL, NULL, 0, 0, 0, NULL, NULL);
if (NT_SUCCESS (status))
{
old_trans = RtlGetCurrentTransaction ();
RtlSetCurrentTransaction (trans);
}
else
{
debug_printf ("NtCreateTransaction failed, %p", status);
old_trans = trans = NULL;
}
}
static NTSTATUS
stop_transaction (NTSTATUS status, HANDLE old_trans, HANDLE trans)
{
RtlSetCurrentTransaction (old_trans);
if (NT_SUCCESS (status))
status = NtCommitTransaction (trans, TRUE);
else
status = NtRollbackTransaction (trans, TRUE);
NtClose (trans);
return status;
}
/* This function tests if a filename has one of the "approved" executable /* This function tests if a filename has one of the "approved" executable
suffix. This list is probably not complete... */ suffix. This list is probably not complete... */
static inline bool static inline bool