Signed-off-by: Daniel Lehman dlehman25@gmail.com
v2: use getdents instead of open/closedir
needed by std::filesystem::remove_all, which removes files with: - CreateFileW - SetFileInformationByHandle(FileDispositionInfoEx) // currently unimplemented - SetFileInformationByHandle(FileDispositionInfo) - DeleteFile = TRUE - CloseHandle --- dlls/ntdll/tests/file.c | 3 --- server/fd.c | 57 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 31c18454f0..1d0682a633 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3185,17 +3185,14 @@ todo_wine CloseHandle( handle2 ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_DIRECTORY_NOT_EMPTY, "unexpected FileDispositionInformation result (expected STATUS_DIRECTORY_NOT_EMPTY, got %x)\n", res ); fileDeleted = DeleteFileA( buffer ); ok( fileDeleted, "File should have been deleted\n" ); buffer[dirpos] = '\0'; CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine ok( !fileDeleted, "Directory shouldn't have been deleted\n" ); fileDeleted = RemoveDirectoryA( buffer ); -todo_wine ok( fileDeleted, "Directory should have been deleted\n" ); }
diff --git a/server/fd.c b/server/fd.c index 06d1d81bdb..178bbccfc7 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2364,6 +2364,56 @@ static struct fd *get_handle_fd_obj( struct process *process, obj_handle_t handl return fd; }
+#if defined(linux) && defined(SYS_getdents64) +typedef struct +{ + uint64_t d_ino; + int64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +} KERNEL_DIRENT64; + +static inline int getdents64( int fd, char *de, unsigned int size ) +{ + return syscall( SYS_getdents64, fd, de, size ); +} +#define USE_GETDENTS +#endif + +static int is_dir_empty(int fd) +{ +#ifdef USE_GETDENTS + char *name, buffer[8192]; + KERNEL_DIRENT64 *de; + off_t old_pos; + int res; + + de = (KERNEL_DIRENT64 *)buffer; + if ((old_pos = lseek( fd, 0, SEEK_CUR )) == -1) + return 0; + + lseek( fd, 0, SEEK_SET ); + res = getdents64( fd, buffer, sizeof(buffer) ); + lseek( fd, old_pos, SEEK_SET ); + if (res == -1) + return 0; + + while (res > 0) + { + res -= de->d_reclen; + name = de->d_name; + de = (KERNEL_DIRENT64 *)((char *)de + de->d_reclen); + if (!strcmp( name, "." ) || !strcmp( name, ".." )) + continue; + return 0; + } + return 1; +#else + return 1; +#endif +} + /* set disposition for the fd */ static void set_fd_disposition( struct fd *fd, int unlink ) { @@ -2401,6 +2451,13 @@ static void set_fd_disposition( struct fd *fd, int unlink ) return; }
+ /* can't remove non-empty directories */ + if (unlink && S_ISDIR(st.st_mode) && !is_dir_empty(fd->unix_fd)) + { + set_error( STATUS_DIRECTORY_NOT_EMPTY ); + return; + } + fd->closed->unlink = unlink ? 1 : 0; if (fd->options & FILE_DELETE_ON_CLOSE) fd->closed->unlink = -1;