ntdll/tests: Test NtCreateFile with an invalid filename.
[wine.git] / dlls / storage.dll16 / storage.c
blobbf79e331e0886d39f22164509028686cdc8cdcf7
1 /* Compound Storage
3 * Implemented using the documentation of the LAOLA project at
4 * <URL:http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/index.html>
5 * (Thanks to Martin Schwartz <schwartz@cs.tu-berlin.de>)
7 * Copyright 1998 Marcus Meissner
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
26 #include <assert.h>
27 #include <time.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winternl.h"
40 #include "winerror.h"
41 #include "wine/winbase16.h"
42 #include "wownt32.h"
43 #include "wine/unicode.h"
44 #include "objbase.h"
45 #include "wine/debug.h"
47 #include "ifs.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(ole);
50 WINE_DECLARE_DEBUG_CHANNEL(relay);
52 struct storage_header {
53 BYTE magic[8]; /* 00: magic */
54 BYTE unknown1[36]; /* 08: unknown */
55 DWORD num_of_bbd_blocks;/* 2C: length of big datablocks */
56 DWORD root_startblock;/* 30: root storage first big block */
57 DWORD unknown2[2]; /* 34: unknown */
58 DWORD sbd_startblock; /* 3C: small block depot first big block */
59 DWORD unknown3[3]; /* 40: unknown */
60 DWORD bbd_list[109]; /* 4C: big data block list (up to end of sector)*/
62 struct storage_pps_entry {
63 WCHAR pps_rawname[32];/* 00: \0 terminated widechar name */
64 WORD pps_sizeofname; /* 40: namelength in bytes */
65 BYTE pps_type; /* 42: flags, 1 storage/dir, 2 stream, 5 root */
66 BYTE pps_unknown0; /* 43: unknown */
67 DWORD pps_prev; /* 44: previous pps */
68 DWORD pps_next; /* 48: next pps */
69 DWORD pps_dir; /* 4C: directory pps */
70 GUID pps_guid; /* 50: class ID */
71 DWORD pps_unknown1; /* 60: unknown */
72 FILETIME pps_ft1; /* 64: filetime1 */
73 FILETIME pps_ft2; /* 70: filetime2 */
74 DWORD pps_sb; /* 74: data startblock */
75 DWORD pps_size; /* 78: datalength. (<0x1000)?small:big blocks*/
76 DWORD pps_unknown2; /* 7C: unknown */
79 #define STORAGE_CHAINENTRY_FAT 0xfffffffd
80 #define STORAGE_CHAINENTRY_ENDOFCHAIN 0xfffffffe
81 #define STORAGE_CHAINENTRY_FREE 0xffffffff
84 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
86 #define BIGSIZE 512
87 #define SMALLSIZE 64
89 #define SMALLBLOCKS_PER_BIGBLOCK (BIGSIZE/SMALLSIZE)
91 #define READ_HEADER(str) STORAGE_get_big_block(str,-1,(LPBYTE)&sth);assert(!memcmp(STORAGE_magic,sth.magic,sizeof(STORAGE_magic)));
92 static IStorage16Vtbl stvt16;
93 static const IStorage16Vtbl *segstvt16 = NULL;
94 static IStream16Vtbl strvt16;
95 static const IStream16Vtbl *segstrvt16 = NULL;
97 /*ULONG WINAPI IStorage16_AddRef(LPSTORAGE16 this);*/
98 static void _create_istorage16(LPSTORAGE16 *stg);
99 static void _create_istream16(LPSTREAM16 *str);
101 #define IMPLEMENTED 1
103 /* The following is taken from the CorVu implementation of docfiles, and
104 * documents things about the file format that are not implemented here, and
105 * not documented by the LAOLA project. The CorVu implementation was posted
106 * to wine-devel in February 2004, and released under the LGPL at the same
107 * time. Because that implementation is in C++, it's not directly usable in
108 * Wine, but does have documentation value.
111 * #define DF_EXT_VTOC -4
112 * #define DF_VTOC_VTOC -3
113 * #define DF_VTOC_EOF -2
114 * #define DF_VTOC_FREE -1
115 * #define DF_NAMELEN 0x20 // Maximum entry name length - 31 characters plus
116 * // a NUL terminator
118 * #define DF_FT_STORAGE 1
119 * #define DF_FT_STREAM 2
120 * #define DF_FT_LOCKBYTES 3 // Not used -- How the bloody hell did I manage
121 * #define DF_FT_PROPERTY 4 // Not Used -- to figure these two out?
122 * #define DF_FT_ROOT 5
124 * #define DF_BLOCK_SIZE 0x200
125 * #define DF_VTOC_SIZE 0x80
126 * #define DF_DE_PER_BLOCK 4
127 * #define DF_STREAM_BLOCK_SIZE 0x40
129 * A DocFile is divided into blocks of 512 bytes.
130 * The first block contains the header.
132 * The file header contains The first 109 entries in the VTOC of VTOCs.
134 * Each block pointed to by a VTOC of VTOCs contains a VTOC, which
135 * includes block chains - just like FAT. This is a somewhat poor
136 * design for the following reasons:
138 * 1. FAT was a poor file system design to begin with, and
139 * has long been known to be horrendously inefficient
140 * for day to day operations.
142 * 2. The problem is compounded here, since the file
143 * level streams are generally *not* read sequentially.
144 * This means that a significant percentage of reads
145 * require seeking from the start of the chain.
147 * Data chains also contain an internal VTOC. The block size for
148 * the standard VTOC is 512. The block size for the internal VTOC
149 * is 64.
151 * Now, the 109 blocks in the VTOC of VTOCs allows for files of
152 * up to around 7MB. So what do you think happens if that's
153 * exceeded? Well, there's an entry in the header block which
154 * points to the first block used as additional storage for
155 * the VTOC of VTOCs.
157 * Now we can get up to around 15MB. Now, guess how the file
158 * format adds in another block to the VTOC of VTOCs. Come on,
159 * it's no big surprise. That's right - the last entry in each
160 * block extending the VTOC of VTOCs is, you guessed it, the
161 * block number of the next block containing an extension to
162 * the VTOC of VTOCs. The VTOC of VTOCs is chained!!!!
164 * So, to review:
166 * 1. If you are using a FAT file system, the location of
167 * your file's blocks is stored in chains.
169 * 2. At the abstract level, the file contains a VTOC of VTOCs,
170 * which is stored in the most inefficient possible format for
171 * random access - a chain (AKA list).
173 * 3. The VTOC of VTOCs contains descriptions of three file level
174 * streams:
176 * a. The Directory stream
177 * b. The Data stream
178 * c. The Data VTOC stream
180 * These are, of course, represented as chains.
182 * 4. The Data VTOC contains data describing the chains of blocks
183 * within the Data stream.
185 * That's right - we have a total of four levels of block chains!
187 * Now, is that complicated enough for you? No? OK, there's another
188 * complication. If an individual stream (ie. an IStream) reaches
189 * 4096 bytes in size, it gets moved from the Data Stream to
190 * a new file level stream. Now, if the stream then gets truncated
191 * back to less than 4096 bytes, it returns to the data stream.
193 * The effect of using this format can be seen very easily. Pick
194 * an arbitrary application with a grid data representation that
195 * can export to both Lotus 123 and Excel 5 or higher. Export
196 * a large file to Lotus 123 and time it. Export the same thing
197 * to Excel 5 and time that. The difference is the inefficiency
198 * of the Microsoft DocFile format.
201 * #define TOTAL_SIMPLE_VTOCS 109
203 * struct DocFile_Header
205 * df_byte iMagic1; // 0xd0
206 * df_byte iMagic2; // 0xcf
207 * df_byte iMagic3; // 0x11
208 * df_byte iMagic4; // 0xe0 - Spells D0CF11E0, or DocFile
209 * df_byte iMagic5; // 161 (igi upside down)
210 * df_byte iMagic6; // 177 (lli upside down - see below
211 * df_byte iMagic7; // 26 (gz upside down)
212 * df_byte iMagic8; // 225 (szz upside down) - see below
213 * df_int4 aiUnknown1[4];
214 * df_int4 iVersion; // DocFile Version - 0x03003E
215 * df_int4 aiUnknown2[4];
216 * df_int4 nVTOCs; // Number of VTOCs
217 * df_int4 iFirstDirBlock; // First Directory Block
218 * df_int4 aiUnknown3[2];
219 * df_int4 iFirstDataVTOC; // First data VTOC block
220 * df_int4 iHasData; // 1 if there is data in the file - yes, this is important
221 * df_int4 iExtendedVTOC; // Extended VTOC location
222 * df_int4 iExtendedVTOCSize; // Size of extended VTOC (+1?)
223 * df_int4 aiVTOCofVTOCs[TOTAL_SIMPLE_VTOCS];
224 * };
226 * struct DocFile_VTOC
228 * df_int4 aiBlocks[DF_VTOC_SIZE];
229 * };
232 * The meaning of the magic numbers
234 * 0xd0cf11e0 is DocFile with a zero on the end (sort of)
236 * If you key 177161 into a calculator, then turn the calculator
237 * upside down, you get igilli, which may be a reference to
238 * somebody's name, or to the Hebrew word for "angel".
240 * If you key 26225 into a calculator, then turn it upside down, you
241 * get szzgz. Microsoft has a tradition of creating nonsense words
242 * using the letters s, g, z and y. We think szzgz may be one of the
243 * Microsoft placeholder variables, along the lines of foo, bar and baz.
244 * Alternatively, it could be 22526, which would be gzszz.
247 * struct DocFile_DirEnt
249 * df_char achEntryName[DF_NAMELEN]; // Entry Name
250 * df_int2 iNameLen; // Name length in bytes, including NUL terminator
251 * df_byte iFileType; // Entry type
252 * df_byte iColour; // 1 = Black, 0 = Red
253 * df_int4 iLeftSibling; // Next Left Sibling Entry - See below
254 * df_int4 iRightSibling; // Next Right Sibling Entry
255 * df_int4 iFirstChild; // First Child Entry
256 * df_byte achClassID[16]; // Class ID
257 * df_int4 iStateBits; // [GS]etStateBits value
258 * df_int4 iCreatedLow; // Low DWORD of creation time
259 * df_int4 iCreatedHigh; // High DWORD of creation time
260 * df_int4 iModifiedLow; // Low DWORD of modification time
261 * df_int4 iModifiedHigh; // High DWORD of modification time
262 * df_int4 iVTOCPosition; // VTOC Position
263 * df_int4 iFileSize; // Size of the stream
264 * df_int4 iZero; // We think this is part of the 64 bit stream size - must be 0
265 * };
267 * Siblings
268 * ========
270 * Siblings are stored in an obscure but incredibly elegant
271 * data structure called a red-black tree. This is generally
272 * defined as a 2-3-4 tree stored in a binary tree.
274 * A red-black tree can always be balanced very easily. The rules
275 * for a red-black tree are as follows:
277 * 1. The root node is always black.
278 * 2. The parent of a red node is always black.
280 * There is a Java demo of red-black trees at:
282 * http://langevin.usc.edu/BST/RedBlackTree-Example.html
284 * This demo is an excellent tool for learning how red-black
285 * trees work, without having to go through the process of
286 * learning how they were derived.
288 * Within the tree, elements are ordered by the length of the
289 * name and within that, ASCII order by name. This causes the
290 * apparently bizarre reordering you see when you use dfview.
292 * This is a somewhat bizarre choice. It suggests that the
293 * designer of the DocFile format was trying to optimise
294 * searching through the directory entries. However searching
295 * through directory entries is a relatively rare operation.
296 * Reading and seeking within a stream are much more common
297 * operations, especially within the file level streams, yet
298 * these use the horrendously inefficient FAT chains.
300 * This suggests that the designer was probably somebody
301 * fresh out of university, who had some basic knowledge of
302 * basic data structures, but little knowledge of anything
303 * more practical. It is bizarre to attempt to optimise
304 * directory searches while not using a more efficient file
305 * block locating system than FAT (seedling/sapling/tree
306 * would result in a massive improvement - in fact we have
307 * an alternative to docfiles that we use internally that
308 * uses seedling/sapling/tree and *is* far more efficient).
310 * It is worth noting that the MS implementation of red-black
311 * trees is incorrect (I can tell you're surprised) and
312 * actually causes more operations to occur than are really
313 * needed. Fortunately the fact that our implementation is
314 * correct will not cause any problems - the MS implementation
315 * still appears to cause the tree to satisfy the rules, albeit
316 * a sequence of the same insertions in the different
317 * implementations may result in a different, and possibly
318 * deeper (but never shallower) tree.
321 typedef struct {
322 HANDLE hf;
323 SEGPTR lockbytes;
324 } stream_access16;
325 /* --- IStorage16 implementation struct */
327 typedef struct
329 /* IUnknown fields */
330 const IStorage16Vtbl *lpVtbl;
331 LONG ref;
332 /* IStorage16 fields */
333 SEGPTR thisptr; /* pointer to this struct as segmented */
334 struct storage_pps_entry stde;
335 int ppsent;
336 stream_access16 str;
337 } IStorage16Impl;
340 /******************************************************************************
341 * STORAGE_get_big_block [Internal]
343 * Reading OLE compound storage
345 static BOOL
346 STORAGE_get_big_block(stream_access16 *str,int n,BYTE *block)
348 DWORD result;
350 assert(n>=-1);
351 if (str->hf) {
352 if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL,
353 SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError())
355 WARN("(%p,%d,%p), seek failed (%d)\n",str->hf, n, block, GetLastError());
356 return FALSE;
358 if (!ReadFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
360 WARN("(hf=%p, block size %d): read didn't read (%d)\n",str->hf,n,GetLastError());
361 return FALSE;
363 } else {
364 DWORD args[6];
365 HRESULT hres;
366 HANDLE16 hsig;
368 args[0] = (DWORD)str->lockbytes; /* iface */
369 args[1] = (n+1)*BIGSIZE;
370 args[2] = 0; /* ULARGE_INTEGER offset */
371 args[3] = WOWGlobalAllocLock16( 0, BIGSIZE, &hsig ); /* sig */
372 args[4] = BIGSIZE;
373 args[5] = 0;
375 if (!WOWCallback16Ex(
376 (DWORD)((const ILockBytes16Vtbl*)MapSL(
377 (SEGPTR)((LPLOCKBYTES16)MapSL(str->lockbytes))->lpVtbl)
378 )->ReadAt,
379 WCB16_PASCAL,
380 6*sizeof(DWORD),
381 (LPVOID)args,
382 (LPDWORD)&hres
383 )) {
384 ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %x\n",hres);
385 return FALSE;
387 memcpy(block, MapSL(args[3]), BIGSIZE);
388 WOWGlobalUnlockFree16(args[3]);
390 return TRUE;
393 static BOOL
394 _ilockbytes16_writeat(SEGPTR lockbytes, DWORD offset, DWORD length, void *buffer) {
395 DWORD args[6];
396 HRESULT hres;
398 args[0] = (DWORD)lockbytes; /* iface */
399 args[1] = offset;
400 args[2] = 0; /* ULARGE_INTEGER offset */
401 args[3] = (DWORD)MapLS( buffer );
402 args[4] = length;
403 args[5] = 0;
405 /* THIS_ ULARGE_INTEGER ulOffset, const void *pv, ULONG cb, ULONG *pcbWritten); */
407 if (!WOWCallback16Ex(
408 (DWORD)((const ILockBytes16Vtbl*)MapSL(
409 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
410 )->WriteAt,
411 WCB16_PASCAL,
412 6*sizeof(DWORD),
413 (LPVOID)args,
414 (LPDWORD)&hres
415 )) {
416 ERR("CallTo16 ILockBytes16::WriteAt() failed, hres %x\n",hres);
417 return FALSE;
419 UnMapLS(args[3]);
420 return TRUE;
423 /******************************************************************************
424 * STORAGE_put_big_block [INTERNAL]
426 static BOOL
427 STORAGE_put_big_block(stream_access16 *str,int n,BYTE *block)
429 DWORD result;
431 assert(n>=-1);
432 if (str->hf) {
433 if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL,
434 SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError())
436 WARN("seek failed (%d)\n",GetLastError());
437 return FALSE;
439 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
441 WARN(" write failed (%d)\n",GetLastError());
442 return FALSE;
444 return TRUE;
445 } else {
446 _ilockbytes16_writeat(str->lockbytes, (n+1)*BIGSIZE, BIGSIZE, block);
447 return TRUE;
451 /******************************************************************************
452 * STORAGE_get_next_big_blocknr [INTERNAL]
454 static int
455 STORAGE_get_next_big_blocknr(stream_access16 *str,int blocknr) {
456 INT bbs[BIGSIZE/sizeof(INT)];
457 struct storage_header sth;
459 READ_HEADER(str);
461 assert(blocknr>>7<sth.num_of_bbd_blocks);
462 if (sth.bbd_list[blocknr>>7]==0xffffffff)
463 return -5;
464 if (!STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs))
465 return -5;
466 assert(bbs[blocknr&0x7f]!=STORAGE_CHAINENTRY_FREE);
467 return bbs[blocknr&0x7f];
470 /******************************************************************************
471 * STORAGE_get_nth_next_big_blocknr [INTERNAL]
473 static int
474 STORAGE_get_nth_next_big_blocknr(stream_access16 *str,int blocknr,int nr) {
475 INT bbs[BIGSIZE/sizeof(INT)];
476 int lastblock = -1;
477 struct storage_header sth;
479 TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr);
480 READ_HEADER(str);
482 assert(blocknr>=0);
483 while (nr--) {
484 assert((blocknr>>7)<sth.num_of_bbd_blocks);
485 assert(sth.bbd_list[blocknr>>7]!=0xffffffff);
487 /* simple caching... */
488 if (lastblock!=sth.bbd_list[blocknr>>7]) {
489 BOOL ret = STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs);
490 assert(ret);
491 lastblock = sth.bbd_list[blocknr>>7];
493 blocknr = bbs[blocknr&0x7f];
495 return blocknr;
498 /******************************************************************************
499 * STORAGE_get_root_pps_entry [Internal]
501 static BOOL
502 STORAGE_get_root_pps_entry(stream_access16* str,struct storage_pps_entry *pstde) {
503 int blocknr,i;
504 BYTE block[BIGSIZE];
505 struct storage_pps_entry *stde=(struct storage_pps_entry*)block;
506 struct storage_header sth;
508 READ_HEADER(str);
509 blocknr = sth.root_startblock;
510 TRACE("startblock is %d\n", blocknr);
511 while (blocknr>=0) {
512 BOOL ret = STORAGE_get_big_block(str,blocknr,block);
513 assert(ret);
514 for (i=0;i<4;i++) {
515 if (!stde[i].pps_sizeofname)
516 continue;
517 if (stde[i].pps_type==5) {
518 *pstde=stde[i];
519 return TRUE;
522 blocknr=STORAGE_get_next_big_blocknr(str,blocknr);
523 TRACE("next block is %d\n", blocknr);
525 return FALSE;
528 /******************************************************************************
529 * STORAGE_get_small_block [INTERNAL]
531 static BOOL
532 STORAGE_get_small_block(stream_access16 *str,int blocknr,BYTE *sblock) {
533 BYTE block[BIGSIZE];
534 int bigblocknr;
535 struct storage_pps_entry root;
536 BOOL ret;
538 TRACE("(blocknr=%d)\n", blocknr);
539 assert(blocknr>=0);
540 ret = STORAGE_get_root_pps_entry(str,&root);
541 assert(ret);
542 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
543 assert(bigblocknr>=0);
544 ret = STORAGE_get_big_block(str,bigblocknr,block);
545 assert(ret);
547 memcpy(sblock,((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),SMALLSIZE);
548 return TRUE;
551 /******************************************************************************
552 * STORAGE_put_small_block [INTERNAL]
554 static BOOL
555 STORAGE_put_small_block(stream_access16 *str,int blocknr,const BYTE *sblock) {
556 BYTE block[BIGSIZE];
557 int bigblocknr;
558 struct storage_pps_entry root;
559 BOOL ret;
561 assert(blocknr>=0);
562 TRACE("(blocknr=%d)\n", blocknr);
564 ret = STORAGE_get_root_pps_entry(str,&root);
565 assert(ret);
566 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
567 assert(bigblocknr>=0);
568 ret = STORAGE_get_big_block(str,bigblocknr,block);
569 assert(ret);
571 memcpy(((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),sblock,SMALLSIZE);
572 ret = STORAGE_put_big_block(str,bigblocknr,block);
573 assert(ret);
574 return TRUE;
577 /******************************************************************************
578 * STORAGE_get_next_small_blocknr [INTERNAL]
580 static int
581 STORAGE_get_next_small_blocknr(stream_access16 *str,int blocknr) {
582 BYTE block[BIGSIZE];
583 LPINT sbd = (LPINT)block;
584 int bigblocknr;
585 struct storage_header sth;
586 BOOL ret;
588 TRACE("(blocknr=%d)\n", blocknr);
589 READ_HEADER(str);
590 assert(blocknr>=0);
591 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
592 assert(bigblocknr>=0);
593 ret = STORAGE_get_big_block(str,bigblocknr,block);
594 assert(ret);
595 assert(sbd[blocknr & 127]!=STORAGE_CHAINENTRY_FREE);
596 return sbd[blocknr & (128-1)];
599 /******************************************************************************
600 * STORAGE_get_nth_next_small_blocknr [INTERNAL]
602 static int
603 STORAGE_get_nth_next_small_blocknr(stream_access16*str,int blocknr,int nr) {
604 int lastblocknr=-1;
605 BYTE block[BIGSIZE];
606 LPINT sbd = (LPINT)block;
607 struct storage_header sth;
608 BOOL ret;
610 TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr);
611 READ_HEADER(str);
612 assert(blocknr>=0);
613 while ((nr--) && (blocknr>=0)) {
614 if (lastblocknr/128!=blocknr/128) {
615 int bigblocknr;
616 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
617 assert(bigblocknr>=0);
618 ret = STORAGE_get_big_block(str,bigblocknr,block);
619 assert(ret);
620 lastblocknr = blocknr;
622 assert(lastblocknr>=0);
623 lastblocknr=blocknr;
624 blocknr=sbd[blocknr & (128-1)];
625 assert(blocknr!=STORAGE_CHAINENTRY_FREE);
627 return blocknr;
630 /******************************************************************************
631 * STORAGE_get_pps_entry [INTERNAL]
633 static int
634 STORAGE_get_pps_entry(stream_access16*str,int n,struct storage_pps_entry *pstde) {
635 int blocknr;
636 BYTE block[BIGSIZE];
637 struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
638 struct storage_header sth;
639 BOOL ret;
641 TRACE("(n=%d)\n", n);
642 READ_HEADER(str);
643 /* we have 4 pps entries per big block */
644 blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4);
645 assert(blocknr>=0);
646 ret = STORAGE_get_big_block(str,blocknr,block);
647 assert(ret);
649 *pstde=*stde;
650 return 1;
653 /******************************************************************************
654 * STORAGE_put_pps_entry [Internal]
656 static int
657 STORAGE_put_pps_entry(stream_access16*str,int n,const struct storage_pps_entry *pstde) {
658 int blocknr;
659 BYTE block[BIGSIZE];
660 struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
661 struct storage_header sth;
662 BOOL ret;
664 TRACE("(n=%d)\n", n);
665 READ_HEADER(str);
666 /* we have 4 pps entries per big block */
667 blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4);
668 assert(blocknr>=0);
669 ret = STORAGE_get_big_block(str,blocknr,block);
670 assert(ret);
671 *stde=*pstde;
672 ret = STORAGE_put_big_block(str,blocknr,block);
673 assert(ret);
674 return 1;
677 /******************************************************************************
678 * STORAGE_look_for_named_pps [Internal]
680 static int
681 STORAGE_look_for_named_pps(stream_access16*str,int n,LPOLESTR name) {
682 struct storage_pps_entry stde;
683 int ret;
685 TRACE("(n=%d,name=%s)\n", n, debugstr_w(name));
686 if (n==-1)
687 return -1;
688 if (1!=STORAGE_get_pps_entry(str,n,&stde))
689 return -1;
691 if (!lstrcmpW(name,stde.pps_rawname))
692 return n;
693 if (stde.pps_prev != -1) {
694 ret=STORAGE_look_for_named_pps(str,stde.pps_prev,name);
695 if (ret!=-1)
696 return ret;
698 if (stde.pps_next != -1) {
699 ret=STORAGE_look_for_named_pps(str,stde.pps_next,name);
700 if (ret!=-1)
701 return ret;
703 return -1;
706 /******************************************************************************
707 * STORAGE_dump_pps_entry [Internal]
709 * This function is there to simplify debugging. It is otherwise unused.
711 void
712 STORAGE_dump_pps_entry(struct storage_pps_entry *stde) {
713 char name[33];
715 WideCharToMultiByte( CP_ACP, 0, stde->pps_rawname, -1, name, sizeof(name), NULL, NULL);
716 if (!stde->pps_sizeofname)
717 return;
718 TRACE("name: %s\n",name);
719 TRACE("type: %d\n",stde->pps_type);
720 TRACE("prev pps: %d\n",stde->pps_prev);
721 TRACE("next pps: %d\n",stde->pps_next);
722 TRACE("dir pps: %d\n",stde->pps_dir);
723 TRACE("guid: %s\n",debugstr_guid(&(stde->pps_guid)));
724 if (stde->pps_type !=2) {
725 time_t t;
726 DWORD dw;
727 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft1),&dw);
728 t = dw;
729 TRACE("ts1: %s\n",ctime(&t));
730 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft2),&dw);
731 t = dw;
732 TRACE("ts2: %s\n",ctime(&t));
734 TRACE("startblock: %d\n",stde->pps_sb);
735 TRACE("size: %d\n",stde->pps_size);
738 /******************************************************************************
739 * STORAGE_init_storage [INTERNAL]
741 static BOOL
742 STORAGE_init_storage(stream_access16 *str) {
743 BYTE block[BIGSIZE];
744 LPDWORD bbs;
745 struct storage_header *sth;
746 struct storage_pps_entry *stde;
747 DWORD result;
749 if (str->hf)
750 SetFilePointer( str->hf, 0, NULL, SEEK_SET );
751 /* block -1 is the storage header */
752 sth = (struct storage_header*)block;
753 memcpy(sth->magic,STORAGE_magic,8);
754 memset(sth->unknown1,0,sizeof(sth->unknown1));
755 memset(sth->unknown2,0,sizeof(sth->unknown2));
756 memset(sth->unknown3,0,sizeof(sth->unknown3));
757 sth->num_of_bbd_blocks = 1;
758 sth->root_startblock = 1;
759 sth->sbd_startblock = 0xffffffff;
760 memset(sth->bbd_list,0xff,sizeof(sth->bbd_list));
761 sth->bbd_list[0] = 0;
762 if (str->hf) {
763 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
764 } else {
765 if (!_ilockbytes16_writeat(str->lockbytes, 0, BIGSIZE, block)) return FALSE;
767 /* block 0 is the big block directory */
768 bbs=(LPDWORD)block;
769 memset(block,0xff,sizeof(block)); /* mark all blocks as free */
770 bbs[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for this block */
771 bbs[1]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for directory entry */
772 if (str->hf) {
773 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
774 } else {
775 if (!_ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block)) return FALSE;
777 /* block 1 is the root directory entry */
778 memset(block,0x00,sizeof(block));
779 stde = (struct storage_pps_entry*)block;
780 MultiByteToWideChar( CP_ACP, 0, "RootEntry", -1, stde->pps_rawname,
781 sizeof(stde->pps_rawname)/sizeof(WCHAR));
782 stde->pps_sizeofname = (strlenW(stde->pps_rawname)+1) * sizeof(WCHAR);
783 stde->pps_type = 5;
784 stde->pps_dir = -1;
785 stde->pps_next = -1;
786 stde->pps_prev = -1;
787 stde->pps_sb = 0xffffffff;
788 stde->pps_size = 0;
789 if (str->hf) {
790 return (WriteFile( str->hf, block, BIGSIZE, &result, NULL ) && result == BIGSIZE);
791 } else {
792 return _ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block);
796 /******************************************************************************
797 * STORAGE_set_big_chain [Internal]
799 static BOOL
800 STORAGE_set_big_chain(stream_access16*str,int blocknr,INT type) {
801 BYTE block[BIGSIZE];
802 LPINT bbd = (LPINT)block;
803 int nextblocknr,bigblocknr;
804 struct storage_header sth;
805 BOOL ret;
807 READ_HEADER(str);
808 assert(blocknr!=type);
809 while (blocknr>=0) {
810 bigblocknr = sth.bbd_list[blocknr/128];
811 assert(bigblocknr>=0);
812 ret = STORAGE_get_big_block(str,bigblocknr,block);
813 assert(ret);
815 nextblocknr = bbd[blocknr&(128-1)];
816 bbd[blocknr&(128-1)] = type;
817 if (type>=0)
818 return TRUE;
819 ret = STORAGE_put_big_block(str,bigblocknr,block);
820 assert(ret);
821 type = STORAGE_CHAINENTRY_FREE;
822 blocknr = nextblocknr;
824 return TRUE;
827 /******************************************************************************
828 * STORAGE_set_small_chain [Internal]
830 static BOOL
831 STORAGE_set_small_chain(stream_access16*str,int blocknr,INT type) {
832 BYTE block[BIGSIZE];
833 LPINT sbd = (LPINT)block;
834 int lastblocknr,nextsmallblocknr,bigblocknr;
835 struct storage_header sth;
836 BOOL ret;
838 READ_HEADER(str);
840 assert(blocknr!=type);
841 lastblocknr=-129;bigblocknr=-2;
842 while (blocknr>=0) {
843 /* cache block ... */
844 if (lastblocknr/128!=blocknr/128) {
845 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
846 assert(bigblocknr>=0);
847 ret = STORAGE_get_big_block(str,bigblocknr,block);
848 assert(ret);
850 lastblocknr = blocknr;
851 nextsmallblocknr = sbd[blocknr&(128-1)];
852 sbd[blocknr&(128-1)] = type;
853 ret = STORAGE_put_big_block(str,bigblocknr,block);
854 assert(ret);
855 if (type>=0)
856 return TRUE;
857 type = STORAGE_CHAINENTRY_FREE;
858 blocknr = nextsmallblocknr;
860 return TRUE;
863 /******************************************************************************
864 * STORAGE_get_free_big_blocknr [Internal]
866 static int
867 STORAGE_get_free_big_blocknr(stream_access16 *str) {
868 BYTE block[BIGSIZE];
869 LPINT sbd = (LPINT)block;
870 int lastbigblocknr,i,bigblocknr;
871 unsigned int curblock;
872 struct storage_header sth;
873 BOOL ret;
875 READ_HEADER(str);
876 curblock = 0;
877 lastbigblocknr = -1;
878 bigblocknr = sth.bbd_list[curblock];
879 while (curblock<sth.num_of_bbd_blocks) {
880 assert(bigblocknr>=0);
881 ret = STORAGE_get_big_block(str,bigblocknr,block);
882 assert(ret);
883 for (i=0;i<128;i++)
884 if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
885 sbd[i] = STORAGE_CHAINENTRY_ENDOFCHAIN;
886 ret = STORAGE_put_big_block(str,bigblocknr,block);
887 assert(ret);
888 memset(block,0x42,sizeof(block));
889 ret = STORAGE_put_big_block(str,i+curblock*128,block);
890 assert(ret);
891 return i+curblock*128;
893 lastbigblocknr = bigblocknr;
894 bigblocknr = sth.bbd_list[++curblock];
896 bigblocknr = curblock*128;
897 /* since we have marked all blocks from 0 up to curblock*128-1
898 * the next free one is curblock*128, where we happily put our
899 * next large block depot.
901 memset(block,0xff,sizeof(block));
902 /* mark the block allocated and returned by this function */
903 sbd[1] = STORAGE_CHAINENTRY_ENDOFCHAIN;
904 ret = STORAGE_put_big_block(str,bigblocknr,block);
905 assert(ret);
907 /* if we had a bbd block already (mostlikely) we need
908 * to link the new one into the chain
910 if (lastbigblocknr!=-1) {
911 ret = STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr);
912 assert(ret);
914 sth.bbd_list[curblock]=bigblocknr;
915 sth.num_of_bbd_blocks++;
916 assert(sth.num_of_bbd_blocks==curblock+1);
917 ret = STORAGE_put_big_block(str,-1,(LPBYTE)&sth);
918 assert(ret);
920 /* Set the end of the chain for the bigblockdepots */
921 ret = STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN);
922 assert(ret);
923 /* add 1, for the first entry is used for the additional big block
924 * depot. (means we already used bigblocknr) */
925 memset(block,0x42,sizeof(block));
926 /* allocate this block (filled with 0x42) */
927 ret = STORAGE_put_big_block(str,bigblocknr+1,block);
928 assert(ret);
929 return bigblocknr+1;
933 /******************************************************************************
934 * STORAGE_get_free_small_blocknr [Internal]
936 static int
937 STORAGE_get_free_small_blocknr(stream_access16 *str) {
938 BYTE block[BIGSIZE];
939 LPINT sbd = (LPINT)block;
940 int lastbigblocknr,newblocknr,i,curblock,bigblocknr;
941 struct storage_pps_entry root;
942 struct storage_header sth;
944 READ_HEADER(str);
945 bigblocknr = sth.sbd_startblock;
946 curblock = 0;
947 lastbigblocknr = -1;
948 newblocknr = -1;
949 while (bigblocknr>=0) {
950 if (!STORAGE_get_big_block(str,bigblocknr,block))
951 return -1;
952 for (i=0;i<128;i++)
953 if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
954 sbd[i]=STORAGE_CHAINENTRY_ENDOFCHAIN;
955 newblocknr = i+curblock*128;
956 break;
958 if (i!=128)
959 break;
960 lastbigblocknr = bigblocknr;
961 bigblocknr = STORAGE_get_next_big_blocknr(str,bigblocknr);
962 curblock++;
964 if (newblocknr==-1) {
965 bigblocknr = STORAGE_get_free_big_blocknr(str);
966 if (bigblocknr<0)
967 return -1;
968 READ_HEADER(str);
969 memset(block,0xff,sizeof(block));
970 sbd[0]=STORAGE_CHAINENTRY_ENDOFCHAIN;
971 if (!STORAGE_put_big_block(str,bigblocknr,block))
972 return -1;
973 if (lastbigblocknr==-1) {
974 sth.sbd_startblock = bigblocknr;
975 if (!STORAGE_put_big_block(str,-1,(LPBYTE)&sth)) /* need to write it */
976 return -1;
977 } else {
978 if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr))
979 return -1;
981 if (!STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
982 return -1;
983 newblocknr = curblock*128;
985 /* allocate enough big blocks for storing the allocated small block */
986 if (!STORAGE_get_root_pps_entry(str,&root))
987 return -1;
988 if (root.pps_sb==-1)
989 lastbigblocknr = -1;
990 else
991 lastbigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,(root.pps_size-1)/BIGSIZE);
992 while (root.pps_size < (newblocknr*SMALLSIZE+SMALLSIZE-1)) {
993 /* we need to allocate more stuff */
994 bigblocknr = STORAGE_get_free_big_blocknr(str);
995 if (bigblocknr<0)
996 return -1;
997 READ_HEADER(str);
998 if (root.pps_sb==-1) {
999 root.pps_sb = bigblocknr;
1000 root.pps_size += BIGSIZE;
1001 } else {
1002 if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr))
1003 return -1;
1004 root.pps_size += BIGSIZE;
1006 lastbigblocknr = bigblocknr;
1008 if (!STORAGE_set_big_chain(str,lastbigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1009 return -1;
1010 if (!STORAGE_put_pps_entry(str,0,&root))
1011 return -1;
1012 return newblocknr;
1015 /******************************************************************************
1016 * STORAGE_get_free_pps_entry [Internal]
1018 static int
1019 STORAGE_get_free_pps_entry(stream_access16*str) {
1020 int blocknr, i, curblock, lastblocknr=-1;
1021 BYTE block[BIGSIZE];
1022 struct storage_pps_entry *stde = (struct storage_pps_entry*)block;
1023 struct storage_header sth;
1025 READ_HEADER(str);
1026 blocknr = sth.root_startblock;
1027 assert(blocknr>=0);
1028 curblock=0;
1029 while (blocknr>=0) {
1030 if (!STORAGE_get_big_block(str,blocknr,block))
1031 return -1;
1032 for (i=0;i<4;i++)
1033 if (stde[i].pps_sizeofname==0) /* free */
1034 return curblock*4+i;
1035 lastblocknr = blocknr;
1036 blocknr = STORAGE_get_next_big_blocknr(str,blocknr);
1037 curblock++;
1039 assert(blocknr==STORAGE_CHAINENTRY_ENDOFCHAIN);
1040 blocknr = STORAGE_get_free_big_blocknr(str);
1041 /* sth invalidated */
1042 if (blocknr<0)
1043 return -1;
1045 if (!STORAGE_set_big_chain(str,lastblocknr,blocknr))
1046 return -1;
1047 if (!STORAGE_set_big_chain(str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1048 return -1;
1049 memset(block,0,sizeof(block));
1050 STORAGE_put_big_block(str,blocknr,block);
1051 return curblock*4;
1054 /* --- IStream16 implementation */
1056 typedef struct
1058 /* IUnknown fields */
1059 const IStream16Vtbl *lpVtbl;
1060 LONG ref;
1061 /* IStream16 fields */
1062 SEGPTR thisptr; /* pointer to this struct as segmented */
1063 struct storage_pps_entry stde;
1064 int ppsent;
1065 ULARGE_INTEGER offset;
1066 stream_access16 str;
1067 } IStream16Impl;
1069 /******************************************************************************
1070 * IStream16_QueryInterface [STORAGE.518]
1072 HRESULT CDECL IStream16_fnQueryInterface(
1073 IStream16* iface,REFIID refiid,LPVOID *obj
1075 IStream16Impl *This = (IStream16Impl *)iface;
1076 TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1077 if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1078 *obj = This;
1079 return 0;
1081 return OLE_E_ENUM_NOMORE;
1085 /******************************************************************************
1086 * IStream16_AddRef [STORAGE.519]
1088 ULONG CDECL IStream16_fnAddRef(IStream16* iface) {
1089 IStream16Impl *This = (IStream16Impl *)iface;
1090 return InterlockedIncrement(&This->ref);
1093 static void
1094 _ilockbytes16_addref(SEGPTR lockbytes) {
1095 DWORD args[1];
1096 HRESULT hres;
1098 args[0] = (DWORD)lockbytes; /* iface */
1099 if (!WOWCallback16Ex(
1100 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1101 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1102 )->AddRef,
1103 WCB16_PASCAL,
1104 1*sizeof(DWORD),
1105 (LPVOID)args,
1106 (LPDWORD)&hres
1108 ERR("CallTo16 ILockBytes16::AddRef() failed, hres %x\n",hres);
1111 static void
1112 _ilockbytes16_release(SEGPTR lockbytes) {
1113 DWORD args[1];
1114 HRESULT hres;
1116 args[0] = (DWORD)lockbytes; /* iface */
1117 if (!WOWCallback16Ex(
1118 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1119 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1120 )->Release,
1121 WCB16_PASCAL,
1122 1*sizeof(DWORD),
1123 (LPVOID)args,
1124 (LPDWORD)&hres
1126 ERR("CallTo16 ILockBytes16::Release() failed, hres %x\n",hres);
1129 static void
1130 _ilockbytes16_flush(SEGPTR lockbytes) {
1131 DWORD args[1];
1132 HRESULT hres;
1134 args[0] = (DWORD)lockbytes; /* iface */
1135 if (!WOWCallback16Ex(
1136 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1137 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1138 )->Flush,
1139 WCB16_PASCAL,
1140 1*sizeof(DWORD),
1141 (LPVOID)args,
1142 (LPDWORD)&hres
1144 ERR("CallTo16 ILockBytes16::Flush() failed, hres %x\n",hres);
1147 /******************************************************************************
1148 * IStream16_Release [STORAGE.520]
1150 ULONG CDECL IStream16_fnRelease(IStream16* iface) {
1151 IStream16Impl *This = (IStream16Impl *)iface;
1152 ULONG ref;
1154 if (This->str.hf)
1155 FlushFileBuffers(This->str.hf);
1156 else
1157 _ilockbytes16_flush(This->str.lockbytes);
1158 ref = InterlockedDecrement(&This->ref);
1159 if (ref)
1160 return ref;
1162 if (This->str.hf)
1163 CloseHandle(This->str.hf);
1164 else
1165 _ilockbytes16_release(This->str.lockbytes);
1166 UnMapLS( This->thisptr );
1167 HeapFree( GetProcessHeap(), 0, This );
1168 return 0;
1171 /******************************************************************************
1172 * IStream16_Seek [STORAGE.523]
1174 * FIXME
1175 * Does not handle 64 bits
1177 HRESULT CDECL IStream16_fnSeek(
1178 IStream16* iface,LARGE_INTEGER offset,DWORD whence,ULARGE_INTEGER *newpos
1180 IStream16Impl *This = (IStream16Impl *)iface;
1181 TRACE_(relay)("(%p)->([%d.%d],%d,%p)\n",This,offset.u.HighPart,offset.u.LowPart,whence,newpos);
1183 switch (whence) {
1184 /* unix SEEK_xx should be the same as win95 ones */
1185 case SEEK_SET:
1186 /* offset must be ==0 (<0 is invalid, and >0 cannot be handled
1187 * right now.
1189 assert(offset.u.HighPart==0);
1190 This->offset.u.HighPart = offset.u.HighPart;
1191 This->offset.u.LowPart = offset.u.LowPart;
1192 break;
1193 case SEEK_CUR:
1194 if (offset.u.HighPart < 0) {
1195 /* FIXME: is this negation correct ? */
1196 offset.u.HighPart = -offset.u.HighPart;
1197 offset.u.LowPart = (0xffffffff ^ offset.u.LowPart)+1;
1199 assert(offset.u.HighPart==0);
1200 assert(This->offset.u.LowPart >= offset.u.LowPart);
1201 This->offset.u.LowPart -= offset.u.LowPart;
1202 } else {
1203 assert(offset.u.HighPart==0);
1204 This->offset.u.LowPart+= offset.u.LowPart;
1206 break;
1207 case SEEK_END:
1208 assert(offset.u.HighPart==0);
1209 This->offset.u.LowPart = This->stde.pps_size-offset.u.LowPart;
1210 break;
1212 if (This->offset.u.LowPart>This->stde.pps_size)
1213 This->offset.u.LowPart=This->stde.pps_size;
1214 if (newpos) *newpos = This->offset;
1215 return S_OK;
1218 /******************************************************************************
1219 * IStream16_Read [STORAGE.521]
1221 HRESULT CDECL IStream16_fnRead(
1222 IStream16* iface,void *pv,ULONG cb,ULONG *pcbRead
1224 IStream16Impl *This = (IStream16Impl *)iface;
1225 BYTE block[BIGSIZE];
1226 ULONG *bytesread=pcbRead,xxread;
1227 int blocknr;
1228 LPBYTE pbv = pv;
1230 TRACE_(relay)("(%p)->(%p,%d,%p)\n",This,pv,cb,pcbRead);
1231 if (!pcbRead) bytesread=&xxread;
1232 *bytesread = 0;
1234 if (cb>This->stde.pps_size-This->offset.u.LowPart)
1235 cb=This->stde.pps_size-This->offset.u.LowPart;
1236 if (This->stde.pps_size < 0x1000) {
1237 /* use small block reader */
1238 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1239 while (cb) {
1240 unsigned int cc;
1242 if (!STORAGE_get_small_block(&This->str,blocknr,block)) {
1243 WARN("small block read failed!!!\n");
1244 return E_FAIL;
1246 cc = cb;
1247 if (cc>SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1)))
1248 cc=SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1249 memcpy(pbv,block+(This->offset.u.LowPart&(SMALLSIZE-1)),cc);
1250 This->offset.u.LowPart+=cc;
1251 pbv+=cc;
1252 *bytesread+=cc;
1253 cb-=cc;
1254 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1256 } else {
1257 /* use big block reader */
1258 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1259 while (cb) {
1260 unsigned int cc;
1262 if (!STORAGE_get_big_block(&This->str,blocknr,block)) {
1263 WARN("big block read failed!!!\n");
1264 return E_FAIL;
1266 cc = cb;
1267 if (cc>BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1)))
1268 cc=BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1269 memcpy(pbv,block+(This->offset.u.LowPart&(BIGSIZE-1)),cc);
1270 This->offset.u.LowPart+=cc;
1271 pbv+=cc;
1272 *bytesread+=cc;
1273 cb-=cc;
1274 blocknr=STORAGE_get_next_big_blocknr(&This->str,blocknr);
1277 return S_OK;
1280 /******************************************************************************
1281 * IStream16_Write [STORAGE.522]
1283 HRESULT CDECL IStream16_fnWrite(
1284 IStream16* iface,const void *pv,ULONG cb,ULONG *pcbWrite
1286 IStream16Impl *This = (IStream16Impl *)iface;
1287 BYTE block[BIGSIZE];
1288 ULONG *byteswritten=pcbWrite,xxwritten;
1289 int oldsize,newsize,i,curoffset=0,lastblocknr,blocknr,cc;
1290 const BYTE* pbv = pv;
1292 if (!pcbWrite) byteswritten=&xxwritten;
1293 *byteswritten = 0;
1295 TRACE_(relay)("(%p)->(%p,%d,%p)\n",This,pv,cb,pcbWrite);
1296 /* do we need to junk some blocks? */
1297 newsize = This->offset.u.LowPart+cb;
1298 oldsize = This->stde.pps_size;
1299 if (newsize < oldsize) {
1300 if (oldsize < 0x1000) {
1301 /* only small blocks */
1302 blocknr=STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,newsize/SMALLSIZE);
1304 assert(blocknr>=0);
1306 /* will set the rest of the chain to 'free' */
1307 if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1308 return E_FAIL;
1309 } else {
1310 if (newsize >= 0x1000) {
1311 blocknr=STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,newsize/BIGSIZE);
1312 assert(blocknr>=0);
1314 /* will set the rest of the chain to 'free' */
1315 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1316 return E_FAIL;
1317 } else {
1318 /* Migrate large blocks to small blocks
1319 * (we just migrate newsize bytes)
1321 LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,newsize+BIGSIZE);
1322 HRESULT r = E_FAIL;
1324 cc = newsize;
1325 blocknr = This->stde.pps_sb;
1326 curdata = data;
1327 while (cc>0) {
1328 if (!STORAGE_get_big_block(&This->str,blocknr,curdata)) {
1329 HeapFree(GetProcessHeap(),0,data);
1330 return E_FAIL;
1332 curdata += BIGSIZE;
1333 cc -= BIGSIZE;
1334 blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr);
1336 /* frees complete chain for this stream */
1337 if (!STORAGE_set_big_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1338 goto err;
1339 curdata = data;
1340 blocknr = This->stde.pps_sb = STORAGE_get_free_small_blocknr(&This->str);
1341 if (blocknr<0)
1342 goto err;
1343 cc = newsize;
1344 while (cc>0) {
1345 if (!STORAGE_put_small_block(&This->str,blocknr,curdata))
1346 goto err;
1347 cc -= SMALLSIZE;
1348 if (cc<=0) {
1349 if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1350 goto err;
1351 break;
1352 } else {
1353 int newblocknr = STORAGE_get_free_small_blocknr(&This->str);
1354 if (newblocknr<0)
1355 goto err;
1356 if (!STORAGE_set_small_chain(&This->str,blocknr,newblocknr))
1357 goto err;
1358 blocknr = newblocknr;
1360 curdata += SMALLSIZE;
1362 r = S_OK;
1363 err:
1364 HeapFree(GetProcessHeap(),0,data);
1365 if(r != S_OK)
1366 return r;
1369 This->stde.pps_size = newsize;
1372 if (newsize > oldsize) {
1373 if (oldsize >= 0x1000) {
1374 /* should return the block right before the 'endofchain' */
1375 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/BIGSIZE);
1376 assert(blocknr>=0);
1377 lastblocknr = blocknr;
1378 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1379 blocknr = STORAGE_get_free_big_blocknr(&This->str);
1380 if (blocknr<0)
1381 return E_FAIL;
1382 if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr))
1383 return E_FAIL;
1384 lastblocknr = blocknr;
1386 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1387 return E_FAIL;
1388 } else {
1389 if (newsize < 0x1000) {
1390 /* find startblock */
1391 if (!oldsize)
1392 This->stde.pps_sb = blocknr = STORAGE_get_free_small_blocknr(&This->str);
1393 else
1394 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/SMALLSIZE);
1395 if (blocknr<0)
1396 return E_FAIL;
1398 /* allocate required new small blocks */
1399 lastblocknr = blocknr;
1400 for (i=oldsize/SMALLSIZE;i<newsize/SMALLSIZE;i++) {
1401 blocknr = STORAGE_get_free_small_blocknr(&This->str);
1402 if (blocknr<0)
1403 return E_FAIL;
1404 if (!STORAGE_set_small_chain(&This->str,lastblocknr,blocknr))
1405 return E_FAIL;
1406 lastblocknr = blocknr;
1408 /* and terminate the chain */
1409 if (!STORAGE_set_small_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1410 return E_FAIL;
1411 } else {
1412 if (!oldsize) {
1413 /* no single block allocated yet */
1414 blocknr=STORAGE_get_free_big_blocknr(&This->str);
1415 if (blocknr<0)
1416 return E_FAIL;
1417 This->stde.pps_sb = blocknr;
1418 } else {
1419 /* Migrate small blocks to big blocks */
1420 LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,oldsize+BIGSIZE);
1421 HRESULT r = E_FAIL;
1423 cc = oldsize;
1424 blocknr = This->stde.pps_sb;
1425 curdata = data;
1426 /* slurp in */
1427 while (cc>0) {
1428 if (!STORAGE_get_small_block(&This->str,blocknr,curdata))
1429 goto err2;
1430 curdata += SMALLSIZE;
1431 cc -= SMALLSIZE;
1432 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1434 /* free small block chain */
1435 if (!STORAGE_set_small_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1436 goto err2;
1437 curdata = data;
1438 blocknr = This->stde.pps_sb = STORAGE_get_free_big_blocknr(&This->str);
1439 if (blocknr<0)
1440 goto err2;
1441 /* put the data into the big blocks */
1442 cc = This->stde.pps_size;
1443 while (cc>0) {
1444 if (!STORAGE_put_big_block(&This->str,blocknr,curdata))
1445 goto err2;
1446 cc -= BIGSIZE;
1447 if (cc<=0) {
1448 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1449 goto err2;
1450 break;
1451 } else {
1452 int newblocknr = STORAGE_get_free_big_blocknr(&This->str);
1453 if (newblocknr<0)
1454 goto err2;
1455 if (!STORAGE_set_big_chain(&This->str,blocknr,newblocknr))
1456 goto err2;
1457 blocknr = newblocknr;
1459 curdata += BIGSIZE;
1461 r = S_OK;
1462 err2:
1463 HeapFree(GetProcessHeap(),0,data);
1464 if(r != S_OK)
1465 return r;
1467 /* generate big blocks to fit the new data */
1468 lastblocknr = blocknr;
1469 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1470 blocknr = STORAGE_get_free_big_blocknr(&This->str);
1471 if (blocknr<0)
1472 return E_FAIL;
1473 if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr))
1474 return E_FAIL;
1475 lastblocknr = blocknr;
1477 /* terminate chain */
1478 if (!STORAGE_set_big_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1479 return E_FAIL;
1482 This->stde.pps_size = newsize;
1485 /* There are just some cases where we didn't modify it, we write it out
1486 * everytime
1488 if (!STORAGE_put_pps_entry(&This->str,This->ppsent,&(This->stde)))
1489 return E_FAIL;
1491 /* finally the write pass */
1492 if (This->stde.pps_size < 0x1000) {
1493 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1494 assert(blocknr>=0);
1495 while (cb>0) {
1496 /* we ensured that it is allocated above */
1497 assert(blocknr>=0);
1498 /* Read old block everytime, since we can have
1499 * overlapping data at START and END of the write
1501 if (!STORAGE_get_small_block(&This->str,blocknr,block))
1502 return E_FAIL;
1504 cc = SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1505 if (cc>cb)
1506 cc=cb;
1507 memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(SMALLSIZE-1)),
1508 pbv+curoffset,
1511 if (!STORAGE_put_small_block(&This->str,blocknr,block))
1512 return E_FAIL;
1513 cb -= cc;
1514 curoffset += cc;
1515 pbv += cc;
1516 This->offset.u.LowPart += cc;
1517 *byteswritten += cc;
1518 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1520 } else {
1521 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1522 assert(blocknr>=0);
1523 while (cb>0) {
1524 /* we ensured that it is allocated above, so it better is */
1525 assert(blocknr>=0);
1526 /* read old block everytime, since we can have
1527 * overlapping data at START and END of the write
1529 if (!STORAGE_get_big_block(&This->str,blocknr,block))
1530 return E_FAIL;
1532 cc = BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1533 if (cc>cb)
1534 cc=cb;
1535 memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(BIGSIZE-1)),
1536 pbv+curoffset,
1539 if (!STORAGE_put_big_block(&This->str,blocknr,block))
1540 return E_FAIL;
1541 cb -= cc;
1542 curoffset += cc;
1543 pbv += cc;
1544 This->offset.u.LowPart += cc;
1545 *byteswritten += cc;
1546 blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr);
1549 return S_OK;
1552 /******************************************************************************
1553 * _create_istream16 [Internal]
1555 static void _create_istream16(LPSTREAM16 *str) {
1556 IStream16Impl* lpst;
1558 if (!strvt16.QueryInterface) {
1559 HMODULE16 wp = GetModuleHandle16("STORAGE");
1560 if (wp>=32) {
1561 /* FIXME: what is This GetProcAddress16. Should the name be IStream16_QueryInterface of IStream16_fnQueryInterface */
1562 #define VTENT(xfn) strvt16.xfn = (void*)GetProcAddress16(wp,"IStream16_"#xfn);assert(strvt16.xfn)
1563 VTENT(QueryInterface);
1564 VTENT(AddRef);
1565 VTENT(Release);
1566 VTENT(Read);
1567 VTENT(Write);
1568 VTENT(Seek);
1569 VTENT(SetSize);
1570 VTENT(CopyTo);
1571 VTENT(Commit);
1572 VTENT(Revert);
1573 VTENT(LockRegion);
1574 VTENT(UnlockRegion);
1575 VTENT(Stat);
1576 VTENT(Clone);
1577 #undef VTENT
1578 segstrvt16 = (const IStream16Vtbl*)MapLS( &strvt16 );
1579 } else {
1580 #define VTENT(xfn) strvt16.xfn = IStream16_fn##xfn;
1581 VTENT(QueryInterface);
1582 VTENT(AddRef);
1583 VTENT(Release);
1584 VTENT(Read);
1585 VTENT(Write);
1586 VTENT(Seek);
1588 VTENT(CopyTo);
1589 VTENT(Commit);
1590 VTENT(SetSize);
1591 VTENT(Revert);
1592 VTENT(LockRegion);
1593 VTENT(UnlockRegion);
1594 VTENT(Stat);
1595 VTENT(Clone);
1597 #undef VTENT
1598 segstrvt16 = &strvt16;
1601 lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1602 lpst->lpVtbl = segstrvt16;
1603 lpst->ref = 1;
1604 lpst->thisptr = MapLS( lpst );
1605 lpst->str.hf = NULL;
1606 lpst->str.lockbytes = 0;
1607 *str = (void*)lpst->thisptr;
1611 /* --- IStream32 implementation */
1613 typedef struct
1615 /* IUnknown fields */
1616 const IStreamVtbl *lpVtbl;
1617 LONG ref;
1618 /* IStream32 fields */
1619 struct storage_pps_entry stde;
1620 int ppsent;
1621 HANDLE hf;
1622 ULARGE_INTEGER offset;
1623 } IStream32Impl;
1625 /******************************************************************************
1626 * IStorage16_QueryInterface [STORAGE.500]
1628 HRESULT CDECL IStorage16_fnQueryInterface(
1629 IStorage16* iface,REFIID refiid,LPVOID *obj
1631 IStorage16Impl *This = (IStorage16Impl *)iface;
1633 TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1635 if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1636 *obj = This;
1637 return 0;
1639 return OLE_E_ENUM_NOMORE;
1642 /******************************************************************************
1643 * IStorage16_AddRef [STORAGE.501]
1645 ULONG CDECL IStorage16_fnAddRef(IStorage16* iface) {
1646 IStorage16Impl *This = (IStorage16Impl *)iface;
1647 return InterlockedIncrement(&This->ref);
1650 /******************************************************************************
1651 * IStorage16_Release [STORAGE.502]
1653 ULONG CDECL IStorage16_fnRelease(IStorage16* iface) {
1654 IStorage16Impl *This = (IStorage16Impl *)iface;
1655 ULONG ref;
1656 ref = InterlockedDecrement(&This->ref);
1657 if (!ref)
1659 UnMapLS( This->thisptr );
1660 HeapFree( GetProcessHeap(), 0, This );
1662 return ref;
1665 /******************************************************************************
1666 * IStorage16_Stat [STORAGE.517]
1668 HRESULT CDECL IStorage16_fnStat(
1669 LPSTORAGE16 iface,STATSTG16 *pstatstg, DWORD grfStatFlag
1671 IStorage16Impl *This = (IStorage16Impl *)iface;
1672 DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL );
1673 LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len );
1675 TRACE("(%p)->(%p,0x%08x)\n",
1676 This,pstatstg,grfStatFlag
1678 WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL );
1679 pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA );
1680 pstatstg->type = This->stde.pps_type;
1681 pstatstg->cbSize.u.LowPart = This->stde.pps_size;
1682 pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */
1683 pstatstg->atime = This->stde.pps_ft2; /* FIXME */
1684 pstatstg->ctime = This->stde.pps_ft2; /* FIXME */
1685 pstatstg->grfMode = 0; /* FIXME */
1686 pstatstg->grfLocksSupported = 0; /* FIXME */
1687 pstatstg->clsid = This->stde.pps_guid;
1688 pstatstg->grfStateBits = 0; /* FIXME */
1689 pstatstg->reserved = 0;
1690 return S_OK;
1693 /******************************************************************************
1694 * IStorage16_Commit [STORAGE.509]
1696 HRESULT CDECL IStorage16_fnCommit(
1697 LPSTORAGE16 iface,DWORD commitflags
1699 IStorage16Impl *This = (IStorage16Impl *)iface;
1700 FIXME("(%p)->(0x%08x),STUB!\n",
1701 This,commitflags
1703 return S_OK;
1706 /******************************************************************************
1707 * IStorage16_CopyTo [STORAGE.507]
1709 HRESULT CDECL IStorage16_fnCopyTo(LPSTORAGE16 iface,DWORD ciidExclude,const IID *rgiidExclude,SNB16 SNB16Exclude,IStorage16 *pstgDest) {
1710 IStorage16Impl *This = (IStorage16Impl *)iface;
1711 FIXME("IStorage16(%p)->(0x%08x,%s,%p,%p),stub!\n",
1712 This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest
1714 return S_OK;
1718 /******************************************************************************
1719 * IStorage16_CreateStorage [STORAGE.505]
1721 HRESULT CDECL IStorage16_fnCreateStorage(
1722 LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD dwStgFormat,DWORD reserved2, IStorage16 **ppstg
1724 IStorage16Impl *This = (IStorage16Impl *)iface;
1725 IStorage16Impl* lpstg;
1726 int ppsent,x;
1727 struct storage_pps_entry stde;
1728 struct storage_header sth;
1729 BOOL ret;
1730 int nPPSEntries;
1732 READ_HEADER(&This->str);
1733 TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n",
1734 This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg
1736 if (grfMode & STGM_TRANSACTED)
1737 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1738 _create_istorage16(ppstg);
1739 lpstg = MapSL((SEGPTR)*ppstg);
1740 if (This->str.hf) {
1741 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1742 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1743 } else {
1744 lpstg->str.lockbytes = This->str.lockbytes;
1745 _ilockbytes16_addref(This->str.lockbytes);
1748 ppsent=STORAGE_get_free_pps_entry(&lpstg->str);
1749 if (ppsent<0)
1750 return E_FAIL;
1751 stde=This->stde;
1752 if (stde.pps_dir==-1) {
1753 stde.pps_dir = ppsent;
1754 x = This->ppsent;
1755 } else {
1756 FIXME(" use prev chain too ?\n");
1757 x=stde.pps_dir;
1758 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1759 return E_FAIL;
1760 while (stde.pps_next!=-1) {
1761 x=stde.pps_next;
1762 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1763 return E_FAIL;
1765 stde.pps_next = ppsent;
1767 ret = STORAGE_put_pps_entry(&lpstg->str,x,&stde);
1768 assert(ret);
1769 nPPSEntries = STORAGE_get_pps_entry(&lpstg->str,ppsent,&(lpstg->stde));
1770 assert(nPPSEntries == 1);
1771 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname,
1772 sizeof(lpstg->stde.pps_rawname)/sizeof(WCHAR));
1773 lpstg->stde.pps_sizeofname = (strlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR);
1774 lpstg->stde.pps_next = -1;
1775 lpstg->stde.pps_prev = -1;
1776 lpstg->stde.pps_dir = -1;
1777 lpstg->stde.pps_sb = -1;
1778 lpstg->stde.pps_size = 0;
1779 lpstg->stde.pps_type = 1;
1780 lpstg->ppsent = ppsent;
1781 /* FIXME: timestamps? */
1782 if (!STORAGE_put_pps_entry(&lpstg->str,ppsent,&(lpstg->stde)))
1783 return E_FAIL;
1784 return S_OK;
1787 /******************************************************************************
1788 * IStorage16_CreateStream [STORAGE.503]
1790 HRESULT CDECL IStorage16_fnCreateStream(
1791 LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2, IStream16 **ppstm
1793 IStorage16Impl *This = (IStorage16Impl *)iface;
1794 IStream16Impl* lpstr;
1795 int ppsent,x;
1796 struct storage_pps_entry stde;
1797 BOOL ret;
1798 int nPPSEntries;
1800 TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n",
1801 This,pwcsName,grfMode,reserved1,reserved2,ppstm
1803 if (grfMode & STGM_TRANSACTED)
1804 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1805 _create_istream16(ppstm);
1806 lpstr = MapSL((SEGPTR)*ppstm);
1807 if (This->str.hf) {
1808 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1809 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1810 } else {
1811 lpstr->str.lockbytes = This->str.lockbytes;
1812 _ilockbytes16_addref(This->str.lockbytes);
1814 lpstr->offset.u.LowPart = 0;
1815 lpstr->offset.u.HighPart= 0;
1817 ppsent=STORAGE_get_free_pps_entry(&lpstr->str);
1818 if (ppsent<0)
1819 return E_FAIL;
1820 stde=This->stde;
1821 if (stde.pps_next==-1)
1822 x=This->ppsent;
1823 else
1824 while (stde.pps_next!=-1) {
1825 x=stde.pps_next;
1826 if (1!=STORAGE_get_pps_entry(&lpstr->str,x,&stde))
1827 return E_FAIL;
1829 stde.pps_next = ppsent;
1830 ret = STORAGE_put_pps_entry(&lpstr->str,x,&stde);
1831 assert(ret);
1832 nPPSEntries = STORAGE_get_pps_entry(&lpstr->str,ppsent,&(lpstr->stde));
1833 assert(nPPSEntries == 1);
1834 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname,
1835 sizeof(lpstr->stde.pps_rawname)/sizeof(WCHAR));
1836 lpstr->stde.pps_sizeofname = (strlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR);
1837 lpstr->stde.pps_next = -1;
1838 lpstr->stde.pps_prev = -1;
1839 lpstr->stde.pps_dir = -1;
1840 lpstr->stde.pps_sb = -1;
1841 lpstr->stde.pps_size = 0;
1842 lpstr->stde.pps_type = 2;
1843 lpstr->ppsent = ppsent;
1845 /* FIXME: timestamps? */
1846 if (!STORAGE_put_pps_entry(&lpstr->str,ppsent,&(lpstr->stde)))
1847 return E_FAIL;
1848 return S_OK;
1851 /******************************************************************************
1852 * IStorage16_OpenStorage [STORAGE.506]
1854 HRESULT CDECL IStorage16_fnOpenStorage(
1855 LPSTORAGE16 iface,LPCOLESTR16 pwcsName, IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg
1857 IStorage16Impl *This = (IStorage16Impl *)iface;
1858 IStream16Impl* lpstg;
1859 WCHAR name[33];
1860 int newpps;
1862 TRACE("(%p)->(%s,%p,0x%08x,%p,0x%08x,%p)\n",
1863 This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg
1865 if (grfMode & STGM_TRANSACTED)
1866 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1867 _create_istorage16(ppstg);
1868 lpstg = MapSL((SEGPTR)*ppstg);
1869 if (This->str.hf) {
1870 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1871 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1872 } else {
1873 lpstg->str.lockbytes = This->str.lockbytes;
1874 _ilockbytes16_addref(This->str.lockbytes);
1876 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1877 newpps = STORAGE_look_for_named_pps(&lpstg->str,This->stde.pps_dir,name);
1878 if (newpps==-1) {
1879 IStream16_fnRelease((IStream16*)lpstg);
1880 return E_FAIL;
1883 if (1!=STORAGE_get_pps_entry(&lpstg->str,newpps,&(lpstg->stde))) {
1884 IStream16_fnRelease((IStream16*)lpstg);
1885 return E_FAIL;
1887 lpstg->ppsent = newpps;
1888 return S_OK;
1891 /******************************************************************************
1892 * IStorage16_OpenStream [STORAGE.504]
1894 HRESULT CDECL IStorage16_fnOpenStream(
1895 LPSTORAGE16 iface,LPCOLESTR16 pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream16 **ppstm
1897 IStorage16Impl *This = (IStorage16Impl *)iface;
1898 IStream16Impl* lpstr;
1899 WCHAR name[33];
1900 int newpps;
1902 TRACE("(%p)->(%s,%p,0x%08x,0x%08x,%p)\n",
1903 This,pwcsName,reserved1,grfMode,reserved2,ppstm
1905 if (grfMode & STGM_TRANSACTED)
1906 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1907 _create_istream16(ppstm);
1908 lpstr = MapSL((SEGPTR)*ppstm);
1909 if (This->str.hf) {
1910 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1911 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1912 } else {
1913 lpstr->str.lockbytes = This->str.lockbytes;
1914 _ilockbytes16_addref(This->str.lockbytes);
1916 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1917 newpps = STORAGE_look_for_named_pps(&lpstr->str,This->stde.pps_dir,name);
1918 if (newpps==-1) {
1919 IStream16_fnRelease((IStream16*)lpstr);
1920 return E_FAIL;
1923 if (1!=STORAGE_get_pps_entry(&lpstr->str,newpps,&(lpstr->stde))) {
1924 IStream16_fnRelease((IStream16*)lpstr);
1925 return E_FAIL;
1927 lpstr->offset.u.LowPart = 0;
1928 lpstr->offset.u.HighPart = 0;
1929 lpstr->ppsent = newpps;
1930 return S_OK;
1933 /******************************************************************************
1934 * _create_istorage16 [INTERNAL]
1936 static void _create_istorage16(LPSTORAGE16 *stg) {
1937 IStorage16Impl* lpst;
1939 if (!stvt16.QueryInterface) {
1940 HMODULE16 wp = GetModuleHandle16("STORAGE");
1941 if (wp>=32) {
1942 #define VTENT(xfn) stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn);
1943 VTENT(QueryInterface)
1944 VTENT(AddRef)
1945 VTENT(Release)
1946 VTENT(CreateStream)
1947 VTENT(OpenStream)
1948 VTENT(CreateStorage)
1949 VTENT(OpenStorage)
1950 VTENT(CopyTo)
1951 VTENT(MoveElementTo)
1952 VTENT(Commit)
1953 VTENT(Revert)
1954 VTENT(EnumElements)
1955 VTENT(DestroyElement)
1956 VTENT(RenameElement)
1957 VTENT(SetElementTimes)
1958 VTENT(SetClass)
1959 VTENT(SetStateBits)
1960 VTENT(Stat)
1961 #undef VTENT
1962 segstvt16 = (const IStorage16Vtbl*)MapLS( &stvt16 );
1963 } else {
1964 #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn;
1965 VTENT(QueryInterface)
1966 VTENT(AddRef)
1967 VTENT(Release)
1968 VTENT(CreateStream)
1969 VTENT(OpenStream)
1970 VTENT(CreateStorage)
1971 VTENT(OpenStorage)
1972 VTENT(CopyTo)
1973 VTENT(Commit)
1974 /* not (yet) implemented ...
1975 VTENT(MoveElementTo)
1976 VTENT(Revert)
1977 VTENT(EnumElements)
1978 VTENT(DestroyElement)
1979 VTENT(RenameElement)
1980 VTENT(SetElementTimes)
1981 VTENT(SetClass)
1982 VTENT(SetStateBits)
1983 VTENT(Stat)
1985 #undef VTENT
1986 segstvt16 = &stvt16;
1989 lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1990 lpst->lpVtbl = segstvt16;
1991 lpst->str.hf = NULL;
1992 lpst->str.lockbytes = 0;
1993 lpst->ref = 1;
1994 lpst->thisptr = MapLS(lpst);
1995 *stg = (void*)lpst->thisptr;
1998 /******************************************************************************
1999 * Storage API functions
2002 /******************************************************************************
2003 * StgCreateDocFileA [STORAGE.1]
2005 HRESULT WINAPI StgCreateDocFile16(
2006 LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen
2008 HANDLE hf;
2009 int i,ret;
2010 IStorage16Impl* lpstg;
2011 struct storage_pps_entry stde;
2013 TRACE("(%s,0x%08x,0x%08x,%p)\n",
2014 pwcsName,grfMode,reserved,ppstgOpen
2016 _create_istorage16(ppstgOpen);
2017 hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0);
2018 if (hf==INVALID_HANDLE_VALUE) {
2019 WARN("couldn't open file for storage:%d\n",GetLastError());
2020 return E_FAIL;
2022 lpstg = MapSL((SEGPTR)*ppstgOpen);
2023 lpstg->str.hf = hf;
2024 lpstg->str.lockbytes = 0;
2025 /* FIXME: check for existence before overwriting? */
2026 if (!STORAGE_init_storage(&lpstg->str)) {
2027 CloseHandle(hf);
2028 return E_FAIL;
2030 i=0;ret=0;
2031 while (!ret) { /* neither 1 nor <0 */
2032 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2033 if ((ret==1) && (stde.pps_type==5)) {
2034 lpstg->stde = stde;
2035 lpstg->ppsent = i;
2036 break;
2038 i++;
2040 if (ret!=1) {
2041 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
2042 return E_FAIL;
2045 return S_OK;
2048 /******************************************************************************
2049 * StgIsStorageFile [STORAGE.5]
2051 HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
2052 UNICODE_STRING strW;
2053 HRESULT ret;
2055 RtlCreateUnicodeStringFromAsciiz(&strW, fn);
2056 ret = StgIsStorageFile( strW.Buffer );
2057 RtlFreeUnicodeString( &strW );
2059 return ret;
2062 /******************************************************************************
2063 * StgOpenStorage [STORAGE.3]
2065 HRESULT WINAPI StgOpenStorage16(
2066 LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode,
2067 SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen
2069 HANDLE hf;
2070 int ret,i;
2071 IStorage16Impl* lpstg;
2072 struct storage_pps_entry stde;
2074 TRACE("(%s,%p,0x%08x,%p,%d,%p)\n",
2075 pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen
2077 _create_istorage16(ppstgOpen);
2078 hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
2079 if (hf==INVALID_HANDLE_VALUE) {
2080 WARN("Couldn't open file for storage\n");
2081 return E_FAIL;
2083 lpstg = MapSL((SEGPTR)*ppstgOpen);
2084 lpstg->str.hf = hf;
2086 i=0;ret=0;
2087 while (!ret) { /* neither 1 nor <0 */
2088 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2089 if ((ret==1) && (stde.pps_type==5)) {
2090 lpstg->stde=stde;
2091 break;
2093 i++;
2095 if (ret!=1) {
2096 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
2097 return E_FAIL;
2099 return S_OK;
2103 /******************************************************************************
2104 * StgIsStorageILockBytes [STORAGE.6]
2106 * Determines if the ILockBytes contains a storage object.
2108 HRESULT WINAPI StgIsStorageILockBytes16(SEGPTR plkbyt)
2110 DWORD args[6];
2111 HRESULT hres;
2112 HANDLE16 hsig;
2114 args[0] = (DWORD)plkbyt; /* iface */
2115 args[1] = args[2] = 0; /* ULARGE_INTEGER offset */
2116 args[3] = WOWGlobalAllocLock16( 0, 8, &hsig ); /* sig */
2117 args[4] = 8;
2118 args[5] = 0;
2120 if (!WOWCallback16Ex(
2121 (DWORD)((const ILockBytes16Vtbl*)MapSL(
2122 (SEGPTR)((LPLOCKBYTES16)MapSL(plkbyt))->lpVtbl)
2123 )->ReadAt,
2124 WCB16_PASCAL,
2125 6*sizeof(DWORD),
2126 (LPVOID)args,
2127 (LPDWORD)&hres
2128 )) {
2129 ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %x\n",hres);
2130 return hres;
2132 if (memcmp(MapSL(args[3]), STORAGE_magic, sizeof(STORAGE_magic)) == 0) {
2133 WOWGlobalUnlockFree16(args[3]);
2134 return S_OK;
2136 WOWGlobalUnlockFree16(args[3]);
2137 return S_FALSE;
2140 /******************************************************************************
2141 * StgOpenStorageOnILockBytes [STORAGE.4]
2143 * PARAMS
2144 * plkbyt FIXME: Should probably be an ILockBytes16 *.
2146 HRESULT WINAPI StgOpenStorageOnILockBytes16(
2147 SEGPTR plkbyt,
2148 IStorage16 *pstgPriority,
2149 DWORD grfMode,
2150 SNB16 snbExclude,
2151 DWORD reserved,
2152 IStorage16 **ppstgOpen)
2154 IStorage16Impl* lpstg;
2155 int i,ret;
2156 struct storage_pps_entry stde;
2158 FIXME("(%x, %p, 0x%08x, %d, %x, %p)\n", plkbyt, pstgPriority, grfMode, (int)snbExclude, reserved, ppstgOpen);
2159 if ((plkbyt == 0) || (ppstgOpen == 0))
2160 return STG_E_INVALIDPOINTER;
2162 *ppstgOpen = 0;
2164 _create_istorage16(ppstgOpen);
2165 lpstg = MapSL((SEGPTR)*ppstgOpen);
2166 lpstg->str.hf = NULL;
2167 lpstg->str.lockbytes = plkbyt;
2168 i=0;ret=0;
2169 while (!ret) { /* neither 1 nor <0 */
2170 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2171 if ((ret==1) && (stde.pps_type==5)) {
2172 lpstg->stde=stde;
2173 break;
2175 i++;
2177 if (ret!=1) {
2178 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
2179 return E_FAIL;
2181 return S_OK;