* fhandler_disk_file.cc (fhandler_disk_file::link): Use FILE_ANY_ACCESS.

(fhandler_base::utimes_fs): Fix white space.
	(fhandler_disk_file::lock): Remove 9x blurb from comment.
	(fhandler_disk_file::mkdir): Use NtCreateFile/NtClose instead of
	CreateDirectoryA.
	(fhandler_disk_file::rmdir): Accommodate changes to unlink_nt.
	Simplify post-delete SMB-related tests.  Use NtQueryAttributesFile
	instead of GetFileAttributes.
	* ntdll.h (STATUS_DIRECTORY_NOT_EMPTY): Define.
	(NtQueryAttributesFile): Declare.
	* syscalls.cc (unlink_nt): Return NTSTATUS.  Drop setattrs parameter.
	Never use FILE_DELETE_ON_CLOSE, always use
	NtSetInformationFile(FileDispositionInformation) instead.
	Check for R/O attributes and open file with FILE_WRITE_ATTRIBUTES
	access if any of them are set.  Remove R/O attributes before
	marking for delete if necessary.  Revert them afterwards if necessary.
	(unlink): Accommodate changes to unlink_nt.
This commit is contained in:
Corinna Vinschen 2007-07-29 12:27:22 +00:00
parent 4368984a7b
commit ed7ec849f6
4 changed files with 138 additions and 137 deletions

View File

@ -1,3 +1,23 @@
2007-07-29 Corinna Vinschen <corinna@vinschen.de>
* fhandler_disk_file.cc (fhandler_disk_file::link): Use FILE_ANY_ACCESS.
(fhandler_base::utimes_fs): Fix white space.
(fhandler_disk_file::lock): Remove 9x blurb from comment.
(fhandler_disk_file::mkdir): Use NtCreateFile/NtClose instead of
CreateDirectoryA.
(fhandler_disk_file::rmdir): Accommodate changes to unlink_nt.
Simplify post-delete SMB-related tests. Use NtQueryAttributesFile
instead of GetFileAttributes.
* ntdll.h (STATUS_DIRECTORY_NOT_EMPTY): Define.
(NtQueryAttributesFile): Declare.
* syscalls.cc (unlink_nt): Return NTSTATUS. Drop setattrs parameter.
Never use FILE_DELETE_ON_CLOSE, always use
NtSetInformationFile(FileDispositionInformation) instead.
Check for R/O attributes and open file with FILE_WRITE_ATTRIBUTES
access if any of them are set. Remove R/O attributes before
marking for delete if necessary. Revert them afterwards if necessary.
(unlink): Accommodate changes to unlink_nt.
2007-07-29 Corinna Vinschen <corinna@vinschen.de>
* fhandler_disk_file.cc: Use get_handle throughout.

View File

@ -1094,7 +1094,8 @@ fhandler_disk_file::link (const char *newpath)
NTSTATUS status;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
status = NtOpenFile (&fh, 0, pc.get_object_attr (attr, sec_none_nih), &io,
status = NtOpenFile (&fh, FILE_ANY_ACCESS,
pc.get_object_attr (attr, sec_none_nih), &io,
FILE_SHARE_VALID_FLAGS,
FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
if (!NT_SUCCESS (status))
@ -1182,7 +1183,7 @@ fhandler_base::utimes_fs (const struct timeval *tvp)
IO_STATUS_BLOCK io;
FILE_BASIC_INFORMATION fbi;
fbi.CreationTime.QuadPart = 0LL;
fbi.LastAccessTime= lastaccess;
fbi.LastAccessTime = lastaccess;
fbi.LastWriteTime = lastwrite;
fbi.ChangeTime.QuadPart = 0LL;
fbi.FileAttributes = 0;
@ -1318,11 +1319,7 @@ fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
/* FIXME: The correct way to do this to get POSIX locking semantics is to
keep a linked list of posix lock requests and map them into Win32 locks.
he problem is that Win32 does not deal correctly with overlapping lock
requests. Also another pain is that Win95 doesn't do non-blocking or
non-exclusive locks at all. For '95 just convert all lock requests into
blocking,exclusive locks. This shouldn't break many apps but denying all
locking would. For now just convert to Win32 locks and hope for
the best. */
requests. */
int
fhandler_disk_file::lock (int cmd, struct __flock64 *fl)
@ -1471,17 +1468,31 @@ fhandler_disk_file::mkdir (mode_t mode)
set_security_attribute (S_IFDIR | ((mode & 07777) & ~cygheap->umask),
&sa, sd);
if (CreateDirectoryA (get_win32_name (), &sa))
{
NTSTATUS status;
HANDLE dir;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
ULONG fattr = FILE_ATTRIBUTE_DIRECTORY;
#ifdef HIDDEN_DOT_FILES
char *c = strrchr (real_dir.get_win32 (), '\\');
if ((c && c[1] == '.') || *get_win32_name () == '.')
SetFileAttributes (get_win32_name (), FILE_ATTRIBUTE_HIDDEN);
UNICODE_STRING basename;
RtlSplitUnicodePath (pc.get_nt_native_path (), NULL, &basename);
if (basename.Buffer[0] == L'.')
fattr |= FILE_ATTRIBUTE_HIDDEN;
#endif
status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
pc.get_object_attr (attr, sa), &io, NULL,
fattr, FILE_SHARE_VALID_FLAGS, FILE_CREATE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_BACKUP_INTENT,
NULL, 0);
if (NT_SUCCESS (status))
{
NtClose (dir);
res = 0;
}
else
__seterrno ();
__seterrno_from_nt_status (status);
return res;
}
@ -1489,47 +1500,43 @@ fhandler_disk_file::mkdir (mode_t mode)
int
fhandler_disk_file::rmdir ()
{
extern DWORD unlink_nt (path_conv &win32_name, bool setattrs);
int res = -1;
extern NTSTATUS unlink_nt (path_conv &pc);
if (!pc.isdir ())
{
set_errno (ENOTDIR);
return -1;
}
/* Even own directories can't be removed if R/O attribute is set. */
if (pc.has_attribute (FILE_ATTRIBUTE_READONLY))
SetFileAttributes (get_win32_name (),
(DWORD) pc & ~FILE_ATTRIBUTE_READONLY);
DWORD err, att = 0;
int rc = !(err = unlink_nt (pc, pc.has_attribute (FILE_ATTRIBUTE_READONLY)));
if (err)
SetLastError (err);
if (isremote () && exists ())
att = GetFileAttributes (get_win32_name ());
/* Sometimes smb indicates failure when it really succeeds, so check for
this case specifically. */
if (rc || att == INVALID_FILE_ATTRIBUTES)
if (!pc.exists ())
{
/* RemoveDirectory on a samba drive doesn't return an error if the
directory can't be removed because it's not empty. Checking for
existence afterwards keeps us informed about success. */
if (!isremote () || att == INVALID_FILE_ATTRIBUTES)
return 0;
err = ERROR_DIR_NOT_EMPTY;
set_errno (ENOENT);
return -1;
}
else
err = GetLastError ();
__seterrno_from_win_error (err);
NTSTATUS status = unlink_nt (pc);
return res;
/* Check for existence of remote dirs after trying to delete them.
Two reasons:
- Sometimes SMB indicates failure when it really succeeds.
- Removeing a directory on a samba drive doesn't return an error if the
directory can't be removed because it's not empty. */
if (isremote ())
{
OBJECT_ATTRIBUTES attr;
FILE_BASIC_INFORMATION fbi;
if (NT_SUCCESS (NtQueryAttributesFile
(pc.get_object_attr (attr, sec_none_nih), &fbi)))
status = STATUS_DIRECTORY_NOT_EMPTY;
else
status = STATUS_SUCCESS;
}
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
return -1;
}
return 0;
}
/* This is the minimal number of entries which fit into the readdir cache.

View File

@ -21,6 +21,7 @@
#define STATUS_DELETE_PENDING ((NTSTATUS) 0xc0000056)
#define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xc00000a1)
#define STATUS_NOT_SUPPORTED ((NTSTATUS) 0xc00000bb)
#define STATUS_DIRECTORY_NOT_EMPTY ((NTSTATUS) 0xc0000101)
#define STATUS_NOT_ALL_ASSIGNED ((NTSTATUS) 0x00000106)
#define STATUS_INVALID_LEVEL ((NTSTATUS) 0xc0000148)
#define STATUS_NO_MORE_FILES ((NTSTATUS) 0x80000006)
@ -752,6 +753,8 @@ extern "C"
BOOLEAN, PULONG, PULONG);
NTSTATUS NTAPI NtQueryEaFile (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG,
BOOLEAN, PVOID, ULONG, PULONG, BOOLEAN);
NTSTATUS NTAPI NtQueryAttributesFile (POBJECT_ATTRIBUTES,
PFILE_BASIC_INFORMATION);
NTSTATUS NTAPI NtQueryFullAttributesFile (POBJECT_ATTRIBUTES,
PFILE_NETWORK_OPEN_INFORMATION);
NTSTATUS NTAPI NtQueryInformationFile (HANDLE, PIO_STATUS_BLOCK, PVOID,

View File

@ -228,43 +228,31 @@ try_to_bin (path_conv &win32_path, HANDLE h)
recycler, status);
}
DWORD
unlink_nt (path_conv &win32_name, bool setattrs)
NTSTATUS
unlink_nt (path_conv &pc)
{
NTSTATUS status;
HANDLE fh;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
NTSTATUS status;
HANDLE h;
FILE_BASIC_INFORMATION fbi;
ACCESS_MASK access = DELETE;
/* If one of the R/O attributes is set, we have to open the file with
FILE_WRITE_ATTRIBUTES to be able to remove these flags before trying
to delete it. */
if (pc.file_attributes () & (FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN))
access |= FILE_WRITE_ATTRIBUTES;
ULONG flags = FILE_OPEN_FOR_BACKUP_INTENT;
/* Don't open directories with "delete on close", because the NT internal
semantic is apparently different from the file semantic. If a directory
is opened "delete on close", the rename operation in try_to_bin fails
with STATUS_ACCESS_DENIED. So directories must be deleted using
NtSetInformationFile, class FileDispositionInformation, which works fine.
Correction, moving a directory opened with delete-on-close fails ONLY
on XP. Note to myself: Never take anything for granted on Windows!
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 removed 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 NtSetInformationFile, class
FileDispositionInformation, 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.isdir () && !win32_name.isremote ())
flags |= FILE_DELETE_ON_CLOSE;
/* Add the reparse point flag to native symlinks, otherwise we remove the
target, not the symlink. */
if (win32_name.is_rep_symlink ())
if (pc.is_rep_symlink ())
flags |= FILE_OPEN_REPARSE_POINT;
win32_name.get_object_attr (attr, sec_none_nih);
pc.get_object_attr (attr, sec_none_nih);
/* First try to open the file with sharing not allowed. If the file
has an open handle on it, this will fail. That indicates that the
file has to be moved to the recycle bin so that it actually disappears
@ -272,13 +260,13 @@ unlink_nt (path_conv &win32_name, bool setattrs)
doesn't fail, the file is not in use and by simply closing the handle
the file will disappear. */
bool move_to_bin = false;
status = NtOpenFile (&h, DELETE, &attr, &io, 0, flags);
status = NtOpenFile (&fh, access, &attr, &io, 0, flags);
if (status == STATUS_SHARING_VIOLATION)
{
move_to_bin = true;
if (!win32_name.isdir () || win32_name.isremote ())
status = NtOpenFile (&h, DELETE, &attr, &io, FILE_SHARE_VALID_FLAGS,
flags);
if (!pc.isdir () || pc.isremote ())
status = NtOpenFile (&fh, access, &attr, &io,
FILE_SHARE_VALID_FLAGS, flags);
else
{
/* It's getting tricky. The directory is opened in some process,
@ -293,7 +281,7 @@ unlink_nt (path_conv &win32_name, bool setattrs)
and another one. Three entries means, not empty. This doesn't
work for the root directory of a drive, but the root dir can
neither be deleted, nor moved anyway. */
status = NtOpenFile (&h, DELETE | SYNCHRONIZE | FILE_LIST_DIRECTORY,
status = NtOpenFile (&fh, access | FILE_LIST_DIRECTORY | SYNCHRONIZE,
&attr, &io, FILE_SHARE_VALID_FLAGS,
flags | FILE_SYNCHRONOUS_IO_NONALERT);
if (NT_SUCCESS (status))
@ -302,15 +290,15 @@ unlink_nt (path_conv &win32_name, bool setattrs)
+ 3 * NAME_MAX * sizeof (WCHAR);
PFILE_NAMES_INFORMATION pfni = (PFILE_NAMES_INFORMATION)
alloca (bufsiz);
status = NtQueryDirectoryFile (h, NULL, NULL, 0, &io, pfni,
status = NtQueryDirectoryFile (fh, NULL, NULL, 0, &io, pfni,
bufsiz, FileNamesInformation,
FALSE, NULL, TRUE);
if (!NT_SUCCESS (status))
{
NtClose (h);
NtClose (fh);
syscall_printf ("Checking if directory is empty failed, "
"status = %p", status);
return RtlNtStatusToDosError (status);
return status;
}
int cnt = 1;
while (pfni->NextEntryOffset)
@ -321,9 +309,9 @@ unlink_nt (path_conv &win32_name, bool setattrs)
}
if (cnt > 2)
{
NtClose (h);
NtClose (fh);
syscall_printf ("Directory not empty");
return ERROR_DIR_NOT_EMPTY;
return STATUS_DIRECTORY_NOT_EMPTY;
}
}
}
@ -332,46 +320,46 @@ unlink_nt (path_conv &win32_name, bool setattrs)
{
if (status == STATUS_DELETE_PENDING)
{
syscall_printf ("Delete already pending, status = %p", status);
syscall_printf ("Delete already pending");
return 0;
}
syscall_printf ("Opening file for delete failed, status = %p", status);
return RtlNtStatusToDosError (status);
return status;
}
if (setattrs)
SetFileAttributes (win32_name, (DWORD) win32_name);
if (move_to_bin && !pc.isremote ())
try_to_bin (pc, fh);
if (move_to_bin && !win32_name.isremote ())
try_to_bin (win32_name, h);
DWORD lasterr = 0;
if (win32_name.isdir () || win32_name.isremote ())
/* Get rid of read-only attributes. */
if (access & FILE_WRITE_ATTRIBUTES)
{
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);
FILE_BASIC_INFORMATION fbi;
fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart =
fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
fbi.FileAttributes = pc.file_attributes ()
& ~(FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN);
NtSetInformationFile (fh, &io, &fbi, sizeof fbi, FileBasicInformation);
}
FILE_DISPOSITION_INFORMATION disp = { TRUE };
status = NtSetInformationFile (fh, &io, &disp, sizeof disp,
FileDispositionInformation);
if (!NT_SUCCESS (status))
{
syscall_printf ("Setting delete disposition failed, status = %p", status);
/* Restore R/O attributes. */
if (access & FILE_WRITE_ATTRIBUTES)
{
fbi.FileAttributes = pc.file_attributes ();
NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
FileBasicInformation);
}
}
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)
lasterr = RtlNtStatusToDosError (status);
}
syscall_printf ("Deleting succeeded");
return lasterr;
NtClose (fh);
return status;
}
extern "C" int
@ -379,7 +367,7 @@ unlink (const char *ourname)
{
int res = -1;
DWORD devn;
DWORD lasterr;
NTSTATUS status;
path_conv win32_name (ourname, PC_SYM_NOFOLLOW,
transparent_exe ? stat_suffixes : NULL);
@ -412,30 +400,13 @@ unlink (const char *ourname)
goto done;
}
bool setattrs;
if (!((DWORD) win32_name & (FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN)))
setattrs = false;
else
{
/* Allow us to delete even if read-only */
setattrs = SetFileAttributes (win32_name,
(DWORD) win32_name
& ~(FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN));
}
lasterr = unlink_nt (win32_name, setattrs);
if (!lasterr)
status = unlink_nt (win32_name);
if (NT_SUCCESS (status))
res = 0;
else
{
SetFileAttributes (win32_name, (DWORD) win32_name);
/* FIXME: Can we get rid of the delqueue now? */
if (lasterr == ERROR_SHARING_VIOLATION)
if (status == STATUS_SHARING_VIOLATION)
{
/* Add file to the "to be deleted" queue. */
syscall_printf ("Sharing violation, couldn't delete file");
@ -443,7 +414,7 @@ unlink (const char *ourname)
res = 0;
}
else
__seterrno_from_win_error (lasterr);
__seterrno_from_nt_status (status);
}
done:
@ -1467,7 +1438,7 @@ rename (const char *oldpath, const char *newpath)
else if (MoveFileEx (real_old.get_win32 (), real_new.get_win32 (),
MOVEFILE_REPLACE_EXISTING))
res = 0;
else if ((lasterr = unlink_nt (real_new, false)))
else if ((lasterr = unlink_nt (real_new)))
{
SetLastError (lasterr);
syscall_printf ("Can't remove target file/dir, %E");
@ -1503,7 +1474,7 @@ done:
if (lnk_suffix)
{
*lnk_suffix = '.';
unlink_nt (real_new, false);
unlink_nt (real_new);
}
/* Shortcut hack, No. 3, part 2 */
/* If a file with the given name exists, it must be deleted after the
@ -1515,7 +1486,7 @@ done:
{
lnk_suffix = strrchr (real_new.get_win32 (), '.');
*lnk_suffix = '\0';
unlink_nt (real_new, false);
unlink_nt (real_new);
}
}