msxml3: Don't crash on null pointer when doing ::Next() on a last child.
[wine] / dlls / ole32 / stg_bigblockfile.c
1 /******************************************************************************
2  *
3  * BigBlockFile
4  *
5  * This is the implementation of a file that consists of blocks of
6  * a predetermined size.
7  * This class is used in the Compound File implementation of the
8  * IStorage and IStream interfaces. It provides the functionality
9  * to read and write any blocks in the file as well as setting and
10  * obtaining the size of the file.
11  * The blocks are indexed sequentially from the start of the file
12  * starting with -1.
13  *
14  * TODO:
15  * - Support for a transacted mode
16  *
17  * Copyright 1999 Thuy Nguyen
18  *
19  * This library is free software; you can redistribute it and/or
20  * modify it under the terms of the GNU Lesser General Public
21  * License as published by the Free Software Foundation; either
22  * version 2.1 of the License, or (at your option) any later version.
23  *
24  * This library is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
27  * Lesser General Public License for more details.
28  *
29  * You should have received a copy of the GNU Lesser General Public
30  * License along with this library; if not, write to the Free Software
31  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32  */
33
34 #include <assert.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <limits.h>
40
41 #define COBJMACROS
42 #define NONAMELESSUNION
43 #define NONAMELESSSTRUCT
44
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winuser.h"
48 #include "winerror.h"
49 #include "objbase.h"
50 #include "ole2.h"
51
52 #include "storage32.h"
53
54 #include "wine/debug.h"
55
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
57
58 /***********************************************************
59  * Data structures used internally by the BigBlockFile
60  * class.
61  */
62
63 /* We map in PAGE_SIZE-sized chunks. Must be a multiple of 4096. */
64 #define PAGE_SIZE       131072
65
66 /* We keep a list of recently-discarded pages. This controls the
67  * size of that list. */
68 #define MAX_VICTIM_PAGES 16
69
70 /***
71  * This structure identifies the paged that are mapped
72  * from the file and their position in memory. It is
73  * also used to hold a reference count to those pages.
74  *
75  * page_index identifies which PAGE_SIZE chunk from the
76  * file this mapping represents. (The mappings are always
77  * PAGE_SIZE-aligned.)
78  */
79
80 typedef struct MappedPage MappedPage;
81 struct MappedPage
82 {
83     MappedPage *next;
84     MappedPage *prev;
85
86     DWORD  page_index;
87     DWORD  mapped_bytes;
88     LPVOID lpBytes;
89     LONG   refcnt;
90 };
91
92 struct BigBlockFile
93 {
94     BOOL fileBased;
95     ULARGE_INTEGER filesize;
96     HANDLE hfile;
97     HANDLE hfilemap;
98     DWORD flProtect;
99     MappedPage *maplist;
100     MappedPage *victimhead, *victimtail;
101     ULONG num_victim_pages;
102     ILockBytes *pLkbyt;
103 };
104
105 /***********************************************************
106  * Prototypes for private methods
107  */
108
109 /* Note that this evaluates a and b multiple times, so don't
110  * pass expressions with side effects. */
111 #define ROUND_UP(a, b) ((((a) + (b) - 1)/(b))*(b))
112
113 /******************************************************************************
114  *      BIGBLOCKFILE_FileInit
115  *
116  * Initialize a big block object supported by a file.
117  */
118 static BOOL BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This, HANDLE hFile)
119 {
120   This->pLkbyt = NULL;
121   This->hfile = hFile;
122
123   if (This->hfile == INVALID_HANDLE_VALUE)
124     return FALSE;
125
126   This->filesize.u.LowPart = GetFileSize(This->hfile,
127                                          &This->filesize.u.HighPart);
128
129   if( This->filesize.u.LowPart || This->filesize.u.HighPart )
130   {
131     /* create the file mapping object
132      */
133     This->hfilemap = CreateFileMappingA(This->hfile,
134                                         NULL,
135                                         This->flProtect,
136                                         0, 0,
137                                         NULL);
138
139     if (!This->hfilemap)
140     {
141       CloseHandle(This->hfile);
142       return FALSE;
143     }
144   }
145   else
146     This->hfilemap = NULL;
147
148   This->maplist = NULL;
149
150   TRACE("file len %u\n", This->filesize.u.LowPart);
151
152   return TRUE;
153 }
154
155 /******************************************************************************
156  *      BIGBLOCKFILE_LockBytesInit
157  *
158  * Initialize a big block object supported by an ILockBytes.
159  */
160 static BOOL BIGBLOCKFILE_LockBytesInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt)
161 {
162     This->hfile    = 0;
163     This->hfilemap = 0;
164     This->pLkbyt   = plkbyt;
165     ILockBytes_AddRef(This->pLkbyt);
166
167     /* We'll get the size directly with ILockBytes_Stat */
168     This->filesize.QuadPart = 0;
169
170     TRACE("ILockBytes %p\n", This->pLkbyt);
171     return TRUE;
172 }
173
174 /******************************************************************************
175  *      BIGBLOCKFILE_FindPageInList      [PRIVATE]
176  *
177  */
178 static MappedPage *BIGBLOCKFILE_FindPageInList(MappedPage *head,
179                                                ULONG page_index)
180 {
181     for (; head != NULL; head = head->next)
182     {
183         if (head->page_index == page_index)
184         {
185             InterlockedIncrement(&head->refcnt);
186             break;
187         }
188     }
189
190     return head;
191
192 }
193
194 static void BIGBLOCKFILE_UnlinkPage(MappedPage *page)
195 {
196     if (page->next) page->next->prev = page->prev;
197     if (page->prev) page->prev->next = page->next;
198 }
199
200 static void BIGBLOCKFILE_LinkHeadPage(MappedPage **head, MappedPage *page)
201 {
202     if (*head) (*head)->prev = page;
203     page->next = *head;
204     page->prev = NULL;
205     *head = page;
206 }
207
208 static BOOL BIGBLOCKFILE_MapPage(BigBlockFile *This, MappedPage *page)
209 {
210     DWORD lowoffset = PAGE_SIZE * page->page_index;
211     DWORD numBytesToMap;
212     DWORD desired_access;
213
214     assert(This->fileBased);
215
216     if( !This->hfilemap )
217         return FALSE;
218
219     if (lowoffset + PAGE_SIZE > This->filesize.u.LowPart)
220         numBytesToMap = This->filesize.u.LowPart - lowoffset;
221     else
222         numBytesToMap = PAGE_SIZE;
223
224     if (This->flProtect == PAGE_READONLY)
225         desired_access = FILE_MAP_READ;
226     else
227         desired_access = FILE_MAP_WRITE;
228
229     page->lpBytes = MapViewOfFile(This->hfilemap, desired_access, 0,
230                                   lowoffset, numBytesToMap);
231     page->mapped_bytes = numBytesToMap;
232
233     TRACE("mapped page %u to %p\n", page->page_index, page->lpBytes);
234
235     return page->lpBytes != NULL;
236 }
237
238
239 static MappedPage *BIGBLOCKFILE_CreatePage(BigBlockFile *This, ULONG page_index)
240 {
241     MappedPage *page;
242
243     page = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
244     if (page == NULL)
245         return NULL;
246
247     page->page_index = page_index;
248     page->refcnt = 1;
249
250     page->next = NULL;
251     page->prev = NULL;
252
253     if (!BIGBLOCKFILE_MapPage(This, page))
254     {
255         HeapFree(GetProcessHeap(),0,page);
256         return NULL;
257     }
258
259     return page;
260 }
261
262
263 /******************************************************************************
264  *      BIGBLOCKFILE_GetMappedView      [PRIVATE]
265  *
266  * Gets the page requested if it is already mapped.
267  * If it's not already mapped, this method will map it
268  */
269 static void * BIGBLOCKFILE_GetMappedView(
270   LPBIGBLOCKFILE This,
271   DWORD          page_index)
272 {
273     MappedPage *page;
274
275     page = BIGBLOCKFILE_FindPageInList(This->maplist, page_index);
276     if (!page)
277     {
278         page = BIGBLOCKFILE_FindPageInList(This->victimhead, page_index);
279         if (page)
280         {
281             This->num_victim_pages--;
282         }
283     }
284
285     if (page)
286     {
287         /* If the page is not already at the head of the list, move
288          * it there. (Also moves pages from victim to main list.) */
289         if (This->maplist != page)
290         {
291             if (This->victimhead == page) This->victimhead = page->next;
292             if (This->victimtail == page) This->victimtail = page->prev;
293
294             BIGBLOCKFILE_UnlinkPage(page);
295
296             BIGBLOCKFILE_LinkHeadPage(&This->maplist, page);
297         }
298
299         return page;
300     }
301
302     page = BIGBLOCKFILE_CreatePage(This, page_index);
303     if (!page) return NULL;
304
305     BIGBLOCKFILE_LinkHeadPage(&This->maplist, page);
306
307     return page;
308 }
309
310 static void BIGBLOCKFILE_UnmapPage(LPBIGBLOCKFILE This, MappedPage *page)
311 {
312     TRACE("%d at %p\n", page->page_index, page->lpBytes);
313
314     assert(This->fileBased);
315
316     if (page->refcnt > 0)
317         ERR("unmapping inuse page %p\n", page->lpBytes);
318
319     if (page->lpBytes)
320         UnmapViewOfFile(page->lpBytes);
321
322     page->lpBytes = NULL;
323 }
324
325 static void BIGBLOCKFILE_DeletePage(LPBIGBLOCKFILE This, MappedPage *page)
326 {
327     BIGBLOCKFILE_UnmapPage(This, page);
328
329     HeapFree(GetProcessHeap(), 0, page);
330 }
331
332 /******************************************************************************
333  *      BIGBLOCKFILE_ReleaseMappedPage      [PRIVATE]
334  *
335  * Decrements the reference count of the mapped page.
336  */
337 static void BIGBLOCKFILE_ReleaseMappedPage(
338   LPBIGBLOCKFILE This,
339   MappedPage    *page)
340 {
341     assert(This != NULL);
342     assert(page != NULL);
343
344     /* If the page is no longer refenced, move it to the victim list.
345      * If the victim list is too long, kick somebody off. */
346     if (!InterlockedDecrement(&page->refcnt))
347     {
348         if (This->maplist == page) This->maplist = page->next;
349
350         BIGBLOCKFILE_UnlinkPage(page);
351
352         if (MAX_VICTIM_PAGES > 0)
353         {
354             if (This->num_victim_pages >= MAX_VICTIM_PAGES)
355             {
356                 MappedPage *victim = This->victimtail;
357                 if (victim)
358                 {
359                     This->victimtail = victim->prev;
360                     if (This->victimhead == victim)
361                         This->victimhead = victim->next;
362
363                     BIGBLOCKFILE_UnlinkPage(victim);
364                     BIGBLOCKFILE_DeletePage(This, victim);
365                 }
366             }
367             else This->num_victim_pages++;
368
369             BIGBLOCKFILE_LinkHeadPage(&This->victimhead, page);
370             if (This->victimtail == NULL) This->victimtail = page;
371         }
372         else
373             BIGBLOCKFILE_DeletePage(This, page);
374     }
375 }
376
377 static void BIGBLOCKFILE_DeleteList(LPBIGBLOCKFILE This, MappedPage *list)
378 {
379     while (list != NULL)
380     {
381         MappedPage *next = list->next;
382
383         BIGBLOCKFILE_DeletePage(This, list);
384
385         list = next;
386     }
387 }
388
389 /******************************************************************************
390  *      BIGBLOCKFILE_FreeAllMappedPages     [PRIVATE]
391  *
392  * Unmap all currently mapped pages.
393  * Empty mapped pages list.
394  */
395 static void BIGBLOCKFILE_FreeAllMappedPages(
396   LPBIGBLOCKFILE This)
397 {
398     BIGBLOCKFILE_DeleteList(This, This->maplist);
399     BIGBLOCKFILE_DeleteList(This, This->victimhead);
400
401     This->maplist = NULL;
402     This->victimhead = NULL;
403     This->victimtail = NULL;
404     This->num_victim_pages = 0;
405 }
406
407 static void BIGBLOCKFILE_UnmapList(LPBIGBLOCKFILE This, MappedPage *list)
408 {
409     for (; list != NULL; list = list->next)
410     {
411         BIGBLOCKFILE_UnmapPage(This, list);
412     }
413 }
414
415 static void BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This)
416 {
417     BIGBLOCKFILE_UnmapList(This, This->maplist);
418     BIGBLOCKFILE_UnmapList(This, This->victimhead);
419 }
420
421 static void BIGBLOCKFILE_RemapList(LPBIGBLOCKFILE This, MappedPage *list)
422 {
423     while (list != NULL)
424     {
425         MappedPage *next = list->next;
426
427         if (list->page_index * PAGE_SIZE > This->filesize.u.LowPart)
428         {
429             TRACE("discarding %u\n", list->page_index);
430
431             /* page is entirely outside of the file, delete it */
432             BIGBLOCKFILE_UnlinkPage(list);
433             BIGBLOCKFILE_DeletePage(This, list);
434         }
435         else
436         {
437             /* otherwise, remap it */
438             BIGBLOCKFILE_MapPage(This, list);
439         }
440
441         list = next;
442     }
443 }
444
445 static void BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This)
446 {
447     BIGBLOCKFILE_RemapList(This, This->maplist);
448     BIGBLOCKFILE_RemapList(This, This->victimhead);
449 }
450
451 /****************************************************************************
452  *      BIGBLOCKFILE_GetProtectMode
453  *
454  * This function will return a protection mode flag for a file-mapping object
455  * from the open flags of a file.
456  */
457 static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags)
458 {
459     switch(STGM_ACCESS_MODE(openFlags))
460     {
461     case STGM_WRITE:
462     case STGM_READWRITE:
463         return PAGE_READWRITE;
464     }
465     return PAGE_READONLY;
466 }
467
468
469 /* ILockByte Interfaces */
470
471 /******************************************************************************
472  * This method is part of the ILockBytes interface.
473  *
474  * It reads a block of information from the byte array at the specified
475  * offset.
476  *
477  * See the documentation of ILockBytes for more info.
478  */
479 static HRESULT ImplBIGBLOCKFILE_ReadAt(
480       BigBlockFile* const This,
481       ULARGE_INTEGER ulOffset,  /* [in] */
482       void*          pv,        /* [length_is][size_is][out] */
483       ULONG          cb,        /* [in] */
484       ULONG*         pcbRead)   /* [out] */
485 {
486     ULONG first_page = ulOffset.u.LowPart / PAGE_SIZE;
487     ULONG offset_in_page = ulOffset.u.LowPart % PAGE_SIZE;
488     ULONG bytes_left = cb;
489     ULONG page_index = first_page;
490     ULONG bytes_from_page;
491     LPVOID writePtr = pv;
492
493     HRESULT rc = S_OK;
494
495     TRACE("(%p)-> %i %p %i %p\n",This, ulOffset.u.LowPart, pv, cb, pcbRead);
496
497     /* verify a sane environment */
498     if (!This) return E_FAIL;
499
500     if (offset_in_page + bytes_left > PAGE_SIZE)
501         bytes_from_page = PAGE_SIZE - offset_in_page;
502     else
503         bytes_from_page = bytes_left;
504
505     if (pcbRead)
506         *pcbRead = 0;
507
508     while (bytes_left)
509     {
510         LPBYTE readPtr;
511         BOOL eof = FALSE;
512         MappedPage *page = BIGBLOCKFILE_GetMappedView(This, page_index);
513
514         if (!page || !page->lpBytes)
515         {
516             rc = STG_E_READFAULT;
517             break;
518         }
519
520         TRACE("page %i,  offset %u, bytes_from_page %u, bytes_left %u\n",
521             page->page_index, offset_in_page, bytes_from_page, bytes_left);
522
523         if (page->mapped_bytes < bytes_from_page)
524         {
525             eof = TRUE;
526             bytes_from_page = page->mapped_bytes;
527         }
528
529         readPtr = (BYTE*)page->lpBytes + offset_in_page;
530         memcpy(writePtr,readPtr,bytes_from_page);
531         BIGBLOCKFILE_ReleaseMappedPage(This, page);
532
533         if (pcbRead)
534             *pcbRead += bytes_from_page;
535         bytes_left -= bytes_from_page;
536
537         if (bytes_left && !eof)
538         {
539             writePtr = (LPBYTE)writePtr + bytes_from_page;
540             page_index ++;
541             offset_in_page = 0;
542             if (bytes_left > PAGE_SIZE)
543                 bytes_from_page = PAGE_SIZE;
544             else
545                 bytes_from_page = bytes_left;
546         }
547         if (eof)
548         {
549             rc = STG_E_READFAULT;
550             break;
551         }
552     }
553
554     TRACE("finished\n");
555     return rc;
556 }
557
558 /******************************************************************************
559  * This method is part of the ILockBytes interface.
560  *
561  * It writes the specified bytes at the specified offset.
562  * position. If the file is too small, it will be resized.
563  *
564  * See the documentation of ILockBytes for more info.
565  */
566 static HRESULT ImplBIGBLOCKFILE_WriteAt(
567       BigBlockFile* const This,
568       ULARGE_INTEGER ulOffset,    /* [in] */
569       const void*    pv,          /* [size_is][in] */
570       ULONG          cb,          /* [in] */
571       ULONG*         pcbWritten)  /* [out] */
572 {
573     ULONG size_needed = ulOffset.u.LowPart + cb;
574     ULONG first_page = ulOffset.u.LowPart / PAGE_SIZE;
575     ULONG offset_in_page = ulOffset.u.LowPart % PAGE_SIZE;
576     ULONG bytes_left = cb;
577     ULONG page_index = first_page;
578     ULONG bytes_to_page;
579     LPCVOID readPtr = pv;
580
581     HRESULT rc = S_OK;
582
583     TRACE("(%p)-> %i %p %i %p\n",This, ulOffset.u.LowPart, pv, cb, pcbWritten);
584
585     /* verify a sane environment */
586     if (!This) return E_FAIL;
587
588     if (This->flProtect != PAGE_READWRITE)
589         return STG_E_ACCESSDENIED;
590
591     if (size_needed > This->filesize.u.LowPart)
592     {
593         ULARGE_INTEGER newSize;
594         newSize.u.HighPart = 0;
595         newSize.u.LowPart = size_needed;
596         BIGBLOCKFILE_SetSize(This, newSize);
597     }
598
599     if (offset_in_page + bytes_left > PAGE_SIZE)
600         bytes_to_page = PAGE_SIZE - offset_in_page;
601     else
602         bytes_to_page = bytes_left;
603
604     if (pcbWritten)
605         *pcbWritten = 0;
606
607     while (bytes_left)
608     {
609         LPBYTE writePtr;
610         MappedPage *page = BIGBLOCKFILE_GetMappedView(This, page_index);
611
612         TRACE("page %i,  offset %u, bytes_to_page %u, bytes_left %u\n",
613             page ? page->page_index : 0, offset_in_page, bytes_to_page, bytes_left);
614
615         if (!page)
616         {
617             ERR("Unable to get a page to write. This should never happen\n");
618             rc = E_FAIL;
619             break;
620         }
621
622         if (page->mapped_bytes < bytes_to_page)
623         {
624             ERR("Not enough bytes mapped to the page. This should never happen\n");
625             rc = E_FAIL;
626             break;
627         }
628
629         writePtr = (BYTE*)page->lpBytes + offset_in_page;
630         memcpy(writePtr,readPtr,bytes_to_page);
631         BIGBLOCKFILE_ReleaseMappedPage(This, page);
632
633         if (pcbWritten)
634             *pcbWritten += bytes_to_page;
635         bytes_left -= bytes_to_page;
636
637         if (bytes_left)
638         {
639             readPtr = (const BYTE *)readPtr + bytes_to_page;
640             page_index ++;
641             offset_in_page = 0;
642             if (bytes_left > PAGE_SIZE)
643                 bytes_to_page = PAGE_SIZE;
644             else
645                 bytes_to_page = bytes_left;
646         }
647     }
648
649     return rc;
650 }
651
652 /******************************************************************************
653  *      BIGBLOCKFILE_Construct
654  *
655  * Construct a big block file. Create the file mapping object.
656  * Create the read only mapped pages list, the writable mapped page list
657  * and the blocks in use list.
658  */
659 BigBlockFile *BIGBLOCKFILE_Construct(HANDLE hFile, ILockBytes* pLkByt, DWORD openFlags,
660                                      BOOL fileBased)
661 {
662     BigBlockFile *This;
663
664     This = HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile));
665
666     if (This == NULL)
667         return NULL;
668
669     This->fileBased = fileBased;
670     This->flProtect = BIGBLOCKFILE_GetProtectMode(openFlags);
671
672     This->maplist = NULL;
673     This->victimhead = NULL;
674     This->victimtail = NULL;
675     This->num_victim_pages = 0;
676
677     if (This->fileBased)
678     {
679         if (!BIGBLOCKFILE_FileInit(This, hFile))
680         {
681             HeapFree(GetProcessHeap(), 0, This);
682             return NULL;
683         }
684     }
685     else
686     {
687         if (!BIGBLOCKFILE_LockBytesInit(This, pLkByt))
688         {
689             HeapFree(GetProcessHeap(), 0, This);
690             return NULL;
691         }
692     }
693
694     return This;
695 }
696
697 /******************************************************************************
698  *      BIGBLOCKFILE_Destructor
699  *
700  * Destructor. Clean up, free memory.
701  */
702 void BIGBLOCKFILE_Destructor(BigBlockFile *This)
703 {
704     BIGBLOCKFILE_FreeAllMappedPages(This);
705
706     if (This->fileBased)
707     {
708         CloseHandle(This->hfilemap);
709         CloseHandle(This->hfile);
710     }
711     else
712     {
713         ILockBytes_Release(This->pLkbyt);
714     }
715
716     HeapFree(GetProcessHeap(), 0, This);
717 }
718
719 /******************************************************************************
720  *      BIGBLOCKFILE_ReadAt
721  */
722 HRESULT BIGBLOCKFILE_ReadAt(BigBlockFile *This, ULARGE_INTEGER offset,
723                             void* buffer, ULONG size, ULONG* bytesRead)
724 {
725     if (This->fileBased)
726         return ImplBIGBLOCKFILE_ReadAt(This,offset,buffer,size,bytesRead);
727     else
728         return ILockBytes_ReadAt(This->pLkbyt,offset,buffer,size,bytesRead);
729 }
730
731 /******************************************************************************
732  *      BIGBLOCKFILE_WriteAt
733  */
734 HRESULT BIGBLOCKFILE_WriteAt(BigBlockFile *This, ULARGE_INTEGER offset,
735                              const void* buffer, ULONG size, ULONG* bytesRead)
736 {
737     if (This->fileBased)
738         return ImplBIGBLOCKFILE_WriteAt(This,offset,buffer,size,bytesRead);
739     else
740         return ILockBytes_WriteAt(This->pLkbyt,offset,buffer,size,bytesRead);
741 }
742
743 /******************************************************************************
744  *      BIGBLOCKFILE_SetSize
745  *
746  * Sets the size of the file.
747  *
748  */
749 HRESULT BIGBLOCKFILE_SetSize(BigBlockFile *This, ULARGE_INTEGER newSize)
750 {
751     HRESULT hr = S_OK;
752     LARGE_INTEGER newpos;
753
754     if (!This->fileBased)
755         return ILockBytes_SetSize(This->pLkbyt, newSize);
756
757     if (This->filesize.u.LowPart == newSize.u.LowPart)
758         return hr;
759
760     TRACE("from %u to %u\n", This->filesize.u.LowPart, newSize.u.LowPart);
761
762     /*
763      * Unmap all views, must be done before call to SetEndFile.
764      *
765      * Just ditch the victim list because there is no guarantee we will need them
766      * and it is not worth the performance hit to unmap and remap them all.
767      */
768     BIGBLOCKFILE_DeleteList(This, This->victimhead);
769     This->victimhead = NULL;
770     This->victimtail = NULL;
771     This->num_victim_pages = 0;
772
773     BIGBLOCKFILE_UnmapAllMappedPages(This);
774
775     newpos.QuadPart = newSize.QuadPart;
776     if (SetFilePointerEx(This->hfile, newpos, NULL, FILE_BEGIN))
777     {
778         if( This->hfilemap ) CloseHandle(This->hfilemap);
779
780         SetEndOfFile(This->hfile);
781
782         /* re-create the file mapping object */
783         This->hfilemap = CreateFileMappingA(This->hfile, NULL, This->flProtect,
784                                             0, 0, NULL);
785     }
786
787     This->filesize = newSize;
788     BIGBLOCKFILE_RemapAllMappedPages(This);
789     return hr;
790 }
791
792 /******************************************************************************
793  *      BIGBLOCKFILE_GetSize
794  *
795  * Gets the size of the file.
796  *
797  */
798 static HRESULT BIGBLOCKFILE_GetSize(BigBlockFile *This, ULARGE_INTEGER *size)
799 {
800     HRESULT hr = S_OK;
801     if(This->fileBased)
802         *size = This->filesize;
803     else
804     {
805         STATSTG stat;
806         hr = ILockBytes_Stat(This->pLkbyt, &stat, STATFLAG_NONAME);
807         if(SUCCEEDED(hr)) *size = stat.cbSize;
808     }
809     return hr;
810 }
811
812 /******************************************************************************
813  *      BIGBLOCKFILE_Expand
814  *
815  * Grows the file to the specified size if necessary.
816  */
817 HRESULT BIGBLOCKFILE_Expand(BigBlockFile *This, ULARGE_INTEGER newSize)
818 {
819     ULARGE_INTEGER size;
820     HRESULT hr;
821
822     hr = BIGBLOCKFILE_GetSize(This, &size);
823     if(FAILED(hr)) return hr;
824
825     if (newSize.QuadPart > size.QuadPart)
826         hr = BIGBLOCKFILE_SetSize(This, newSize);
827     return hr;
828 }