[PATCH v2] server: Fail if non-empty directory marked for deletion.

Daniel Lehman dlehman25 at gmail.com
Mon May 25 22:56:21 CDT 2020


Signed-off-by: Daniel Lehman <dlehman25 at 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;
-- 
2.17.1




More information about the wine-devel mailing list