From 8884a1682ad6f67fa3473a3e0405ea2898e01863 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Wed, 1 Aug 2007 14:46:09 +0000 Subject: [PATCH] * ntdll.h (STATUS_ACCESS_DENIED): Define. * syscalls.cc (check_dir_not_empty): New static function. (unlink_nt): Move code checking for non-empty dir to check_dir_not_empty and call check_dir_not_empty instead. (rename): Add fault handler. Check oldpath and newpath for trailing . and .. path components and return EINVAL if so. Check oldpath for being on a vrtual file system. If renaming a dir fails with STATUS_ACCESS_DENIED, check if the target dir is non-empty and return ENOTEMPTY if so. --- winsup/cygwin/ChangeLog | 12 +++++ winsup/cygwin/ntdll.h | 1 + winsup/cygwin/syscalls.cc | 92 ++++++++++++++++++++++++++++----------- 3 files changed, 80 insertions(+), 25 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 3d3ea9b01..222d03f31 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,15 @@ +2007-08-01 Corinna Vinschen + + * ntdll.h (STATUS_ACCESS_DENIED): Define. + * syscalls.cc (check_dir_not_empty): New static function. + (unlink_nt): Move code checking for non-empty dir to check_dir_not_empty + and call check_dir_not_empty instead. + (rename): Add fault handler. Check oldpath and newpath for trailing + . and .. path components and return EINVAL if so. Check oldpath + for being on a vrtual file system. If renaming a dir fails with + STATUS_ACCESS_DENIED, check if the target dir is non-empty and return + ENOTEMPTY if so. + 2007-08-01 Corinna Vinschen * localtime.cc (tzsetwall): Don't set TZ. diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 8936dd44c..0eca36275 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -17,6 +17,7 @@ #define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xc000000d) #define STATUS_INVALID_DEVICE_REQUEST ((NTSTATUS) 0xc0000010) #define STATUS_NO_MEDIA_IN_DEVICE ((NTSTATUS) 0xc0000013) +#define STATUS_ACCESS_DENIED ((NTSTATUS) 0xc0000022) #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xc0000023) #define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS) 0xc0000034) #define STATUS_SHARING_VIOLATION ((NTSTATUS) 0xc0000043) diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 6e1f78185..1c202e33e 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -228,6 +228,38 @@ try_to_bin (path_conv &win32_path, HANDLE h) recycler, status); } +static NTSTATUS +check_dir_not_empty (HANDLE dir) +{ + IO_STATUS_BLOCK io; + const ULONG bufsiz = 3 * sizeof (FILE_NAMES_INFORMATION) + + 3 * NAME_MAX * sizeof (WCHAR); + PFILE_NAMES_INFORMATION pfni = (PFILE_NAMES_INFORMATION) + alloca (bufsiz); + NTSTATUS status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io, pfni, + bufsiz, FileNamesInformation, + FALSE, NULL, TRUE); + if (!NT_SUCCESS (status)) + { + syscall_printf ("Checking if directory is empty failed, " + "status = %p", status); + return status; + } + int cnt = 1; + while (pfni->NextEntryOffset) + { + pfni = (PFILE_NAMES_INFORMATION) + ((caddr_t) pfni + pfni->NextEntryOffset); + ++cnt; + } + if (cnt > 2) + { + syscall_printf ("Directory not empty"); + return STATUS_DIRECTORY_NOT_EMPTY; + } + return STATUS_SUCCESS; +} + NTSTATUS unlink_nt (path_conv &pc) { @@ -284,33 +316,12 @@ unlink_nt (path_conv &pc) 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 (fh, NULL, NULL, 0, &io, pfni, - bufsiz, FileNamesInformation, - FALSE, NULL, TRUE); + status = check_dir_not_empty (fh); if (!NT_SUCCESS (status)) { NtClose (fh); - syscall_printf ("Checking if directory is empty failed, " - "status = %p", status); return status; } - int cnt = 1; - while (pfni->NextEntryOffset) - { - pfni = (PFILE_NAMES_INFORMATION) - ((caddr_t) pfni + pfni->NextEntryOffset); - ++cnt; - } - if (cnt > 2) - { - NtClose (fh); - syscall_printf ("Directory not empty"); - return STATUS_DIRECTORY_NOT_EMPTY; - } } } } @@ -1347,7 +1358,18 @@ rename (const char *oldpath, const char *newpath) IO_STATUS_BLOCK io; ULONG size; PFILE_RENAME_INFORMATION pfri; - + + myfault efault; + if (efault.faulted (EFAULT)) + return -1; + + if (has_dot_last_component (oldpath, true) + || has_dot_last_component (newpath, true)) + { + set_errno (EINVAL); + goto out; + } + oldpc.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes); if (oldpc.error) { @@ -1359,6 +1381,11 @@ rename (const char *oldpath, const char *newpath) set_errno (ENOENT); goto out; } + if (oldpc.isspecial ()) /* No renames from virtual FS */ + { + set_errno (EROFS); + goto out; + } olen = strlen (oldpath); if (oldpc.known_suffix && (strcasematch (oldpath + olen - 4, ".lnk") @@ -1371,7 +1398,7 @@ rename (const char *oldpath, const char *newpath) set_errno (newpc.error); goto out; } - if (newpc.isspecial ()) /* No renames out of the FS */ + if (newpc.isspecial ()) /* No renames to virtual FSes */ { set_errno (EROFS); goto out; @@ -1509,7 +1536,22 @@ rename (const char *oldpath, const char *newpath) res = 0; } else - __seterrno_from_nt_status (status); + { + /* Check in case of STATUS_ACCESS_DENIED and pc.isdir(), + whether we tried to rename to an existing non-empty dir. + In this case we have to set errno to EEXIST. */ + if (status == STATUS_ACCESS_DENIED && dstpc->isdir () + && NT_SUCCESS (NtOpenFile (&fh, FILE_LIST_DIRECTORY | SYNCHRONIZE, + dstpc->get_object_attr (attr, sec_none_nih), + &io, FILE_SHARE_VALID_FLAGS, + FILE_OPEN_FOR_BACKUP_INTENT + | FILE_SYNCHRONOUS_IO_NONALERT))) + { + status = check_dir_not_empty (fh); + NtClose (fh); + } + __seterrno_from_nt_status (status); + } NtClose (fh); out: