From 18b484d7787d542999b594002832f8de25c099d0 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Fri, 8 Aug 2014 14:39:42 -0600 Subject: [PATCH] Added patch to support setting file disposition information. --- README.md | 1 + debian/changelog | 3 +- patches/Makefile | 22 ++ ...inter-to-parent-s-fd-unix_name-in-th.patch | 168 +++++++++++++ ...rt-for-setting-file-disposition-info.patch | 233 ++++++++++++++++++ ...rmit-FileDispositionInformation-to-d.patch | 78 ++++++ .../definition | 4 + 7 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 patches/ntdll-FileDispositionInformation/0001-server-Keep-a-pointer-to-parent-s-fd-unix_name-in-th.patch create mode 100644 patches/ntdll-FileDispositionInformation/0002-server-Add-support-for-setting-file-disposition-info.patch create mode 100644 patches/ntdll-FileDispositionInformation/0003-server-Do-not-permit-FileDispositionInformation-to-d.patch create mode 100644 patches/ntdll-FileDispositionInformation/definition diff --git a/README.md b/README.md index 4ea5e2ef..48cd1095 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Wine-Compholio contains fixes for the following Wine bugs: * Support for AllocateAndGetTcpExTableFromStack ([Wine Bug #34372](http://bugs.winehq.org/show_bug.cgi?id=34372 "Add missing function AllocateAndGetTcpExTableFromStack() to iphlpapi.dll")) * Support for GetSystemTimes ([Wine Bug #19813](http://bugs.winehq.org/show_bug.cgi?id=19813 "Voddler needs GetSystemTimes to run")) * Support for Junction Points ([Wine Bug #12401](http://bugs.winehq.org/show_bug.cgi?id=12401 "Support junction points, i.e. DeviceIoCtl(FSCTL_SET_REPARSE_POINT/FSCTL_GET_REPARSE_POINT)")) +* Support for NtSetInformationFile class FileDispositionInformation ([Wine Bug #30397](http://bugs.winehq.org/show_bug.cgi?id=30397 "Multiple applications need support for NtSetInformationFile class FileDispositionInformation (Cygwin installer, Stylizer 5.x Visual CSS editor, Spoon Studio 2011 (ex Xenocode) application sandboxing scheme)")) * Support for PulseAudio backend for audio ([Wine Bug #10495](http://bugs.winehq.org/show_bug.cgi?id=10495 "Wine should support PulseAudio")) * Support for TOOLTIPS_GetTipText edge cases ([Wine Bug #30648](http://bugs.winehq.org/show_bug.cgi?id=30648 "SEGA Genesis / Mega Drive Classic Collection (Steam) crashes on startup")) * Support for TransmitFile ([Wine Bug #5048](http://bugs.winehq.org/show_bug.cgi?id=5048 "Multiple applications and games need support for ws2_32 SIO_GET_EXTENSION_FUNCTION_POINTER TransmitFile (WSAID_TRANSMITFILE)")) diff --git a/debian/changelog b/debian/changelog index f289fd0f..527d387d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,6 +8,7 @@ wine-compholio (1.7.24) UNRELEASED; urgency=low * Added patch to allow special characters in pipe names. * Added patch with stubs for [Get|Set]SystemFileCacheSize. * Added patch to implement AllocateAndGetTcpExTableFromStack. + * Added patch to support setting file disposition information. * Added patch to fix ConnectNamedPort return value in overlapped mode. * Added patch to store IOCS data in a property instead of GWLP_USERDATA. * Added patch to return empty D3D hardware flags for HEL device enumeration. @@ -21,7 +22,7 @@ wine-compholio (1.7.24) UNRELEASED; urgency=low * Removed patch to create Vista directories (accepted upstream). * Removed strmbase/quartz locking fix patches (accepted upstream). * Removed windowscodecs/PropertyBag patch (accepted upstream). - -- Erich E. Hoover Thu, 07 Aug 2014 17:31:56 -0600 + -- Erich E. Hoover Fri, 08 Aug 2014 14:39:15 -0600 wine-compholio (1.7.23) unstable; urgency=low * Rewrite of patch system to simplify maintaining large patchsets. diff --git a/patches/Makefile b/patches/Makefile index a16055b6..571f52ea 100644 --- a/patches/Makefile +++ b/patches/Makefile @@ -20,6 +20,7 @@ PATCHLIST := Miscellaneous.ok \ loader-Cmdline_Diagnostics.ok \ ntdll-Dynamic_DST.ok \ ntdll-FD_Cache.ok \ + ntdll-FileDispositionInformation.ok \ ntdll-Junction_Points.ok \ ntdll-Pipe_SpecialCharacters.ok \ ntdll-loader_EntryPoint.ok \ @@ -348,6 +349,27 @@ ntdll-FD_Cache.ok: echo '+ { "ntdll-FD_Cache", "Sebastian Lackner", "Use lockfree implementation for get_cached_fd. [rev 4]" },'; \ ) > ntdll-FD_Cache.ok +# Patchset ntdll-FileDispositionInformation +# | +# | Included patches: +# | * Add support for setting file disposition information. [by Dmitry Timoshkov / Erich E. Hoover] +# | +# | This patchset fixes the following Wine bugs: +# | * [#30397] Multiple applications need support for NtSetInformationFile class FileDispositionInformation (Cygwin installer, +# | Stylizer 5.x Visual CSS editor, Spoon Studio 2011 (ex Xenocode) application sandboxing scheme) +# | +# | Modified files: +# | * dlls/ntdll/file.c, dlls/ntdll/tests/file.c, server/fd.c, server/file.c, server/file.h, server/protocol.def +# | +.INTERMEDIATE: ntdll-FileDispositionInformation.ok +ntdll-FileDispositionInformation.ok: + $(PATCH) < ntdll-FileDispositionInformation/0001-server-Keep-a-pointer-to-parent-s-fd-unix_name-in-th.patch + $(PATCH) < ntdll-FileDispositionInformation/0002-server-Add-support-for-setting-file-disposition-info.patch + $(PATCH) < ntdll-FileDispositionInformation/0003-server-Do-not-permit-FileDispositionInformation-to-d.patch + @( \ + echo '+ { "ntdll-FileDispositionInformation", "Dmitry Timoshkov / Erich E. Hoover", "Add support for setting file disposition information." },'; \ + ) > ntdll-FileDispositionInformation.ok + # Patchset ntdll-Junction_Points # | # | Included patches: diff --git a/patches/ntdll-FileDispositionInformation/0001-server-Keep-a-pointer-to-parent-s-fd-unix_name-in-th.patch b/patches/ntdll-FileDispositionInformation/0001-server-Keep-a-pointer-to-parent-s-fd-unix_name-in-th.patch new file mode 100644 index 00000000..318fdc7c --- /dev/null +++ b/patches/ntdll-FileDispositionInformation/0001-server-Keep-a-pointer-to-parent-s-fd-unix_name-in-th.patch @@ -0,0 +1,168 @@ +From 35cc6b5ed224e27ef833bb84cea092bb65b8ab12 Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Thu, 7 Aug 2014 18:31:33 -0600 +Subject: server: Keep a pointer to parent's fd unix_name in the closed_fd + structure. + +--- + server/fd.c | 38 +++++++++++++++++++++----------------- + 1 file changed, 21 insertions(+), 17 deletions(-) + +diff --git a/server/fd.c b/server/fd.c +index e3b722c..b022a3c 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -164,7 +164,8 @@ struct closed_fd + { + struct list entry; /* entry in inode closed list */ + int unix_fd; /* the unix file descriptor */ +- char unlink[1]; /* name to unlink on close (if any) */ ++ int unlink; /* whether to unlink on close */ ++ char *unix_name; /* name to unlink on close, points to parent fd unix_name */ + }; + + struct fd +@@ -1016,9 +1017,10 @@ static void inode_close_pending( struct inode *inode, int keep_unlinks ) + close( fd->unix_fd ); + fd->unix_fd = -1; + } +- if (!keep_unlinks || !fd->unlink[0]) /* get rid of it unless there's an unlink pending on that file */ ++ if (!keep_unlinks || !fd->unlink) /* get rid of it unless there's an unlink pending on that file */ + { + list_remove( ptr ); ++ free( fd->unix_name ); + free( fd ); + } + ptr = next; +@@ -1048,16 +1050,17 @@ static void inode_destroy( struct object *obj ) + struct closed_fd *fd = LIST_ENTRY( ptr, struct closed_fd, entry ); + list_remove( ptr ); + if (fd->unix_fd != -1) close( fd->unix_fd ); +- if (fd->unlink[0]) ++ if (fd->unlink) + { + /* make sure it is still the same file */ + struct stat st; +- if (!stat( fd->unlink, &st ) && st.st_dev == inode->device->dev && st.st_ino == inode->ino) ++ if (!stat( fd->unix_name, &st ) && st.st_dev == inode->device->dev && st.st_ino == inode->ino) + { +- if (S_ISDIR(st.st_mode)) rmdir( fd->unlink ); +- else unlink( fd->unlink ); ++ if (S_ISDIR(st.st_mode)) rmdir( fd->unix_name ); ++ else unlink( fd->unix_name ); + } + } ++ free( fd->unix_name ); + free( fd ); + } + release_object( inode->device ); +@@ -1103,7 +1106,7 @@ static void inode_add_closed_fd( struct inode *inode, struct closed_fd *fd ) + { + list_add_head( &inode->closed, &fd->entry ); + } +- else if (fd->unlink[0]) /* close the fd but keep the structure around for unlink */ ++ else if (fd->unlink) /* close the fd but keep the structure around for unlink */ + { + if (fd->unix_fd != -1) close( fd->unix_fd ); + fd->unix_fd = -1; +@@ -1112,6 +1115,7 @@ static void inode_add_closed_fd( struct inode *inode, struct closed_fd *fd ) + else /* no locks on this inode and no unlink, get rid of the fd */ + { + if (fd->unix_fd != -1) close( fd->unix_fd ); ++ free( fd->unix_name ); + free( fd ); + } + } +@@ -1451,7 +1455,7 @@ static void fd_dump( struct object *obj, int verbose ) + { + struct fd *fd = (struct fd *)obj; + fprintf( stderr, "Fd unix_fd=%d user=%p options=%08x", fd->unix_fd, fd->user, fd->options ); +- if (fd->inode) fprintf( stderr, " inode=%p unlink='%s'", fd->inode, fd->closed->unlink ); ++ if (fd->inode) fprintf( stderr, " inode=%p unlink=%d", fd->inode, fd->closed->unlink ); + fprintf( stderr, "\n" ); + } + +@@ -1465,7 +1469,6 @@ static void fd_destroy( struct object *obj ) + + if (fd->completion) release_object( fd->completion ); + remove_fd_locks( fd ); +- free( fd->unix_name ); + list_remove( &fd->inode_entry ); + if (fd->poll_index != -1) remove_poll_user( fd, fd->poll_index ); + if (fd->inode) +@@ -1476,6 +1479,7 @@ static void fd_destroy( struct object *obj ) + else /* no inode, close it right away */ + { + if (fd->unix_fd != -1) close( fd->unix_fd ); ++ free( fd->unix_name ); + } + } + +@@ -1563,7 +1567,7 @@ static inline void unmount_fd( struct fd *fd ) + fd->unix_fd = -1; + fd->no_fd_status = STATUS_VOLUME_DISMOUNTED; + fd->closed->unix_fd = -1; +- fd->closed->unlink[0] = 0; ++ fd->closed->unlink = 0; + + /* stop using Unix locks on this fd (existing locks have been removed by close) */ + fd->fs_locks = 0; +@@ -1662,7 +1666,8 @@ struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sha + goto failed; + } + closed->unix_fd = fd->unix_fd; +- closed->unlink[0] = 0; ++ closed->unlink = 0; ++ closed->unix_name = fd->unix_name; + fd->closed = closed; + fd->inode = (struct inode *)grab_object( orig->inode ); + list_add_head( &fd->inode->open, &fd->inode_entry ); +@@ -1738,7 +1743,6 @@ struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, + struct stat st; + struct closed_fd *closed_fd; + struct fd *fd; +- const char *unlink_name = ""; + int root_fd = -1; + int rw_mode; + +@@ -1752,8 +1756,7 @@ struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, + if (!(fd = alloc_fd_object())) return NULL; + + fd->options = options; +- if (options & FILE_DELETE_ON_CLOSE) unlink_name = name; +- if (!(closed_fd = mem_alloc( sizeof(*closed_fd) + strlen(unlink_name) ))) ++ if (!(closed_fd = mem_alloc( sizeof(*closed_fd) ))) + { + release_object( fd ); + return NULL; +@@ -1810,7 +1813,8 @@ struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, + } + + closed_fd->unix_fd = fd->unix_fd; +- closed_fd->unlink[0] = 0; ++ closed_fd->unlink = 0; ++ closed_fd->unix_name = fd->unix_name; + fstat( fd->unix_fd, &st ); + *mode = st.st_mode; + +@@ -1851,7 +1855,7 @@ struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, + set_error( err ); + return NULL; + } +- strcpy( closed_fd->unlink, unlink_name ); ++ closed_fd->unlink = (options & FILE_DELETE_ON_CLOSE) != 0; + if (flags & O_TRUNC) + { + if (S_ISDIR(st.st_mode)) +@@ -1870,7 +1874,7 @@ struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, + set_error( STATUS_NOT_A_DIRECTORY ); + goto error; + } +- if (unlink_name[0]) /* we can't unlink special files */ ++ if (options & FILE_DELETE_ON_CLOSE) /* we can't unlink special files */ + { + set_error( STATUS_INVALID_PARAMETER ); + goto error; +-- +1.7.9.5 + diff --git a/patches/ntdll-FileDispositionInformation/0002-server-Add-support-for-setting-file-disposition-info.patch b/patches/ntdll-FileDispositionInformation/0002-server-Add-support-for-setting-file-disposition-info.patch new file mode 100644 index 00000000..de49d8c0 --- /dev/null +++ b/patches/ntdll-FileDispositionInformation/0002-server-Add-support-for-setting-file-disposition-info.patch @@ -0,0 +1,233 @@ +From 723aef38c2549d8767bb185906cc3fc8d865b695 Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +Date: Thu, 7 Aug 2014 20:32:19 -0600 +Subject: server: Add support for setting file disposition information. + +--- + dlls/ntdll/file.c | 16 ++++++++++++++++ + dlls/ntdll/tests/file.c | 12 ++---------- + server/fd.c | 30 ++++++++++++++++++++++++++++++ + server/file.c | 23 +++++++++++++++++++++++ + server/file.h | 1 + + server/protocol.def | 6 ++++++ + 6 files changed, 78 insertions(+), 10 deletions(-) + +diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c +index d2efcc1..5d59470 100644 +--- a/dlls/ntdll/file.c ++++ b/dlls/ntdll/file.c +@@ -2383,6 +2383,22 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io, + else io->u.Status = STATUS_INVALID_PARAMETER_3; + break; + ++ case FileDispositionInformation: ++ if (len >= sizeof(FILE_DISPOSITION_INFORMATION)) ++ { ++ FILE_DISPOSITION_INFORMATION *info = ptr; ++ ++ SERVER_START_REQ( set_file_info ) ++ { ++ req->handle = wine_server_obj_handle( handle ); ++ req->unlink = info->DoDeleteFile; ++ io->u.Status = wine_server_call( req ); ++ } ++ SERVER_END_REQ; ++ } else ++ io->u.Status = STATUS_INVALID_PARAMETER_3; ++ break; ++ + default: + FIXME("Unsupported class (%d)\n", class); + io->u.Status = STATUS_NOT_IMPLEMENTED; +diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c +index d70ed6b..1606f83 100644 +--- a/dlls/ntdll/tests/file.c ++++ b/dlls/ntdll/tests/file.c +@@ -1371,7 +1371,6 @@ static void test_file_disposition_information(void) + ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_NOT_IMPLEMENTED, "Unexpected NtQueryInformationFile result (expected STATUS_INVALID_INFO_CLASS, got %x)\n", res ); + fdi.DoDeleteFile = TRUE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); +- todo_wine + ok( res == STATUS_ACCESS_DENIED, "unexpected FileDispositionInformation result (expected STATUS_ACCESS_DENIED, got %x)\n", res ); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; +@@ -1384,11 +1383,9 @@ static void test_file_disposition_information(void) + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + fdi.DoDeleteFile = TRUE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); +- todo_wine + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; +- todo_wine + ok( fileDeleted, "File should have been deleted\n" ); + DeleteFileA( buffer ); + +@@ -1402,6 +1399,7 @@ static void test_file_disposition_information(void) + ok( res == STATUS_CANNOT_DELETE, "unexpected FileDispositionInformation result (expected STATUS_CANNOT_DELETE, got %x)\n", res ); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ++ todo_wine + ok( !fileDeleted, "File shouldn't have been deleted\n" ); + SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); + DeleteFileA( buffer ); +@@ -1412,11 +1410,9 @@ static void test_file_disposition_information(void) + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + fdi.DoDeleteFile = TRUE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); +- todo_wine + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + fdi.DoDeleteFile = FALSE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); +- todo_wine + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; +@@ -1429,7 +1425,6 @@ static void test_file_disposition_information(void) + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + fdi.DoDeleteFile = FALSE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); +- todo_wine + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; +@@ -1444,7 +1439,6 @@ static void test_file_disposition_information(void) + CloseHandle( handle ); + fdi.DoDeleteFile = FALSE; + res = pNtSetInformationFile( handle2, &io, &fdi, sizeof fdi, FileDispositionInformation ); +- todo_wine + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + CloseHandle( handle2 ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; +@@ -1459,11 +1453,9 @@ static void test_file_disposition_information(void) + ok( handle != INVALID_HANDLE_VALUE, "failed to open a directory\n" ); + fdi.DoDeleteFile = TRUE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); +- todo_wine + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; +- todo_wine + ok( fileDeleted, "Directory should have been deleted\n" ); + RemoveDirectoryA( buffer ); + +@@ -1476,7 +1468,6 @@ static void test_file_disposition_information(void) + RemoveDirectoryA( buffer ); + fdi.DoDeleteFile = FALSE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); +- todo_wine + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; +@@ -1501,6 +1492,7 @@ static void test_file_disposition_information(void) + 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" ); + RemoveDirectoryA( buffer ); + } +diff --git a/server/fd.c b/server/fd.c +index b022a3c..7be3f4c 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -1921,6 +1921,36 @@ unsigned int get_fd_options( struct fd *fd ) + return fd->options; + } + ++/* set disposition for the fd */ ++void set_fd_disposition( struct fd *fd, int unlink ) ++{ ++ struct stat st; ++ ++ if (fd->unix_fd == -1) ++ { ++ set_error( STATUS_INVALID_HANDLE ); ++ return; ++ } ++ ++ if (fstat( fd->unix_fd, &st ) == -1) ++ { ++ file_set_error(); ++ return; ++ } ++ ++ if (unlink && !S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) ++ { ++ /* can't unlink special files */ ++ set_error( STATUS_INVALID_PARAMETER ); ++ return; ++ } ++ ++ if (unlink) ++ fd->closed->unlink = 1; ++ else if (!(fd->options & FILE_DELETE_ON_CLOSE)) ++ fd->closed->unlink = 0; ++} ++ + /* retrieve the unix fd for an object */ + int get_unix_fd( struct fd *fd ) + { +diff --git a/server/file.c b/server/file.c +index cceb8ad..0a7da00 100644 +--- a/server/file.c ++++ b/server/file.c +@@ -728,3 +728,26 @@ DECL_HANDLER(unlock_file) + release_object( file ); + } + } ++ ++/* set file information */ ++DECL_HANDLER(set_file_info) ++{ ++ struct file *file; ++ ++ if ((file = get_file_obj( current->process, req->handle, DELETE ))) ++ { ++ set_fd_disposition( file->fd, req->unlink ); ++ release_object( file ); ++ return; ++ } ++ ++ if (get_error() == STATUS_OBJECT_TYPE_MISMATCH) ++ { ++ clear_error(); ++ if ((file = (struct file *)get_dir_obj( current->process, req->handle, DELETE ))) ++ { ++ set_fd_disposition( file->fd, req->unlink ); ++ release_object( file ); ++ } ++ } ++} +diff --git a/server/file.h b/server/file.h +index 493d30b..d689d29 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -65,6 +65,7 @@ extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, + extern void *get_fd_user( struct fd *fd ); + extern void set_fd_user( struct fd *fd, const struct fd_ops *ops, struct object *user ); + extern unsigned int get_fd_options( struct fd *fd ); ++extern void set_fd_disposition( struct fd *fd, int unlink ); + extern int get_unix_fd( struct fd *fd ); + extern int is_same_file_fd( struct fd *fd1, struct fd *fd2 ); + extern int is_fd_removable( struct fd *fd ); +diff --git a/server/protocol.def b/server/protocol.def +index a8c1fb9..85505c8 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -1178,6 +1178,12 @@ enum server_fd_type + @END + + ++/* Set file information */ ++@REQ(set_file_info) ++ obj_handle_t handle; /* handle to the file */ ++ int unlink; /* whether to unlink file on close */ ++@END ++ + /* Create a socket */ + @REQ(create_socket) + unsigned int access; /* wanted access rights */ +-- +1.7.9.5 + diff --git a/patches/ntdll-FileDispositionInformation/0003-server-Do-not-permit-FileDispositionInformation-to-d.patch b/patches/ntdll-FileDispositionInformation/0003-server-Do-not-permit-FileDispositionInformation-to-d.patch new file mode 100644 index 00000000..675094d2 --- /dev/null +++ b/patches/ntdll-FileDispositionInformation/0003-server-Do-not-permit-FileDispositionInformation-to-d.patch @@ -0,0 +1,78 @@ +From ea378bcb8a0a6113178c2d42a3e8151b7f99dfb6 Mon Sep 17 00:00:00 2001 +From: "Erich E. Hoover" +Date: Thu, 7 Aug 2014 21:14:25 -0600 +Subject: server: Do not permit FileDispositionInformation to delete a file + without write access. + +--- + dlls/ntdll/tests/file.c | 28 ++++++++++++++++++++++++++++ + server/fd.c | 7 +++++++ + 2 files changed, 35 insertions(+) + +diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c +index 1606f83..05ce517 100644 +--- a/dlls/ntdll/tests/file.c ++++ b/dlls/ntdll/tests/file.c +@@ -1391,6 +1391,20 @@ static void test_file_disposition_information(void) + + /* cannot set disposition on readonly file */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); ++ DeleteFileA( buffer ); ++ handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0); ++ ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); ++ fdi.DoDeleteFile = TRUE; ++ res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); ++ ok( res == STATUS_CANNOT_DELETE, "unexpected FileDispositionInformation result (expected STATUS_CANNOT_DELETE, got %x)\n", res ); ++ CloseHandle( handle ); ++ fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ++ ok( !fileDeleted, "File shouldn't have been deleted\n" ); ++ SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); ++ DeleteFileA( buffer ); ++ ++ /* cannot set disposition on readonly file */ ++ GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + fdi.DoDeleteFile = TRUE; +@@ -1404,6 +1418,20 @@ static void test_file_disposition_information(void) + SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); + DeleteFileA( buffer ); + ++ /* cannot set disposition on readonly file */ ++ GetTempFileNameA( tmp_path, "dis", 0, buffer ); ++ DeleteFileA( buffer ); ++ handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0); ++ ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); ++ fdi.DoDeleteFile = TRUE; ++ res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); ++ ok( res == STATUS_CANNOT_DELETE, "unexpected FileDispositionInformation result (expected STATUS_CANNOT_DELETE, got %x)\n", res ); ++ CloseHandle( handle ); ++ fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ++ ok( !fileDeleted, "File shouldn't have been deleted\n" ); ++ SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); ++ DeleteFileA( buffer ); ++ + /* can set disposition on file and then reset it */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0); +diff --git a/server/fd.c b/server/fd.c +index 7be3f4c..37377d1 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -1945,6 +1945,13 @@ void set_fd_disposition( struct fd *fd, int unlink ) + return; + } + ++ if (unlink && !(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) ++ { ++ /* can't unlink files we don't have permission to access */ ++ set_error( STATUS_CANNOT_DELETE ); ++ return; ++ } ++ + if (unlink) + fd->closed->unlink = 1; + else if (!(fd->options & FILE_DELETE_ON_CLOSE)) +-- +1.7.9.5 + diff --git a/patches/ntdll-FileDispositionInformation/definition b/patches/ntdll-FileDispositionInformation/definition new file mode 100644 index 00000000..dd0ab3e0 --- /dev/null +++ b/patches/ntdll-FileDispositionInformation/definition @@ -0,0 +1,4 @@ +Author: Dmitry Timoshkov / Erich E. Hoover +Subject: Add support for setting file disposition information. +Revision: 1 +Fixes: [30397] Support for NtSetInformationFile class FileDispositionInformation