diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 31c18454f0e..1d0682a6334 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 06d1d81bdb0..7ea8ac273e5 100644 --- a/server/fd.c +++ b/server/fd.c @@ -23,6 +23,7 @@ #include "wine/port.h" #include +#include #include #include #include @@ -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;