From 3b5ea5ff15907d1d8f1902a08451b0a03eb11dc9 Mon Sep 17 00:00:00 2001
From: Qian Hong <qhong@codeweavers.com>
Date: Fri, 21 Aug 2015 21:58:51 +0800
Subject: ntdll: Set EOF on file which has a memory mapping should fail.

---
 dlls/ntdll/file.c   | 27 +++++++-------------------
 server/fd.c         | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 server/protocol.def |  7 +++++++
 3 files changed, 69 insertions(+), 20 deletions(-)

diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index be6b591..8c9b076 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -2650,30 +2650,17 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io,
     case FileEndOfFileInformation:
         if (len >= sizeof(FILE_END_OF_FILE_INFORMATION))
         {
-            struct stat st;
             const FILE_END_OF_FILE_INFORMATION *info = ptr;
 
-            if ((io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
-                return io->u.Status;
-
-            /* first try normal truncate */
-            if (ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
-
-            /* now check for the need to extend the file */
-            if (fstat( fd, &st ) != -1 && (off_t)info->EndOfFile.QuadPart > st.st_size)
+            SERVER_START_REQ( set_fd_eof_info )
             {
-                static const char zero;
-
-                /* extend the file one byte beyond the requested size and then truncate it */
-                /* this should work around ftruncate implementations that can't extend files */
-                if (pwrite( fd, &zero, 1, (off_t)info->EndOfFile.QuadPart ) != -1 &&
-                    ftruncate( fd, (off_t)info->EndOfFile.QuadPart ) != -1) break;
+                req->handle   = wine_server_obj_handle( handle );
+                req->eof      = info->EndOfFile.QuadPart;
+                io->u.Status  = wine_server_call( req );
             }
-            io->u.Status = FILE_GetNtStatus();
-
-            if (needs_close) close( fd );
-        }
-        else io->u.Status = STATUS_INVALID_PARAMETER_3;
+            SERVER_END_REQ;
+        } else
+            io->u.Status = STATUS_INVALID_PARAMETER_3;
         break;
 
     case FilePipeInformation:
diff --git a/server/fd.c b/server/fd.c
index fef4827..62373d8 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -2353,6 +2353,50 @@ failed:
     free( name );
 }
 
+static void set_fd_eof( struct fd *fd, file_pos_t eof )
+{
+    static const char zero;
+    struct stat st;
+    struct list *ptr;
+
+    if (!fd->inode)
+    {
+        set_error( STATUS_OBJECT_TYPE_MISMATCH );
+        return;
+    }
+
+    if (fd->unix_fd == -1)
+    {
+        set_error( fd->no_fd_status );
+        return;
+    }
+
+    /* can't set eof of files which are mapped to memory */
+    LIST_FOR_EACH( ptr, &fd->inode->open )
+    {
+        struct fd *fd_ptr = LIST_ENTRY( ptr, struct fd, inode_entry );
+        if (fd_ptr != fd && (fd_ptr->access & FILE_MAPPING_ACCESS))
+        {
+            set_error( STATUS_USER_MAPPED_FILE );
+            return;
+        }
+    }
+
+    /* first try normal truncate */
+    if (ftruncate( fd->unix_fd, eof ) != -1) return;
+
+    /* now check for the need to extend the file */
+    if (fstat( fd->unix_fd, &st ) != -1 && eof > st.st_size)
+    {
+        /* extend the file one byte beyond the requested size and then truncate it */
+        /* this should work around ftruncate implementations that can't extend files */
+        if (pwrite( fd->unix_fd, &zero, 1, eof ) != -1 &&
+            ftruncate( fd->unix_fd, eof) != -1) return;
+    }
+
+    file_set_error();
+}
+
 struct completion *fd_get_completion( struct fd *fd, apc_param_t *p_key )
 {
     *p_key = fd->comp_key;
@@ -2593,3 +2637,14 @@ DECL_HANDLER(set_fd_name_info)
     }
     if (root_fd) release_object( root_fd );
 }
+
+/* set fd eof information */
+DECL_HANDLER(set_fd_eof_info)
+{
+    struct fd *fd = get_handle_fd_obj( current->process, req->handle, 0 );
+    if (fd)
+    {
+        set_fd_eof( fd, req->eof );
+        release_object( fd );
+    }
+}
diff --git a/server/protocol.def b/server/protocol.def
index c313006..8d197f0 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3541,6 +3541,13 @@ enum coords_relative
 @END
 
 
+/* set fd eof information */
+@REQ(set_fd_eof_info)
+    obj_handle_t handle;        /* handle to a file or directory */
+    file_pos_t   eof;           /* offset of eof of file */
+@END
+
+
 /* Retrieve layered info for a window */
 @REQ(get_window_layered_info)
     user_handle_t  handle;        /* handle to the window */
-- 
2.5.0

