From 788e7da136e19ec6e3e5d40872eb728677f9a4cc Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 2 Mar 2007 20:04:26 +0000 Subject: [PATCH] * ntdll.h (struct _OBJECT_NAME_INFORMATION): Define. * syscalls.cc (unlink_nt): Check shared directory for being empty before trying to move and mark for deletion. --- winsup/cygwin/ChangeLog | 6 +++++ winsup/cygwin/ntdll.h | 8 ++++++ winsup/cygwin/syscalls.cc | 53 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 12a48d1c4..0d5144b1f 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,9 @@ +2007-03-02 Corinna Vinschen + + * ntdll.h (struct _OBJECT_NAME_INFORMATION): Define. + * syscalls.cc (unlink_nt): Check shared directory for being empty + before trying to move and mark for deletion. + 2007-03-02 Corinna Vinschen * security.cc (get_file_attribute): Avoid compiler warning. diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 4fc5da599..0bbb5a521 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -79,6 +79,14 @@ typedef enum _FILE_INFORMATION_CLASS FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; +typedef struct _FILE_NAMES_INFORMATION +{ + ULONG NextEntryOffset; + ULONG FileIndex; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION; + typedef struct _FILE_BOTH_DIR_INFORMATION { ULONG NextEntryOffset; diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 30a67b876..e564eddc5 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -283,8 +283,57 @@ unlink_nt (path_conv &win32_name, bool setattrs) if (status == STATUS_SHARING_VIOLATION) { move_to_bin = true; - status = NtOpenFile (&h, DELETE, &attr, &io, FILE_SHARE_VALID_FLAGS, - flags); + if (!win32_name.isdir () || win32_name.isremote ()) + status = NtOpenFile (&h, DELETE, &attr, &io, FILE_SHARE_VALID_FLAGS, + flags); + else + { + /* It's getting tricky. The directory is opened in some process, + so we're supposed to move it to the recycler and mark it for + deletion. But what if the directory is not empty? The move + will work, but the subsequent delete will fail. So we would + have to move it back. That's bad, because the directory would + be moved around which results in a temporary inconsistent state. + So, what we do here is to test if the directory is empty. If + not, we bail out with ERROR_DIR_NOT_EMTPY. The below code + tests for at least three entries in the directory, ".", "..", + 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, + &attr, &io, FILE_SHARE_VALID_FLAGS, + flags | FILE_SYNCHRONOUS_IO_NONALERT); + if (NT_SUCCESS (status)) + { + const ULONG bufsiz = 3 * sizeof (FILE_NAMES_INFORMATION) + + 3 * NAME_MAX * sizeof (WCHAR); + PFILE_NAMES_INFORMATION pfni = (PFILE_NAMES_INFORMATION) + alloca (bufsiz); + status = NtQueryDirectoryFile (h, NULL, NULL, 0, &io, pfni, + bufsiz, FileNamesInformation, + FALSE, NULL, TRUE); + if (!NT_SUCCESS (status)) + { + NtClose (h); + syscall_printf ("Checking if directory is empty failed, " + "status = %p", status); + return RtlNtStatusToDosError (status); + } + int cnt = 1; + while (pfni->NextEntryOffset) + { + pfni = (PFILE_NAMES_INFORMATION) + ((caddr_t) pfni + pfni->NextEntryOffset); + ++cnt; + } + if (cnt > 2) + { + NtClose (h); + syscall_printf ("Directory not empty"); + return ERROR_DIR_NOT_EMPTY; + } + } + } } if (!NT_SUCCESS (status)) {