ntdll: Store the per-page committed status in the server for anonymous file mappings.
diff --git a/server/mapping.c b/server/mapping.c
index 02b73b1..34fc6ce 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -39,6 +39,18 @@
 #include "request.h"
 #include "security.h"
 
+/* list of memory ranges, used to store committed info */
+struct ranges
+{
+    unsigned int count;
+    unsigned int max;
+    struct range
+    {
+        file_pos_t  start;
+        file_pos_t  end;
+    } ranges[1];
+};
+
 struct mapping
 {
     struct object   obj;             /* object header */
@@ -47,6 +59,7 @@
     struct file    *file;            /* file mapped */
     int             header_size;     /* size of headers (for PE image mapping) */
     void           *base;            /* default base addr (for PE image mapping) */
+    struct ranges  *committed;       /* list of committed ranges in this mapping */
     struct file    *shared_file;     /* temp file for shared PE mapping */
     struct list     shared_entry;    /* entry in global shared PE mappings list */
 };
@@ -138,6 +151,81 @@
     if (*file_size > *map_size) *file_size = *map_size;
 }
 
+/* add a range to the committed list */
+static void add_committed_range( struct mapping *mapping, file_pos_t start, file_pos_t end )
+{
+    unsigned int i, j;
+    struct range *ranges;
+
+    if (!mapping->committed) return;  /* everything committed already */
+
+    for (i = 0, ranges = mapping->committed->ranges; i < mapping->committed->count; i++)
+    {
+        if (ranges[i].start > end) break;
+        if (ranges[i].end < start) continue;
+        if (ranges[i].start > start) ranges[i].start = start;   /* extend downwards */
+        if (ranges[i].end < end)  /* extend upwards and maybe merge with next */
+        {
+            for (j = i + 1; j < mapping->committed->count; j++)
+            {
+                if (ranges[j].start > end) break;
+                if (ranges[j].end > end) end = ranges[j].end;
+            }
+            if (j > i + 1)
+            {
+                memmove( &ranges[i + 1], &ranges[j], (mapping->committed->count - j) * sizeof(*ranges) );
+                mapping->committed->count -= j - (i + 1);
+            }
+            ranges[i].end = end;
+        }
+        return;
+    }
+
+    /* now add a new range */
+
+    if (mapping->committed->count == mapping->committed->max)
+    {
+        unsigned int new_size = mapping->committed->max * 2;
+        struct ranges *new_ptr = realloc( mapping->committed, offsetof( struct ranges, ranges[new_size] ));
+        if (!new_ptr) return;
+        new_ptr->max = new_size;
+        ranges = new_ptr->ranges;
+        mapping->committed = new_ptr;
+    }
+    memmove( &ranges[i + 1], &ranges[i], (mapping->committed->count - i) * sizeof(*ranges) );
+    ranges[i].start = start;
+    ranges[i].end = end;
+    mapping->committed->count++;
+}
+
+/* find the range containing start and return whether it's committed */
+static int find_committed_range( struct mapping *mapping, file_pos_t start, file_pos_t *size )
+{
+    unsigned int i;
+    struct range *ranges;
+
+    if (!mapping->committed)  /* everything is committed */
+    {
+        *size = mapping->size - start;
+        return 1;
+    }
+    for (i = 0, ranges = mapping->committed->ranges; i < mapping->committed->count; i++)
+    {
+        if (ranges[i].start > start)
+        {
+            *size = ranges[i].start - start;
+            return 0;
+        }
+        if (ranges[i].end > start)
+        {
+            *size = ranges[i].end - start;
+            return 1;
+        }
+    }
+    *size = mapping->size - start;
+    return 0;
+}
+
 /* allocate and fill the temp file for a shared PE image mapping */
 static int build_shared_mapping( struct mapping *mapping, int fd,
                                  IMAGE_SECTION_HEADER *sec, unsigned int nb_sec )
@@ -302,13 +390,20 @@
                                                SACL_SECURITY_INFORMATION );
     mapping->header_size = 0;
     mapping->base        = NULL;
+    mapping->file        = NULL;
     mapping->shared_file = NULL;
+    mapping->committed   = NULL;
 
     if (protect & VPROT_READ) access |= FILE_READ_DATA;
     if (protect & VPROT_WRITE) access |= FILE_WRITE_DATA;
 
     if (handle)
     {
+        if (!(protect & VPROT_COMMITTED))
+        {
+            set_error( STATUS_INVALID_PARAMETER );
+            goto error;
+        }
         if (!(mapping->file = get_file_obj( current->process, handle, access ))) goto error;
         if (protect & VPROT_IMAGE)
         {
@@ -334,9 +429,14 @@
         if (!size || (protect & VPROT_IMAGE))
         {
             set_error( STATUS_INVALID_PARAMETER );
-            mapping->file = NULL;
             goto error;
         }
+        if (!(protect & VPROT_COMMITTED))
+        {
+            if (!(mapping->committed = mem_alloc( offsetof(struct ranges, ranges[8]) ))) goto error;
+            mapping->committed->count = 0;
+            mapping->committed->max   = 8;
+        }
         if (!(mapping->file = create_temp_file( access ))) goto error;
         if (!grow_file( mapping->file, size )) goto error;
     }
@@ -394,6 +494,7 @@
         release_object( mapping->shared_file );
         list_remove( &mapping->shared_entry );
     }
+    free( mapping->committed );
 }
 
 int get_page_size(void)
@@ -485,3 +586,39 @@
         release_object( mapping );
     }
 }
+
+/* get a range of committed pages in a file mapping */
+DECL_HANDLER(get_mapping_committed_range)
+{
+    struct mapping *mapping;
+
+    if ((mapping = (struct mapping *)get_handle_obj( current->process, req->handle, 0, &mapping_ops )))
+    {
+        if (!(req->offset & page_mask) && req->offset < mapping->size)
+            reply->committed = find_committed_range( mapping, req->offset, &reply->size );
+        else
+            set_error( STATUS_INVALID_PARAMETER );
+
+        release_object( mapping );
+    }
+}
+
+/* add a range to the committed pages in a file mapping */
+DECL_HANDLER(add_mapping_committed_range)
+{
+    struct mapping *mapping;
+
+    if ((mapping = (struct mapping *)get_handle_obj( current->process, req->handle, 0, &mapping_ops )))
+    {
+        if (!(req->size & page_mask) &&
+            !(req->offset & page_mask) &&
+            req->offset < mapping->size &&
+            req->size > 0 &&
+            req->size <= mapping->size - req->offset)
+            add_committed_range( mapping, req->offset, req->offset + req->size );
+        else
+            set_error( STATUS_INVALID_PARAMETER );
+
+        release_object( mapping );
+    }
+}