Wrong access on server handle was demanded (GENERIC_READ instead of
[wine/multimedia.git] / ole / stg_bigblockfile.c
blob587c0f513986b36bdf807fb91d99865c252646bc
1 /******************************************************************************
3 * BigBlockFile
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.
14 * TODO:
15 * - Support for a transacted mode
17 * Copyright 1999 Thuy Nguyen
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
26 #include "windows.h"
27 #include "winerror.h"
28 #include "ole.h"
29 #include "ole2.h"
30 #include "wine/obj_base.h"
31 #include "wine/obj_storage.h"
33 #include "storage32.h"
35 /***********************************************************
36 * Data structures used internally by the BigBlockFile
37 * class.
40 /***
41 * Itdentifies a single big block and the related
42 * information
44 struct BigBlock
46 BigBlock * next;
47 DWORD index;
48 DWORD access_mode;
49 LPVOID lpBlock;
52 /***
53 * This structure identifies the paged that are mapped
54 * from the file and their position in memory. It is
55 * also used to hold a reference count to those pages.
57 struct MappedPage
59 MappedPage * next;
60 DWORD number;
61 int ref;
62 LPVOID lpBytes;
65 #define BLOCKS_PER_PAGE 128
66 #define PAGE_SIZE 65536
68 /***********************************************************
69 * Prototypes for private methods
71 static void* BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This,
72 DWORD pagenum,
73 DWORD desired_access);
74 static void BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This,
75 DWORD pagenum,
76 DWORD access);
77 static void BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This);
78 static void* BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This,
79 ULONG index,
80 DWORD desired_access);
81 static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer(LPBIGBLOCKFILE This,
82 void* pBlock);
83 static void BIGBLOCKFILE_RemoveBlock(LPBIGBLOCKFILE This,
84 ULONG index);
85 static BigBlock* BIGBLOCKFILE_AddBigBlock(LPBIGBLOCKFILE This,
86 ULONG index);
87 static BigBlock* BIGBLOCKFILE_CreateBlock(ULONG index);
89 /******************************************************************************
90 * BIGBLOCKFILE_Construct
92 * Construct a big block file. Create the file mapping object.
93 * Create the read only mapped pages list, the writeable mapped page list
94 * and the blocks in use list.
96 BigBlockFile * BIGBLOCKFILE_Construct(
97 HANDLE32 hFile,
98 ULONG blocksize)
100 LPBIGBLOCKFILE This;
102 This = (LPBIGBLOCKFILE)HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile));
104 if (This == NULL)
105 return NULL;
107 This->hfile = hFile;
109 if (This->hfile == INVALID_HANDLE_VALUE32)
111 HeapFree(GetProcessHeap(), 0, This);
112 return NULL;
115 /* create the file mapping object
117 This->hfilemap = CreateFileMapping32A(This->hfile,
118 NULL,
119 PAGE_READWRITE,
120 0, 0,
121 NULL);
123 if (This->hfilemap == NULL)
125 CloseHandle(This->hfile);
126 HeapFree(GetProcessHeap(), 0, This);
127 return NULL;
130 /* initialize this
132 This->filesize.LowPart = GetFileSize(This->hfile, NULL);
133 This->blocksize = blocksize;
135 /* create the read only mapped pages list
137 This->headmap_ro = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
139 if (This->headmap_ro == NULL)
141 CloseHandle(This->hfilemap);
142 CloseHandle(This->hfile);
143 HeapFree(GetProcessHeap(), 0, This);
144 return NULL;
147 This->headmap_ro->next = NULL;
149 /* create the writeable mapped pages list
151 This->headmap_w = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
153 if (This->headmap_w == NULL)
155 CloseHandle(This->hfilemap);
156 CloseHandle(This->hfile);
157 HeapFree(GetProcessHeap(), 0, This->headmap_ro);
158 HeapFree(GetProcessHeap(), 0, This);
159 return NULL;
162 This->headmap_w->next = NULL;
164 /* initialize the block list
166 This->headblock = NULL;
168 return This;
171 /******************************************************************************
172 * BIGBLOCKFILE_Destructor
174 * Destructor. Clean up, free memory.
176 void BIGBLOCKFILE_Destructor(
177 LPBIGBLOCKFILE This)
179 /* unmap all views and destroy the mapped page lists
181 BIGBLOCKFILE_FreeAllMappedPages(This);
182 HeapFree(GetProcessHeap(), 0, This->headmap_ro);
183 HeapFree(GetProcessHeap(), 0, This->headmap_w);
185 /* close all open handles
187 CloseHandle(This->hfilemap);
188 CloseHandle(This->hfile);
190 /* destroy this
192 HeapFree(GetProcessHeap(), 0, This);
195 /******************************************************************************
196 * BIGBLOCKFILE_GetROBigBlock
198 * Returns the specified block in read only mode.
199 * Will return NULL if the block doesn't exists.
201 void* BIGBLOCKFILE_GetROBigBlock(
202 LPBIGBLOCKFILE This,
203 ULONG index)
206 * block index starts at -1
207 * translate to zero based index
209 if (index == 0xffffffff)
210 index = 0;
211 else
212 index++;
215 * validate the block index
218 if ((This->blocksize * (index + 1)) >
219 (This->filesize.LowPart +
220 (This->blocksize - (This->filesize.LowPart % This->blocksize))))
221 return 0;
223 return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_READ);
226 /******************************************************************************
227 * BIGBLOCKFILE_GetBigBlock
229 * Returns the specified block.
230 * Will grow the file if necessary.
232 void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This, ULONG index)
235 * block index starts at -1
236 * translate to zero based index
238 if (index == 0xffffffff)
239 index = 0;
240 else
241 index++;
244 * make sure that the block physically exists
246 if ((This->blocksize * (index + 1)) > This->filesize.LowPart)
248 ULARGE_INTEGER newSize;
250 newSize.HighPart = 0;
251 newSize.LowPart = This->blocksize * (index + 1);
253 BIGBLOCKFILE_SetSize(This, newSize);
256 return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_WRITE);
259 /******************************************************************************
260 * BIGBLOCKFILE_ReleaseBigBlock
262 * Releases the specified block.
265 void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This, void *pBlock)
267 DWORD page_num;
268 BigBlock* theBigBlock;
270 if (pBlock == NULL)
271 return;
274 * get the block from the block list
276 theBigBlock = BIGBLOCKFILE_GetBigBlockFromPointer(This, pBlock);
278 if (theBigBlock == NULL)
279 return;
282 * find out which page this block is in
284 page_num = theBigBlock->index / BLOCKS_PER_PAGE;
287 * release this page
289 BIGBLOCKFILE_ReleaseMappedPage(This, page_num, theBigBlock->access_mode);
292 * remove block from list
294 BIGBLOCKFILE_RemoveBlock(This, theBigBlock->index);
297 /******************************************************************************
298 * BIGBLOCKFILE_SetSize
300 * Sets the size of the file.
303 void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This, ULARGE_INTEGER newSize)
305 if (This->filesize.LowPart == newSize.LowPart)
306 return;
309 * unmap all views, must be done before call to SetEndFile
311 BIGBLOCKFILE_FreeAllMappedPages(This);
314 * close file-mapping object, must be done before call to SetEndFile
316 CloseHandle(This->hfilemap);
317 This->hfilemap = NULL;
320 * set the new end of file
322 SetFilePointer(This->hfile, newSize.LowPart, NULL, FILE_BEGIN);
323 SetEndOfFile(This->hfile);
326 * re-create the file mapping object
328 This->hfilemap = CreateFileMapping32A(This->hfile,
329 NULL,
330 PAGE_READWRITE,
331 0, 0,
332 NULL);
334 This->filesize.LowPart = newSize.LowPart;
335 This->filesize.HighPart = newSize.HighPart;
338 /******************************************************************************
339 * BIGBLOCKFILE_GetSize
341 * Returns the size of the file.
344 ULARGE_INTEGER BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This)
346 return This->filesize;
349 /******************************************************************************
350 * BIGBLOCKFILE_GetBigBlockPointer [PRIVATE]
352 * Returns a pointer to the specified block.
354 static void* BIGBLOCKFILE_GetBigBlockPointer(
355 LPBIGBLOCKFILE This,
356 ULONG index,
357 DWORD desired_access)
359 DWORD page_num, block_num;
360 void * pBytes;
361 BigBlock *aBigBlock;
363 /* get the big block from the list or add it to the list
365 aBigBlock = BIGBLOCKFILE_AddBigBlock(This, index);
367 if (aBigBlock == NULL)
368 return NULL;
370 /* we already have an address for this block
372 if (aBigBlock->lpBlock != NULL)
374 /* make sure the desired access matches what we already have
376 if (aBigBlock->access_mode == desired_access)
377 return aBigBlock->lpBlock;
378 else
379 return NULL;
383 * else aBigBlock->lpBigBlock == NULL, it's a new block
386 /* find out which page this block is in
388 page_num = index / BLOCKS_PER_PAGE;
390 /* offset of the block in the page
392 block_num = index % BLOCKS_PER_PAGE;
394 /* get a pointer to the first byte in the page
396 pBytes = BIGBLOCKFILE_GetMappedView(This, page_num, desired_access);
398 if (pBytes == NULL)
399 return NULL;
401 /* initialize block
403 aBigBlock->lpBlock = ((BYTE*)pBytes + (block_num*This->blocksize));
404 aBigBlock->access_mode = desired_access;
406 return aBigBlock->lpBlock;
409 /******************************************************************************
410 * BIGBLOCKFILE_CreateBlock [PRIVATE]
412 * Creates a node of the blocks list.
414 static BigBlock* BIGBLOCKFILE_CreateBlock(
415 ULONG index)
417 BigBlock *newBigBlock;
419 /* create new list node
421 newBigBlock = HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlock));
423 if (newBigBlock == NULL)
424 return NULL;
426 /* initialize node
428 newBigBlock->index = index;
429 newBigBlock->lpBlock = NULL;
431 return newBigBlock;
434 /******************************************************************************
435 * BIGBLOCKFILE_AddBigBlock [PRIVATE]
437 * Returns the specified block from the blocks list.
438 * If the block is not found in the list, we will create it and add it to the
439 * list.
441 static BigBlock* BIGBLOCKFILE_AddBigBlock(
442 LPBIGBLOCKFILE This,
443 ULONG index)
445 BigBlock *current = This->headblock;
446 BigBlock *newBigBlock;
448 if (current == NULL) /* empty list */
450 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
452 if (newBigBlock != NULL)
454 newBigBlock->next = NULL;
455 This->headblock = newBigBlock;
458 return newBigBlock;
460 else
463 * special handling for head of the list
466 if (current->index == index) /* it's already here */
467 return current;
468 else if (current->index > index) /* insertion at head of the list */
470 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
472 if (newBigBlock != NULL)
474 newBigBlock->next = current;
475 This->headblock = newBigBlock;
478 return newBigBlock;
482 /* iterate through rest the list
484 while (current->next != NULL)
486 if (current->next->index == index) /* found it */
488 return current->next;
490 else if (current->next->index > index) /* it's not in the list */
492 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
494 if (newBigBlock != NULL)
496 newBigBlock->next = current->next;
497 current->next = newBigBlock;
500 return newBigBlock;
502 else
503 current = current->next;
507 * insertion at end of the list
509 if (current->next == NULL)
511 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
513 if (newBigBlock != NULL)
515 newBigBlock->next = NULL;
516 current->next = newBigBlock;
519 return newBigBlock;
522 return NULL;
525 /******************************************************************************
526 * BIGBLOCKFILE_RemoveBlock [PRIVATE]
528 * Removes the specified block from the blocks list.
530 static void BIGBLOCKFILE_RemoveBlock(
531 LPBIGBLOCKFILE This,
532 ULONG index)
534 BigBlock *current = This->headblock;
537 * empty list
539 if (current == NULL)
540 return;
543 *special case: removing head of list
545 if (current->index == index)
548 * set new head free the old one
550 This->headblock = current->next;
551 HeapFree(GetProcessHeap(), 0, current);
553 return;
557 * iterate through rest of the list
559 while (current->next != NULL)
561 if (current->next->index == index) /* found it */
564 * unlink the block and free the block
566 current->next = current->next->next;
567 HeapFree(GetProcessHeap(), 0, current->next);
569 return;
571 else
573 /* next node
575 current = current->next;
580 /******************************************************************************
581 * BIGBLOCKFILE_GetBigBlockFromPointer [PRIVATE]
583 * Given a block pointer, this will return the corresponding block
584 * from the blocks list.
586 static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer(
587 LPBIGBLOCKFILE This,
588 void* pBlock)
590 BigBlock *current = This->headblock;
592 while (current != NULL)
594 if (current->lpBlock == pBlock)
596 break;
598 else
599 current = current->next;
602 return current;
605 /******************************************************************************
606 * BIGBLOCKFILE_GetMappedView [PRIVATE]
608 * Gets the page requested if it is already mapped.
609 * If it's not already mapped, this method will map it
611 static void * BIGBLOCKFILE_GetMappedView(
612 LPBIGBLOCKFILE This,
613 DWORD pagenum,
614 DWORD desired_access)
616 MappedPage * current;
617 MappedPage * newMappedPage;
618 DWORD hioffset, lowoffset;
620 /* use correct list
622 if (desired_access == FILE_MAP_READ)
623 current = This->headmap_ro;
624 else if (desired_access == FILE_MAP_WRITE)
625 current = This->headmap_w;
626 else
627 return NULL;
629 hioffset = 0;
630 lowoffset = PAGE_SIZE * pagenum;
632 while (current->next != NULL)
634 if (current->next->number == pagenum) /* page already mapped */
636 current->next->ref++;
637 return current->next->lpBytes;
639 else if (current->next->number > pagenum) /* this page is not mapped yet */
641 /* allocate new MappedPage
643 newMappedPage = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
645 if (newMappedPage == NULL)
646 return NULL;
648 /* initialize the new MappedPage
650 newMappedPage->number = pagenum;
651 newMappedPage->ref = 1;
653 newMappedPage->next = current->next;
654 current->next = newMappedPage;
656 /* actually map the page
658 if (This->filesize.LowPart < PAGE_SIZE)
660 newMappedPage->lpBytes = MapViewOfFile(This->hfilemap,
661 desired_access,
662 hioffset, lowoffset,
663 This->filesize.LowPart);
665 else
667 newMappedPage->lpBytes = MapViewOfFile(This->hfilemap,
668 desired_access,
669 hioffset,
670 lowoffset,
671 PAGE_SIZE);
674 return newMappedPage->lpBytes;
676 else
677 current = current->next;
680 /* reached end of the list, this view is not mapped yet
682 if (current->next == NULL)
684 /* allocate new MappedPage
686 newMappedPage = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
688 if (newMappedPage == NULL)
689 return NULL;
691 /* initialize the new MappedPage
693 newMappedPage->number = pagenum;
694 newMappedPage->ref = 1;
696 newMappedPage->next = NULL;
697 current->next = newMappedPage;
699 /* actually map the page
701 if (This->filesize.LowPart < PAGE_SIZE)
703 newMappedPage->lpBytes = MapViewOfFile(This->hfilemap,
704 desired_access,
705 hioffset,
706 lowoffset,
707 This->filesize.LowPart);
709 else
711 newMappedPage->lpBytes = MapViewOfFile(This->hfilemap,
712 desired_access,
713 hioffset,
714 lowoffset,
715 PAGE_SIZE);
718 return newMappedPage->lpBytes;
721 return NULL;
724 /******************************************************************************
725 * BIGBLOCKFILE_ReleaseMappedPage [PRIVATE]
727 * Decrements the reference count of the mapped page.
728 * If the page is not used anymore it will be unmapped.
730 static void BIGBLOCKFILE_ReleaseMappedPage(
731 LPBIGBLOCKFILE This,
732 DWORD pagenum,
733 DWORD access)
735 MappedPage * previous;
736 MappedPage * current;
738 /* use the list corresponding to the desired access mode
740 if (access == FILE_MAP_READ)
741 previous = This->headmap_ro;
742 else if (access == FILE_MAP_WRITE)
743 previous = This->headmap_w;
744 else
745 return;
747 current = previous->next;
749 /* search for the page in the list
751 while (current != NULL)
753 if (current->number == pagenum)
755 /* decrement the reference count
757 current->ref--;
759 if (current->ref == 0)
761 /* this page is not used anymore, we can unmap it
763 UnmapViewOfFile(current->lpBytes);
765 previous->next = current->next;
766 HeapFree(GetProcessHeap(), 0, current);
769 return;
771 else
773 previous = current;
774 current = current->next;
779 /******************************************************************************
780 * BIGBLOCKFILE_FreeAllMappedPages [PRIVATE]
782 * Unmap all currently mapped pages.
783 * Empty both mapped pages lists.
785 static void BIGBLOCKFILE_FreeAllMappedPages(
786 LPBIGBLOCKFILE This)
789 * start with the read only list
791 MappedPage * current = This->headmap_ro->next;
793 while (current != NULL)
795 /* unmap views
797 UnmapViewOfFile(current->lpBytes);
799 /* free the nodes
801 This->headmap_ro->next = current->next;
802 HeapFree(GetProcessHeap(), 0, current);
804 current = This->headmap_ro->next;
808 * then do the write list
810 current = This->headmap_w->next;
812 while (current != NULL)
814 /* unmap views
816 UnmapViewOfFile(current->lpBytes);
818 /* free the nodes
820 This->headmap_w->next = current->next;
821 HeapFree(GetProcessHeap(), 0, current);
823 current = This->headmap_w->next;