* ntdll.h (STATUS_DELETE_PENDING): Define.
(struct _FILE_DISPOSITION_INFORMATION): Define. * syscalls.cc (unlink_9x): new function to delete file on 9x. * syscalls.cc (unlink_nt): new function to delete file on NT. (unlink): Simplify. Move OS dependent stuff into aforementioned functions. Also handle FILE_ATTRIBUTE_HIDDEN as R/O-like flag.
This commit is contained in:
parent
07ec6541ab
commit
1c9b2968fa
|
@ -1,3 +1,12 @@
|
|||
2006-12-07 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
* ntdll.h (STATUS_DELETE_PENDING): Define.
|
||||
(struct _FILE_DISPOSITION_INFORMATION): Define.
|
||||
* syscalls.cc (unlink_9x): new function to delete file on 9x.
|
||||
* syscalls.cc (unlink_nt): new function to delete file on NT.
|
||||
(unlink): Simplify. Move OS dependent stuff into aforementioned
|
||||
functions. Also handle FILE_ATTRIBUTE_HIDDEN as R/O-like flag.
|
||||
|
||||
2006-12-07 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
* autoload.cc (SHFileOperationA): Define.
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xc0000004)
|
||||
#define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xc000000d)
|
||||
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xc0000023)
|
||||
#define STATUS_DELETE_PENDING ((NTSTATUS) 0xc0000056)
|
||||
#define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xc00000a1L)
|
||||
#define STATUS_INVALID_LEVEL ((NTSTATUS) 0xc0000148)
|
||||
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
|
||||
|
@ -480,6 +481,10 @@ typedef struct _FILE_ACCESS_INFORMATION {
|
|||
ACCESS_MASK AccessFlags;
|
||||
} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
|
||||
|
||||
typedef struct _FILE_DISPOSITION_INFORMATION {
|
||||
BOOLEAN DeleteFile;
|
||||
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
|
||||
|
||||
typedef struct _FILE_POSITION_INFORMATION {
|
||||
LARGE_INTEGER CurrentByteOffset;
|
||||
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
|
||||
|
|
|
@ -46,6 +46,8 @@ details. */
|
|||
#include <lmcons.h> /* for UNLEN */
|
||||
#include <rpc.h>
|
||||
#include <shellapi.h>
|
||||
#include <ntdef.h>
|
||||
#include "ntdll.h"
|
||||
|
||||
#undef fstat
|
||||
#undef lstat
|
||||
|
@ -173,6 +175,89 @@ try_to_bin (const char *win32_path)
|
|||
debug_printf ("SHFileOperation (%s) = %d\n", win32_path, ret);
|
||||
}
|
||||
|
||||
static DWORD
|
||||
unlink_9x (path_conv &win32_name)
|
||||
{
|
||||
BOOL ret = DeleteFile (win32_name);
|
||||
syscall_printf ("DeleteFile %s", ret ? "succeeded" : "failed");
|
||||
return GetLastError ();
|
||||
}
|
||||
|
||||
static DWORD
|
||||
unlink_nt (path_conv &win32_name, bool setattrs)
|
||||
{
|
||||
WCHAR wpath[CYG_MAX_PATH + 10];
|
||||
UNICODE_STRING upath = {0, sizeof (wpath), wpath};
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
IO_STATUS_BLOCK io;
|
||||
NTSTATUS status;
|
||||
HANDLE h;
|
||||
|
||||
ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT;
|
||||
/* Don't try "delete on close" if the file is on a remote share. If two
|
||||
processes have open handles on a file and one of them calls unlink,
|
||||
then it happens that the file is remove from the remote share even
|
||||
though the other process still has an open handle. This other process
|
||||
than gets Win32 error 59, ERROR_UNEXP_NET_ERR when trying to access the
|
||||
file.
|
||||
That does not happen when using DeleteFile, which nicely succeeds but
|
||||
still, the file is available for the other process.
|
||||
Microsoft KB 837665 describes this problem as a bug in 2K3, but I have
|
||||
reproduced it on shares on Samba 2.2.8, Samba 3.0.2, NT4SP6, XP64SP1 and
|
||||
2K3 and in all cases, DeleteFile works, "delete on close" does not. */
|
||||
if (!win32_name.isremote ())
|
||||
flags |= FILE_DELETE_ON_CLOSE;
|
||||
|
||||
win32_name.get_nt_native_path (upath);
|
||||
InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
|
||||
NULL, sec_none_nih.lpSecurityDescriptor);
|
||||
status = NtOpenFile (&h, DELETE, &attr, &io, wincap.shared (), flags);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
if (status == STATUS_DELETE_PENDING)
|
||||
{
|
||||
syscall_printf ("Delete already pending, status = %p", status);
|
||||
return 0;
|
||||
}
|
||||
syscall_printf ("Opening file for delete failed, status = %p", status);
|
||||
return RtlNtStatusToDosError (status);
|
||||
}
|
||||
|
||||
if (setattrs)
|
||||
SetFileAttributes (win32_name, (DWORD) win32_name);
|
||||
|
||||
if (!win32_name.isremote ())
|
||||
try_to_bin (win32_name.get_win32 ());
|
||||
|
||||
DWORD lasterr = 0;
|
||||
|
||||
if (win32_name.isremote ())
|
||||
{
|
||||
FILE_DISPOSITION_INFORMATION disp = { TRUE };
|
||||
status = NtSetInformationFile (h, &io, &disp, sizeof disp,
|
||||
FileDispositionInformation);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
syscall_printf ("Setting delete disposition failed, status = %p",
|
||||
status);
|
||||
lasterr = RtlNtStatusToDosError (status);
|
||||
}
|
||||
}
|
||||
|
||||
status = NtClose (h);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
/* Maybe that's really paranoid, but not being able to close the file
|
||||
also means that deleting fails. */
|
||||
syscall_printf ("%p = NtClose (%p)", status, h);
|
||||
if (!lasterr)
|
||||
RtlNtStatusToDosError (status);
|
||||
}
|
||||
|
||||
syscall_printf ("Deleting succeeded");
|
||||
return lasterr;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
unlink (const char *ourname)
|
||||
{
|
||||
|
@ -211,7 +296,9 @@ unlink (const char *ourname)
|
|||
}
|
||||
|
||||
bool setattrs;
|
||||
if (!((DWORD) win32_name & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
|
||||
if (!((DWORD) win32_name & (FILE_ATTRIBUTE_READONLY
|
||||
| FILE_ATTRIBUTE_SYSTEM
|
||||
| FILE_ATTRIBUTE_HIDDEN)))
|
||||
setattrs = false;
|
||||
else
|
||||
{
|
||||
|
@ -219,100 +306,33 @@ unlink (const char *ourname)
|
|||
setattrs = SetFileAttributes (win32_name,
|
||||
(DWORD) win32_name
|
||||
& ~(FILE_ATTRIBUTE_READONLY
|
||||
| FILE_ATTRIBUTE_SYSTEM));
|
||||
}
|
||||
/* Attempt to use "delete on close" semantics to handle removing
|
||||
a file which may be open.
|
||||
|
||||
CV 2004-09-17: Not if the file is on a remote share. If two processes
|
||||
have open handles on a file and one of them calls unlink, then it
|
||||
happens that the file is remove from the remote share even though the
|
||||
other process still has an open handle. This other process than gets
|
||||
Win32 error 59, ERROR_UNEXP_NET_ERR when trying to access the file.
|
||||
|
||||
For some reason, that does not happen when using DeleteFile, which
|
||||
nicely succeeds but still, the file is available for the other process.
|
||||
To reproduce, mount /tmp on a remote share and call
|
||||
|
||||
bash -c "cat << EOF"
|
||||
|
||||
Microsoft KB 837665 describes this problem as a bug in 2K3, but I have
|
||||
reproduced it on shares on Samba 2.2.8, Samba 3.0.2, NT4SP6, XP64SP1 and
|
||||
2K3 and in all cases, DeleteFile works, "delete on close" does not. */
|
||||
if (!win32_name.isremote () && wincap.has_delete_on_close ())
|
||||
{
|
||||
HANDLE h;
|
||||
DWORD flags = FILE_FLAG_DELETE_ON_CLOSE;
|
||||
if (win32_name.is_rep_symlink ())
|
||||
flags |= FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS;
|
||||
h = CreateFile (win32_name, DELETE, wincap.shared (), &sec_none_nih,
|
||||
OPEN_EXISTING, flags, 0);
|
||||
if (h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (wincap.has_hard_links () && setattrs)
|
||||
SetFileAttributes (win32_name, (DWORD) win32_name);
|
||||
try_to_bin (win32_name.get_win32 ());
|
||||
BOOL res = CloseHandle (h);
|
||||
syscall_printf ("%d = CloseHandle (%p)", res, h);
|
||||
if (GetFileAttributes (win32_name) == INVALID_FILE_ATTRIBUTES
|
||||
|| !win32_name.isremote ())
|
||||
{
|
||||
syscall_printf ("CreateFile (FILE_FLAG_DELETE_ON_CLOSE) succeeded");
|
||||
goto ok;
|
||||
}
|
||||
else
|
||||
{
|
||||
syscall_printf ("CreateFile (FILE_FLAG_DELETE_ON_CLOSE) failed");
|
||||
if (setattrs)
|
||||
SetFileAttributes (win32_name, (DWORD) win32_name & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Try a delete with attributes reset */
|
||||
if (win32_name.is_rep_symlink () && RemoveDirectory (win32_name))
|
||||
{
|
||||
syscall_printf ("RemoveDirectory after CreateFile/CloseHandle succeeded");
|
||||
goto ok;
|
||||
}
|
||||
else if (DeleteFile (win32_name))
|
||||
{
|
||||
syscall_printf ("DeleteFile after CreateFile/CloseHandle succeeded");
|
||||
goto ok;
|
||||
| FILE_ATTRIBUTE_SYSTEM
|
||||
| FILE_ATTRIBUTE_HIDDEN));
|
||||
}
|
||||
|
||||
DWORD lasterr;
|
||||
lasterr = GetLastError ();
|
||||
lasterr = wincap.is_winnt () ? unlink_nt (win32_name, setattrs)
|
||||
: unlink_9x (win32_name);
|
||||
if (!lasterr)
|
||||
res = 0;
|
||||
else
|
||||
{
|
||||
SetFileAttributes (win32_name, (DWORD) win32_name);
|
||||
|
||||
SetFileAttributes (win32_name, (DWORD) win32_name);
|
||||
|
||||
/* Windows 9x seems to report ERROR_ACCESS_DENIED rather than sharing
|
||||
violation. So, set lasterr to ERROR_SHARING_VIOLATION in this case
|
||||
to simplify tests. */
|
||||
if (wincap.access_denied_on_delete () && lasterr == ERROR_ACCESS_DENIED
|
||||
&& !win32_name.isremote ())
|
||||
lasterr = ERROR_SHARING_VIOLATION;
|
||||
|
||||
/* FILE_FLAGS_DELETE_ON_CLOSE was a bust. If this is a sharing
|
||||
violation, then queue the file for deletion when the process
|
||||
exits. Otherwise, punt. */
|
||||
if (lasterr != ERROR_SHARING_VIOLATION)
|
||||
goto err;
|
||||
|
||||
syscall_printf ("couldn't delete file, err %d", lasterr);
|
||||
|
||||
/* Add file to the "to be deleted" queue. */
|
||||
user_shared->delqueue.queue_file (win32_name);
|
||||
|
||||
/* Success condition. */
|
||||
ok:
|
||||
res = 0;
|
||||
goto done;
|
||||
|
||||
/* Error condition. */
|
||||
err:
|
||||
__seterrno ();
|
||||
res = -1;
|
||||
/* Windows 9x seems to report ERROR_ACCESS_DENIED rather than sharing
|
||||
violation. */
|
||||
if ((wincap.access_denied_on_delete () && lasterr == ERROR_ACCESS_DENIED
|
||||
&& !win32_name.isremote ())
|
||||
|| lasterr == ERROR_SHARING_VIOLATION)
|
||||
{
|
||||
/* Add file to the "to be deleted" queue. */
|
||||
syscall_printf ("Sharing violation, couldn't delete file");
|
||||
user_shared->delqueue.queue_file (win32_name);
|
||||
res = 0;
|
||||
}
|
||||
else
|
||||
__seterrno_from_win_error (lasterr);
|
||||
}
|
||||
|
||||
done:
|
||||
syscall_printf ("%d = unlink (%s)", res, ourname);
|
||||
|
|
Loading…
Reference in New Issue