* 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:
		@@ -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);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user