Module: wine Branch: master Commit: a302ab44acaf72ecc9b0307c82a7d11f759e6a72 URL: https://source.winehq.org/git/wine.git/?a=commit;h=a302ab44acaf72ecc9b0307c8...
Author: Daniel Lehman dlehman25@gmail.com Date: Thu May 28 20:28:09 2020 -0700
server: Fail if non-empty directory marked for deletion.
Signed-off-by: Daniel Lehman dlehman25@gmail.com Signed-off-by: Alexandre Julliard julliard@winehq.org
---
dlls/ntdll/tests/file.c | 3 -- server/fd.c | 74 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 57 insertions(+), 20 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..7ea8ac273e 100644 --- a/server/fd.c +++ b/server/fd.c @@ -23,6 +23,7 @@ #include "wine/port.h"
#include <assert.h> +#include <dirent.h> #include <errno.h> #include <fcntl.h> #include <limits.h> @@ -2364,6 +2365,31 @@ static struct fd *get_handle_fd_obj( struct process *process, obj_handle_t handl return fd; }
+static int is_dir_empty( int fd ) +{ + DIR *dir; + int empty; + struct dirent *de; + + if ((fd = dup( fd )) == -1) + return -1; + + if (!(dir = fdopendir( fd ))) + { + close( fd ); + return -1; + } + + empty = 1; + while (empty && (de = readdir( dir ))) + { + if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue; + empty = 0; + } + closedir( dir ); + return empty; +} + /* set disposition for the fd */ static void set_fd_disposition( struct fd *fd, int unlink ) { @@ -2381,24 +2407,38 @@ static void set_fd_disposition( struct fd *fd, int unlink ) return; }
- if (fstat( fd->unix_fd, &st ) == -1) + if (unlink) { - file_set_error(); - return; - } - - /* can't unlink special files */ - if (unlink && !S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) - { - set_error( STATUS_INVALID_PARAMETER ); - return; - } - - /* can't unlink files we don't have permission to write */ - if (unlink && !(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) && !S_ISDIR(st.st_mode)) - { - set_error( STATUS_CANNOT_DELETE ); - return; + if (fstat( fd->unix_fd, &st ) == -1) + { + file_set_error(); + return; + } + if (S_ISREG( st.st_mode )) /* can't unlink files we don't have permission to write */ + { + if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) + { + set_error( STATUS_CANNOT_DELETE ); + return; + } + } + else if (S_ISDIR( st.st_mode )) /* can't remove non-empty directories */ + { + switch (is_dir_empty( fd->unix_fd )) + { + case -1: + file_set_error(); + return; + case 0: + set_error( STATUS_DIRECTORY_NOT_EMPTY ); + return; + } + } + else /* can't unlink special files */ + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } }
fd->closed->unlink = unlink ? 1 : 0;