* 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>
|
2006-12-07 Corinna Vinschen <corinna@vinschen.de>
|
||||||
|
|
||||||
* autoload.cc (SHFileOperationA): Define.
|
* autoload.cc (SHFileOperationA): Define.
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xc0000004)
|
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xc0000004)
|
||||||
#define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xc000000d)
|
#define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xc000000d)
|
||||||
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xc0000023)
|
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xc0000023)
|
||||||
|
#define STATUS_DELETE_PENDING ((NTSTATUS) 0xc0000056)
|
||||||
#define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xc00000a1L)
|
#define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xc00000a1L)
|
||||||
#define STATUS_INVALID_LEVEL ((NTSTATUS) 0xc0000148)
|
#define STATUS_INVALID_LEVEL ((NTSTATUS) 0xc0000148)
|
||||||
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
|
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
|
||||||
|
@ -480,6 +481,10 @@ typedef struct _FILE_ACCESS_INFORMATION {
|
||||||
ACCESS_MASK AccessFlags;
|
ACCESS_MASK AccessFlags;
|
||||||
} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
|
} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
|
||||||
|
|
||||||
|
typedef struct _FILE_DISPOSITION_INFORMATION {
|
||||||
|
BOOLEAN DeleteFile;
|
||||||
|
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
|
||||||
|
|
||||||
typedef struct _FILE_POSITION_INFORMATION {
|
typedef struct _FILE_POSITION_INFORMATION {
|
||||||
LARGE_INTEGER CurrentByteOffset;
|
LARGE_INTEGER CurrentByteOffset;
|
||||||
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
|
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
|
||||||
|
|
|
@ -46,6 +46,8 @@ details. */
|
||||||
#include <lmcons.h> /* for UNLEN */
|
#include <lmcons.h> /* for UNLEN */
|
||||||
#include <rpc.h>
|
#include <rpc.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
#include <ntdef.h>
|
||||||
|
#include "ntdll.h"
|
||||||
|
|
||||||
#undef fstat
|
#undef fstat
|
||||||
#undef lstat
|
#undef lstat
|
||||||
|
@ -173,6 +175,89 @@ try_to_bin (const char *win32_path)
|
||||||
debug_printf ("SHFileOperation (%s) = %d\n", win32_path, ret);
|
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
|
extern "C" int
|
||||||
unlink (const char *ourname)
|
unlink (const char *ourname)
|
||||||
{
|
{
|
||||||
|
@ -211,7 +296,9 @@ unlink (const char *ourname)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setattrs;
|
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;
|
setattrs = false;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -219,100 +306,33 @@ unlink (const char *ourname)
|
||||||
setattrs = SetFileAttributes (win32_name,
|
setattrs = SetFileAttributes (win32_name,
|
||||||
(DWORD) win32_name
|
(DWORD) win32_name
|
||||||
& ~(FILE_ATTRIBUTE_READONLY
|
& ~(FILE_ATTRIBUTE_READONLY
|
||||||
| FILE_ATTRIBUTE_SYSTEM));
|
| FILE_ATTRIBUTE_SYSTEM
|
||||||
}
|
| FILE_ATTRIBUTE_HIDDEN));
|
||||||
/* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD lasterr;
|
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. */
|
||||||
/* Windows 9x seems to report ERROR_ACCESS_DENIED rather than sharing
|
if ((wincap.access_denied_on_delete () && lasterr == ERROR_ACCESS_DENIED
|
||||||
violation. So, set lasterr to ERROR_SHARING_VIOLATION in this case
|
&& !win32_name.isremote ())
|
||||||
to simplify tests. */
|
|| lasterr == ERROR_SHARING_VIOLATION)
|
||||||
if (wincap.access_denied_on_delete () && lasterr == ERROR_ACCESS_DENIED
|
{
|
||||||
&& !win32_name.isremote ())
|
/* Add file to the "to be deleted" queue. */
|
||||||
lasterr = ERROR_SHARING_VIOLATION;
|
syscall_printf ("Sharing violation, couldn't delete file");
|
||||||
|
user_shared->delqueue.queue_file (win32_name);
|
||||||
/* FILE_FLAGS_DELETE_ON_CLOSE was a bust. If this is a sharing
|
res = 0;
|
||||||
violation, then queue the file for deletion when the process
|
}
|
||||||
exits. Otherwise, punt. */
|
else
|
||||||
if (lasterr != ERROR_SHARING_VIOLATION)
|
__seterrno_from_win_error (lasterr);
|
||||||
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;
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
syscall_printf ("%d = unlink (%s)", res, ourname);
|
syscall_printf ("%d = unlink (%s)", res, ourname);
|
||||||
|
|
Loading…
Reference in New Issue