/* Compound Storage * * Implemented using the documentation of the LAOLA project at * * (Thanks to Martin Schwartz ) * * Copyright 1998 Marcus Meissner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #define NONAMELESSUNION #define NONAMELESSSTRUCT #include "windef.h" #include "winbase.h" #include "winternl.h" #include "winerror.h" #include "wine/winbase16.h" #include "wownt32.h" #include "wine/unicode.h" #include "objbase.h" #include "wine/debug.h" #include "ifs.h" WINE_DEFAULT_DEBUG_CHANNEL(ole); WINE_DECLARE_DEBUG_CHANNEL(relay); struct storage_header { BYTE magic[8]; /* 00: magic */ BYTE unknown1[36]; /* 08: unknown */ DWORD num_of_bbd_blocks;/* 2C: length of big datablocks */ DWORD root_startblock;/* 30: root storage first big block */ DWORD unknown2[2]; /* 34: unknown */ DWORD sbd_startblock; /* 3C: small block depot first big block */ DWORD unknown3[3]; /* 40: unknown */ DWORD bbd_list[109]; /* 4C: big data block list (up to end of sector)*/ }; struct storage_pps_entry { WCHAR pps_rawname[32];/* 00: \0 terminated widechar name */ WORD pps_sizeofname; /* 40: namelength in bytes */ BYTE pps_type; /* 42: flags, 1 storage/dir, 2 stream, 5 root */ BYTE pps_unknown0; /* 43: unknown */ DWORD pps_prev; /* 44: previous pps */ DWORD pps_next; /* 48: next pps */ DWORD pps_dir; /* 4C: directory pps */ GUID pps_guid; /* 50: class ID */ DWORD pps_unknown1; /* 60: unknown */ FILETIME pps_ft1; /* 64: filetime1 */ FILETIME pps_ft2; /* 70: filetime2 */ DWORD pps_sb; /* 74: data startblock */ DWORD pps_size; /* 78: datalength. (<0x1000)?small:big blocks*/ DWORD pps_unknown2; /* 7C: unknown */ }; #define STORAGE_CHAINENTRY_FAT 0xfffffffd #define STORAGE_CHAINENTRY_ENDOFCHAIN 0xfffffffe #define STORAGE_CHAINENTRY_FREE 0xffffffff static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1}; #define BIGSIZE 512 #define SMALLSIZE 64 #define SMALLBLOCKS_PER_BIGBLOCK (BIGSIZE/SMALLSIZE) #define READ_HEADER(str) STORAGE_get_big_block(str,-1,(LPBYTE)&sth);assert(!memcmp(STORAGE_magic,sth.magic,sizeof(STORAGE_magic))); static IStorage16Vtbl stvt16; static const IStorage16Vtbl *segstvt16 = NULL; static IStream16Vtbl strvt16; static const IStream16Vtbl *segstrvt16 = NULL; /*ULONG WINAPI IStorage16_AddRef(LPSTORAGE16 this);*/ static void _create_istorage16(LPSTORAGE16 *stg); static void _create_istream16(LPSTREAM16 *str); #define IMPLEMENTED 1 /* The following is taken from the CorVu implementation of docfiles, and * documents things about the file format that are not implemented here, and * not documented by the LAOLA project. The CorVu implementation was posted * to wine-devel in February 2004, and released under the LGPL at the same * time. Because that implementation is in C++, it's not directly usable in * Wine, but does have documentation value. * * * #define DF_EXT_VTOC -4 * #define DF_VTOC_VTOC -3 * #define DF_VTOC_EOF -2 * #define DF_VTOC_FREE -1 * #define DF_NAMELEN 0x20 // Maximum entry name length - 31 characters plus * // a NUL terminator * * #define DF_FT_STORAGE 1 * #define DF_FT_STREAM 2 * #define DF_FT_LOCKBYTES 3 // Not used -- How the bloody hell did I manage * #define DF_FT_PROPERTY 4 // Not Used -- to figure these two out? * #define DF_FT_ROOT 5 * * #define DF_BLOCK_SIZE 0x200 * #define DF_VTOC_SIZE 0x80 * #define DF_DE_PER_BLOCK 4 * #define DF_STREAM_BLOCK_SIZE 0x40 * * A DocFile is divided into blocks of 512 bytes. * The first block contains the header. * * The file header contains The first 109 entries in the VTOC of VTOCs. * * Each block pointed to by a VTOC of VTOCs contains a VTOC, which * includes block chains - just like FAT. This is a somewhat poor * design for the following reasons: * * 1. FAT was a poor file system design to begin with, and * has long been known to be horrendously inefficient * for day to day operations. * * 2. The problem is compounded here, since the file * level streams are generally *not* read sequentially. * This means that a significant percentage of reads * require seeking from the start of the chain. * * Data chains also contain an internal VTOC. The block size for * the standard VTOC is 512. The block size for the internal VTOC * is 64. * * Now, the 109 blocks in the VTOC of VTOCs allows for files of * up to around 7MB. So what do you think happens if that's * exceeded? Well, there's an entry in the header block which * points to the first block used as additional storage for * the VTOC of VTOCs. * * Now we can get up to around 15MB. Now, guess how the file * format adds in another block to the VTOC of VTOCs. Come on, * it's no big surprise. That's right - the last entry in each * block extending the VTOC of VTOCs is, you guessed it, the * block number of the next block containing an extension to * the VTOC of VTOCs. The VTOC of VTOCs is chained!!!! * * So, to review: * * 1. If you are using a FAT file system, the location of * your file's blocks is stored in chains. * * 2. At the abstract level, the file contains a VTOC of VTOCs, * which is stored in the most inefficient possible format for * random access - a chain (AKA list). * * 3. The VTOC of VTOCs contains descriptions of three file level * streams: * * a. The Directory stream * b. The Data stream * c. The Data VTOC stream * * These are, of course, represented as chains. * * 4. The Data VTOC contains data describing the chains of blocks * within the Data stream. * * That's right - we have a total of four levels of block chains! * * Now, is that complicated enough for you? No? OK, there's another * complication. If an individual stream (ie. an IStream) reaches * 4096 bytes in size, it gets moved from the Data Stream to * a new file level stream. Now, if the stream then gets truncated * back to less than 4096 bytes, it returns to the data stream. * * The effect of using this format can be seen very easily. Pick * an arbitrary application with a grid data representation that * can export to both Lotus 123 and Excel 5 or higher. Export * a large file to Lotus 123 and time it. Export the same thing * to Excel 5 and time that. The difference is the inefficiency * of the Microsoft DocFile format. * * * #define TOTAL_SIMPLE_VTOCS 109 * * struct DocFile_Header * { * df_byte iMagic1; // 0xd0 * df_byte iMagic2; // 0xcf * df_byte iMagic3; // 0x11 * df_byte iMagic4; // 0xe0 - Spells D0CF11E0, or DocFile * df_byte iMagic5; // 161 (igi upside down) * df_byte iMagic6; // 177 (lli upside down - see below * df_byte iMagic7; // 26 (gz upside down) * df_byte iMagic8; // 225 (szz upside down) - see below * df_int4 aiUnknown1[4]; * df_int4 iVersion; // DocFile Version - 0x03003E * df_int4 aiUnknown2[4]; * df_int4 nVTOCs; // Number of VTOCs * df_int4 iFirstDirBlock; // First Directory Block * df_int4 aiUnknown3[2]; * df_int4 iFirstDataVTOC; // First data VTOC block * df_int4 iHasData; // 1 if there is data in the file - yes, this is important * df_int4 iExtendedVTOC; // Extended VTOC location * df_int4 iExtendedVTOCSize; // Size of extended VTOC (+1?) * df_int4 aiVTOCofVTOCs[TOTAL_SIMPLE_VTOCS]; * }; * * struct DocFile_VTOC * { * df_int4 aiBlocks[DF_VTOC_SIZE]; * }; * * * The meaning of the magic numbers * * 0xd0cf11e0 is DocFile with a zero on the end (sort of) * * If you key 177161 into a calculator, then turn the calculator * upside down, you get igilli, which may be a reference to * somebody's name, or to the Hebrew word for "angel". * * If you key 26225 into a calculator, then turn it upside down, you * get szzgz. Microsoft has a tradition of creating nonsense words * using the letters s, g, z and y. We think szzgz may be one of the * Microsoft placeholder variables, along the lines of foo, bar and baz. * Alternatively, it could be 22526, which would be gzszz. * * * struct DocFile_DirEnt * { * df_char achEntryName[DF_NAMELEN]; // Entry Name * df_int2 iNameLen; // Name length in bytes, including NUL terminator * df_byte iFileType; // Entry type * df_byte iColour; // 1 = Black, 0 = Red * df_int4 iLeftSibling; // Next Left Sibling Entry - See below * df_int4 iRightSibling; // Next Right Sibling Entry * df_int4 iFirstChild; // First Child Entry * df_byte achClassID[16]; // Class ID * df_int4 iStateBits; // [GS]etStateBits value * df_int4 iCreatedLow; // Low DWORD of creation time * df_int4 iCreatedHigh; // High DWORD of creation time * df_int4 iModifiedLow; // Low DWORD of modification time * df_int4 iModifiedHigh; // High DWORD of modification time * df_int4 iVTOCPosition; // VTOC Position * df_int4 iFileSize; // Size of the stream * df_int4 iZero; // We think this is part of the 64 bit stream size - must be 0 * }; * * Siblings * ======== * * Siblings are stored in an obscure but incredibly elegant * data structure called a red-black tree. This is generally * defined as a 2-3-4 tree stored in a binary tree. * * A red-black tree can always be balanced very easily. The rules * for a red-black tree are as follows: * * 1. The root node is always black. * 2. The parent of a red node is always black. * * There is a Java demo of red-black trees at: * * http://langevin.usc.edu/BST/RedBlackTree-Example.html * * This demo is an excellent tool for learning how red-black * trees work, without having to go through the process of * learning how they were derived. * * Within the tree, elements are ordered by the length of the * name and within that, ASCII order by name. This causes the * apparently bizarre reordering you see when you use dfview. * * This is a somewhat bizarre choice. It suggests that the * designer of the DocFile format was trying to optimise * searching through the directory entries. However searching * through directory entries is a relatively rare operation. * Reading and seeking within a stream are much more common * operations, especially within the file level streams, yet * these use the horrendously inefficient FAT chains. * * This suggests that the designer was probably somebody * fresh out of university, who had some basic knowledge of * basic data structures, but little knowledge of anything * more practical. It is bizarre to attempt to optimise * directory searches while not using a more efficient file * block locating system than FAT (seedling/sapling/tree * would result in a massive improvement - in fact we have * an alternative to docfiles that we use internally that * uses seedling/sapling/tree and *is* far more efficient). * * It is worth noting that the MS implementation of red-black * trees is incorrect (I can tell you're surprised) and * actually causes more operations to occur than are really * needed. Fortunately the fact that our implementation is * correct will not cause any problems - the MS implementation * still appears to cause the tree to satisfy the rules, albeit * a sequence of the same insertions in the different * implementations may result in a different, and possibly * deeper (but never shallower) tree. */ typedef struct { HANDLE hf; SEGPTR lockbytes; } stream_access16; /* --- IStorage16 implementation struct */ typedef struct { IStorage16 IStorage16_iface; LONG ref; /* IStorage16 fields */ SEGPTR thisptr; /* pointer to this struct as segmented */ struct storage_pps_entry stde; int ppsent; stream_access16 str; } IStorage16Impl; /****************************************************************************** * STORAGE_get_big_block [Internal] * * Reading OLE compound storage */ static BOOL STORAGE_get_big_block(stream_access16 *str,int n,BYTE *block) { DWORD result; assert(n>=-1); if (str->hf) { if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL, SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError()) { WARN("(%p,%d,%p), seek failed (%d)\n",str->hf, n, block, GetLastError()); return FALSE; } if (!ReadFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) { WARN("(hf=%p, block size %d): read didn't read (%d)\n",str->hf,n,GetLastError()); return FALSE; } } else { DWORD args[6]; HRESULT hres; HANDLE16 hsig; args[0] = (DWORD)str->lockbytes; /* iface */ args[1] = (n+1)*BIGSIZE; args[2] = 0; /* ULARGE_INTEGER offset */ args[3] = WOWGlobalAllocLock16( 0, BIGSIZE, &hsig ); /* sig */ args[4] = BIGSIZE; args[5] = 0; if (!WOWCallback16Ex( (DWORD)((const ILockBytes16Vtbl*)MapSL( (SEGPTR)((LPLOCKBYTES16)MapSL(str->lockbytes))->lpVtbl) )->ReadAt, WCB16_PASCAL, 6*sizeof(DWORD), (LPVOID)args, (LPDWORD)&hres )) { ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %x\n",hres); return FALSE; } memcpy(block, MapSL(args[3]), BIGSIZE); WOWGlobalUnlockFree16(args[3]); } return TRUE; } static BOOL _ilockbytes16_writeat(SEGPTR lockbytes, DWORD offset, DWORD length, void *buffer) { DWORD args[6]; HRESULT hres; args[0] = (DWORD)lockbytes; /* iface */ args[1] = offset; args[2] = 0; /* ULARGE_INTEGER offset */ args[3] = (DWORD)MapLS( buffer ); args[4] = length; args[5] = 0; /* THIS_ ULARGE_INTEGER ulOffset, const void *pv, ULONG cb, ULONG *pcbWritten); */ if (!WOWCallback16Ex( (DWORD)((const ILockBytes16Vtbl*)MapSL( (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl) )->WriteAt, WCB16_PASCAL, 6*sizeof(DWORD), (LPVOID)args, (LPDWORD)&hres )) { ERR("CallTo16 ILockBytes16::WriteAt() failed, hres %x\n",hres); return FALSE; } UnMapLS(args[3]); return TRUE; } /****************************************************************************** * STORAGE_put_big_block [INTERNAL] */ static BOOL STORAGE_put_big_block(stream_access16 *str,int n,BYTE *block) { DWORD result; assert(n>=-1); if (str->hf) { if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL, SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError()) { WARN("seek failed (%d)\n",GetLastError()); return FALSE; } if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) { WARN(" write failed (%d)\n",GetLastError()); return FALSE; } return TRUE; } else { _ilockbytes16_writeat(str->lockbytes, (n+1)*BIGSIZE, BIGSIZE, block); return TRUE; } } /****************************************************************************** * STORAGE_get_next_big_blocknr [INTERNAL] */ static int STORAGE_get_next_big_blocknr(stream_access16 *str,int blocknr) { INT bbs[BIGSIZE/sizeof(INT)]; struct storage_header sth; READ_HEADER(str); assert(blocknr>>7>7]==0xffffffff) return -5; if (!STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs)) return -5; assert(bbs[blocknr&0x7f]!=STORAGE_CHAINENTRY_FREE); return bbs[blocknr&0x7f]; } /****************************************************************************** * STORAGE_get_nth_next_big_blocknr [INTERNAL] */ static int STORAGE_get_nth_next_big_blocknr(stream_access16 *str,int blocknr,int nr) { INT bbs[BIGSIZE/sizeof(INT)]; int lastblock = -1; struct storage_header sth; TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr); READ_HEADER(str); assert(blocknr>=0); while (nr--) { assert((blocknr>>7)>7]!=0xffffffff); /* simple caching... */ if (lastblock!=sth.bbd_list[blocknr>>7]) { BOOL ret = STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs); assert(ret); lastblock = sth.bbd_list[blocknr>>7]; } blocknr = bbs[blocknr&0x7f]; } return blocknr; } /****************************************************************************** * STORAGE_get_root_pps_entry [Internal] */ static BOOL STORAGE_get_root_pps_entry(stream_access16* str,struct storage_pps_entry *pstde) { int blocknr,i; BYTE block[BIGSIZE]; struct storage_pps_entry *stde=(struct storage_pps_entry*)block; struct storage_header sth; READ_HEADER(str); blocknr = sth.root_startblock; TRACE("startblock is %d\n", blocknr); while (blocknr>=0) { BOOL ret = STORAGE_get_big_block(str,blocknr,block); assert(ret); for (i=0;i<4;i++) { if (!stde[i].pps_sizeofname) continue; if (stde[i].pps_type==5) { *pstde=stde[i]; return TRUE; } } blocknr=STORAGE_get_next_big_blocknr(str,blocknr); TRACE("next block is %d\n", blocknr); } return FALSE; } /****************************************************************************** * STORAGE_get_small_block [INTERNAL] */ static BOOL STORAGE_get_small_block(stream_access16 *str,int blocknr,BYTE *sblock) { BYTE block[BIGSIZE]; int bigblocknr; struct storage_pps_entry root; BOOL ret; TRACE("(blocknr=%d)\n", blocknr); assert(blocknr>=0); ret = STORAGE_get_root_pps_entry(str,&root); assert(ret); bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK); assert(bigblocknr>=0); ret = STORAGE_get_big_block(str,bigblocknr,block); assert(ret); memcpy(sblock,((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),SMALLSIZE); return TRUE; } /****************************************************************************** * STORAGE_put_small_block [INTERNAL] */ static BOOL STORAGE_put_small_block(stream_access16 *str,int blocknr,const BYTE *sblock) { BYTE block[BIGSIZE]; int bigblocknr; struct storage_pps_entry root; BOOL ret; assert(blocknr>=0); TRACE("(blocknr=%d)\n", blocknr); ret = STORAGE_get_root_pps_entry(str,&root); assert(ret); bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK); assert(bigblocknr>=0); ret = STORAGE_get_big_block(str,bigblocknr,block); assert(ret); memcpy(((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),sblock,SMALLSIZE); ret = STORAGE_put_big_block(str,bigblocknr,block); assert(ret); return TRUE; } /****************************************************************************** * STORAGE_get_next_small_blocknr [INTERNAL] */ static int STORAGE_get_next_small_blocknr(stream_access16 *str,int blocknr) { BYTE block[BIGSIZE]; LPINT sbd = (LPINT)block; int bigblocknr; struct storage_header sth; BOOL ret; TRACE("(blocknr=%d)\n", blocknr); READ_HEADER(str); assert(blocknr>=0); bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128); assert(bigblocknr>=0); ret = STORAGE_get_big_block(str,bigblocknr,block); assert(ret); assert(sbd[blocknr & 127]!=STORAGE_CHAINENTRY_FREE); return sbd[blocknr & (128-1)]; } /****************************************************************************** * STORAGE_get_nth_next_small_blocknr [INTERNAL] */ static int STORAGE_get_nth_next_small_blocknr(stream_access16*str,int blocknr,int nr) { int lastblocknr=-1; BYTE block[BIGSIZE]; LPINT sbd = (LPINT)block; struct storage_header sth; BOOL ret; TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr); READ_HEADER(str); assert(blocknr>=0); while ((nr--) && (blocknr>=0)) { if (lastblocknr/128!=blocknr/128) { int bigblocknr; bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128); assert(bigblocknr>=0); ret = STORAGE_get_big_block(str,bigblocknr,block); assert(ret); lastblocknr = blocknr; } assert(lastblocknr>=0); lastblocknr=blocknr; blocknr=sbd[blocknr & (128-1)]; assert(blocknr!=STORAGE_CHAINENTRY_FREE); } return blocknr; } /****************************************************************************** * STORAGE_get_pps_entry [INTERNAL] */ static int STORAGE_get_pps_entry(stream_access16*str,int n,struct storage_pps_entry *pstde) { int blocknr; BYTE block[BIGSIZE]; struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3)); struct storage_header sth; BOOL ret; TRACE("(n=%d)\n", n); READ_HEADER(str); /* we have 4 pps entries per big block */ blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4); assert(blocknr>=0); ret = STORAGE_get_big_block(str,blocknr,block); assert(ret); *pstde=*stde; return 1; } /****************************************************************************** * STORAGE_put_pps_entry [Internal] */ static int STORAGE_put_pps_entry(stream_access16*str,int n,const struct storage_pps_entry *pstde) { int blocknr; BYTE block[BIGSIZE]; struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3)); struct storage_header sth; BOOL ret; TRACE("(n=%d)\n", n); READ_HEADER(str); /* we have 4 pps entries per big block */ blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4); assert(blocknr>=0); ret = STORAGE_get_big_block(str,blocknr,block); assert(ret); *stde=*pstde; ret = STORAGE_put_big_block(str,blocknr,block); assert(ret); return 1; } /****************************************************************************** * STORAGE_look_for_named_pps [Internal] */ static int STORAGE_look_for_named_pps(stream_access16*str,int n,LPOLESTR name) { struct storage_pps_entry stde; int ret; TRACE("(n=%d,name=%s)\n", n, debugstr_w(name)); if (n==-1) return -1; if (1!=STORAGE_get_pps_entry(str,n,&stde)) return -1; if (!lstrcmpW(name,stde.pps_rawname)) return n; if (stde.pps_prev != -1) { ret=STORAGE_look_for_named_pps(str,stde.pps_prev,name); if (ret!=-1) return ret; } if (stde.pps_next != -1) { ret=STORAGE_look_for_named_pps(str,stde.pps_next,name); if (ret!=-1) return ret; } return -1; } /****************************************************************************** * STORAGE_dump_pps_entry [Internal] * * This function is there to simplify debugging. It is otherwise unused. */ void STORAGE_dump_pps_entry(struct storage_pps_entry *stde) { char name[33]; WideCharToMultiByte( CP_ACP, 0, stde->pps_rawname, -1, name, sizeof(name), NULL, NULL); if (!stde->pps_sizeofname) return; TRACE("name: %s\n",name); TRACE("type: %d\n",stde->pps_type); TRACE("prev pps: %d\n",stde->pps_prev); TRACE("next pps: %d\n",stde->pps_next); TRACE("dir pps: %d\n",stde->pps_dir); TRACE("guid: %s\n",debugstr_guid(&(stde->pps_guid))); if (stde->pps_type !=2) { time_t t; DWORD dw; RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft1),&dw); t = dw; TRACE("ts1: %s\n",ctime(&t)); RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft2),&dw); t = dw; TRACE("ts2: %s\n",ctime(&t)); } TRACE("startblock: %d\n",stde->pps_sb); TRACE("size: %d\n",stde->pps_size); } /****************************************************************************** * STORAGE_init_storage [INTERNAL] */ static BOOL STORAGE_init_storage(stream_access16 *str) { BYTE block[BIGSIZE]; LPDWORD bbs; struct storage_header *sth; struct storage_pps_entry *stde; DWORD result; if (str->hf) SetFilePointer( str->hf, 0, NULL, SEEK_SET ); /* block -1 is the storage header */ sth = (struct storage_header*)block; memcpy(sth->magic,STORAGE_magic,8); memset(sth->unknown1,0,sizeof(sth->unknown1)); memset(sth->unknown2,0,sizeof(sth->unknown2)); memset(sth->unknown3,0,sizeof(sth->unknown3)); sth->num_of_bbd_blocks = 1; sth->root_startblock = 1; sth->sbd_startblock = 0xffffffff; memset(sth->bbd_list,0xff,sizeof(sth->bbd_list)); sth->bbd_list[0] = 0; if (str->hf) { if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE; } else { if (!_ilockbytes16_writeat(str->lockbytes, 0, BIGSIZE, block)) return FALSE; } /* block 0 is the big block directory */ bbs=(LPDWORD)block; memset(block,0xff,sizeof(block)); /* mark all blocks as free */ bbs[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for this block */ bbs[1]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for directory entry */ if (str->hf) { if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE; } else { if (!_ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block)) return FALSE; } /* block 1 is the root directory entry */ memset(block,0x00,sizeof(block)); stde = (struct storage_pps_entry*)block; MultiByteToWideChar( CP_ACP, 0, "RootEntry", -1, stde->pps_rawname, sizeof(stde->pps_rawname)/sizeof(WCHAR)); stde->pps_sizeofname = (strlenW(stde->pps_rawname)+1) * sizeof(WCHAR); stde->pps_type = 5; stde->pps_dir = -1; stde->pps_next = -1; stde->pps_prev = -1; stde->pps_sb = 0xffffffff; stde->pps_size = 0; if (str->hf) { return (WriteFile( str->hf, block, BIGSIZE, &result, NULL ) && result == BIGSIZE); } else { return _ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block); } } /****************************************************************************** * STORAGE_set_big_chain [Internal] */ static BOOL STORAGE_set_big_chain(stream_access16*str,int blocknr,INT type) { BYTE block[BIGSIZE]; LPINT bbd = (LPINT)block; int nextblocknr,bigblocknr; struct storage_header sth; BOOL ret; READ_HEADER(str); assert(blocknr!=type); while (blocknr>=0) { bigblocknr = sth.bbd_list[blocknr/128]; assert(bigblocknr>=0); ret = STORAGE_get_big_block(str,bigblocknr,block); assert(ret); nextblocknr = bbd[blocknr&(128-1)]; bbd[blocknr&(128-1)] = type; if (type>=0) return TRUE; ret = STORAGE_put_big_block(str,bigblocknr,block); assert(ret); type = STORAGE_CHAINENTRY_FREE; blocknr = nextblocknr; } return TRUE; } /****************************************************************************** * STORAGE_set_small_chain [Internal] */ static BOOL STORAGE_set_small_chain(stream_access16*str,int blocknr,INT type) { BYTE block[BIGSIZE]; LPINT sbd = (LPINT)block; int lastblocknr,nextsmallblocknr,bigblocknr; struct storage_header sth; BOOL ret; READ_HEADER(str); assert(blocknr!=type); lastblocknr=-129;bigblocknr=-2; while (blocknr>=0) { /* cache block ... */ if (lastblocknr/128!=blocknr/128) { bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128); assert(bigblocknr>=0); ret = STORAGE_get_big_block(str,bigblocknr,block); assert(ret); } lastblocknr = blocknr; nextsmallblocknr = sbd[blocknr&(128-1)]; sbd[blocknr&(128-1)] = type; ret = STORAGE_put_big_block(str,bigblocknr,block); assert(ret); if (type>=0) return TRUE; type = STORAGE_CHAINENTRY_FREE; blocknr = nextsmallblocknr; } return TRUE; } /****************************************************************************** * STORAGE_get_free_big_blocknr [Internal] */ static int STORAGE_get_free_big_blocknr(stream_access16 *str) { BYTE block[BIGSIZE]; LPINT sbd = (LPINT)block; int lastbigblocknr,i,bigblocknr; unsigned int curblock; struct storage_header sth; BOOL ret; READ_HEADER(str); curblock = 0; lastbigblocknr = -1; bigblocknr = sth.bbd_list[curblock]; while (curblock=0); ret = STORAGE_get_big_block(str,bigblocknr,block); assert(ret); for (i=0;i<128;i++) if (sbd[i]==STORAGE_CHAINENTRY_FREE) { sbd[i] = STORAGE_CHAINENTRY_ENDOFCHAIN; ret = STORAGE_put_big_block(str,bigblocknr,block); assert(ret); memset(block,0x42,sizeof(block)); ret = STORAGE_put_big_block(str,i+curblock*128,block); assert(ret); return i+curblock*128; } lastbigblocknr = bigblocknr; bigblocknr = sth.bbd_list[++curblock]; } bigblocknr = curblock*128; /* since we have marked all blocks from 0 up to curblock*128-1 * the next free one is curblock*128, where we happily put our * next large block depot. */ memset(block,0xff,sizeof(block)); /* mark the block allocated and returned by this function */ sbd[1] = STORAGE_CHAINENTRY_ENDOFCHAIN; ret = STORAGE_put_big_block(str,bigblocknr,block); assert(ret); /* if we had a bbd block already (most likely) we need * to link the new one into the chain */ if (lastbigblocknr!=-1) { ret = STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr); assert(ret); } sth.bbd_list[curblock]=bigblocknr; sth.num_of_bbd_blocks++; assert(sth.num_of_bbd_blocks==curblock+1); ret = STORAGE_put_big_block(str,-1,(LPBYTE)&sth); assert(ret); /* Set the end of the chain for the bigblockdepots */ ret = STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN); assert(ret); /* add 1, for the first entry is used for the additional big block * depot. (means we already used bigblocknr) */ memset(block,0x42,sizeof(block)); /* allocate this block (filled with 0x42) */ ret = STORAGE_put_big_block(str,bigblocknr+1,block); assert(ret); return bigblocknr+1; } /****************************************************************************** * STORAGE_get_free_small_blocknr [Internal] */ static int STORAGE_get_free_small_blocknr(stream_access16 *str) { BYTE block[BIGSIZE]; LPINT sbd = (LPINT)block; int lastbigblocknr,newblocknr,i,curblock,bigblocknr; struct storage_pps_entry root; struct storage_header sth; READ_HEADER(str); bigblocknr = sth.sbd_startblock; curblock = 0; lastbigblocknr = -1; newblocknr = -1; while (bigblocknr>=0) { if (!STORAGE_get_big_block(str,bigblocknr,block)) return -1; for (i=0;i<128;i++) if (sbd[i]==STORAGE_CHAINENTRY_FREE) { sbd[i]=STORAGE_CHAINENTRY_ENDOFCHAIN; newblocknr = i+curblock*128; break; } if (i!=128) break; lastbigblocknr = bigblocknr; bigblocknr = STORAGE_get_next_big_blocknr(str,bigblocknr); curblock++; } if (newblocknr==-1) { bigblocknr = STORAGE_get_free_big_blocknr(str); if (bigblocknr<0) return -1; READ_HEADER(str); memset(block,0xff,sizeof(block)); sbd[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; if (!STORAGE_put_big_block(str,bigblocknr,block)) return -1; if (lastbigblocknr==-1) { sth.sbd_startblock = bigblocknr; if (!STORAGE_put_big_block(str,-1,(LPBYTE)&sth)) /* need to write it */ return -1; } else { if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr)) return -1; } if (!STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) return -1; newblocknr = curblock*128; } /* allocate enough big blocks for storing the allocated small block */ if (!STORAGE_get_root_pps_entry(str,&root)) return -1; if (root.pps_sb==-1) lastbigblocknr = -1; else lastbigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,(root.pps_size-1)/BIGSIZE); while (root.pps_size < (newblocknr*SMALLSIZE+SMALLSIZE-1)) { /* we need to allocate more stuff */ bigblocknr = STORAGE_get_free_big_blocknr(str); if (bigblocknr<0) return -1; READ_HEADER(str); if (root.pps_sb==-1) { root.pps_sb = bigblocknr; root.pps_size += BIGSIZE; } else { if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr)) return -1; root.pps_size += BIGSIZE; } lastbigblocknr = bigblocknr; } if (!STORAGE_set_big_chain(str,lastbigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) return -1; if (!STORAGE_put_pps_entry(str,0,&root)) return -1; return newblocknr; } /****************************************************************************** * STORAGE_get_free_pps_entry [Internal] */ static int STORAGE_get_free_pps_entry(stream_access16*str) { int blocknr, i, curblock, lastblocknr=-1; BYTE block[BIGSIZE]; struct storage_pps_entry *stde = (struct storage_pps_entry*)block; struct storage_header sth; READ_HEADER(str); blocknr = sth.root_startblock; assert(blocknr>=0); curblock=0; while (blocknr>=0) { if (!STORAGE_get_big_block(str,blocknr,block)) return -1; for (i=0;i<4;i++) if (stde[i].pps_sizeofname==0) /* free */ return curblock*4+i; lastblocknr = blocknr; blocknr = STORAGE_get_next_big_blocknr(str,blocknr); curblock++; } assert(blocknr==STORAGE_CHAINENTRY_ENDOFCHAIN); blocknr = STORAGE_get_free_big_blocknr(str); /* sth invalidated */ if (blocknr<0) return -1; if (!STORAGE_set_big_chain(str,lastblocknr,blocknr)) return -1; if (!STORAGE_set_big_chain(str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) return -1; memset(block,0,sizeof(block)); STORAGE_put_big_block(str,blocknr,block); return curblock*4; } /* --- IStream16 implementation */ typedef struct { IStream16 IStream16_iface; LONG ref; /* IStream16 fields */ SEGPTR thisptr; /* pointer to this struct as segmented */ struct storage_pps_entry stde; int ppsent; ULARGE_INTEGER offset; stream_access16 str; } IStream16Impl; static inline IStream16Impl *impl_from_IStream16(IStream16 *iface) { return CONTAINING_RECORD(iface, IStream16Impl, IStream16_iface); } /****************************************************************************** * IStream16_QueryInterface [STORAGE.518] */ HRESULT CDECL IStream16_fnQueryInterface(IStream16 *iface, REFIID refiid, void **obj) { IStream16Impl *This = impl_from_IStream16(iface); TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj); if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) { *obj = This; return 0; } return OLE_E_ENUM_NOMORE; } /****************************************************************************** * IStream16_AddRef [STORAGE.519] */ ULONG CDECL IStream16_fnAddRef(IStream16 *iface) { IStream16Impl *This = impl_from_IStream16(iface); return InterlockedIncrement(&This->ref); } static void _ilockbytes16_addref(SEGPTR lockbytes) { DWORD args[1]; HRESULT hres; args[0] = (DWORD)lockbytes; /* iface */ if (!WOWCallback16Ex( (DWORD)((const ILockBytes16Vtbl*)MapSL( (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl) )->AddRef, WCB16_PASCAL, 1*sizeof(DWORD), (LPVOID)args, (LPDWORD)&hres )) ERR("CallTo16 ILockBytes16::AddRef() failed, hres %x\n",hres); } static void _ilockbytes16_release(SEGPTR lockbytes) { DWORD args[1]; HRESULT hres; args[0] = (DWORD)lockbytes; /* iface */ if (!WOWCallback16Ex( (DWORD)((const ILockBytes16Vtbl*)MapSL( (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl) )->Release, WCB16_PASCAL, 1*sizeof(DWORD), (LPVOID)args, (LPDWORD)&hres )) ERR("CallTo16 ILockBytes16::Release() failed, hres %x\n",hres); } static void _ilockbytes16_flush(SEGPTR lockbytes) { DWORD args[1]; HRESULT hres; args[0] = (DWORD)lockbytes; /* iface */ if (!WOWCallback16Ex( (DWORD)((const ILockBytes16Vtbl*)MapSL( (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl) )->Flush, WCB16_PASCAL, 1*sizeof(DWORD), (LPVOID)args, (LPDWORD)&hres )) ERR("CallTo16 ILockBytes16::Flush() failed, hres %x\n",hres); } /****************************************************************************** * IStream16_Release [STORAGE.520] */ ULONG CDECL IStream16_fnRelease(IStream16 *iface) { IStream16Impl *This = impl_from_IStream16(iface); ULONG ref; if (This->str.hf) FlushFileBuffers(This->str.hf); else _ilockbytes16_flush(This->str.lockbytes); ref = InterlockedDecrement(&This->ref); if (ref) return ref; if (This->str.hf) CloseHandle(This->str.hf); else _ilockbytes16_release(This->str.lockbytes); UnMapLS( This->thisptr ); HeapFree( GetProcessHeap(), 0, This ); return 0; } /****************************************************************************** * IStream16_Seek [STORAGE.523] * * FIXME * Does not handle 64 bits */ HRESULT CDECL IStream16_fnSeek(IStream16 *iface, LARGE_INTEGER offset, DWORD whence, ULARGE_INTEGER *newpos) { IStream16Impl *This = impl_from_IStream16(iface); TRACE_(relay)("(%p)->([%d.%d],%d,%p)\n",This,offset.u.HighPart,offset.u.LowPart,whence,newpos); switch (whence) { /* unix SEEK_xx should be the same as win95 ones */ case SEEK_SET: /* offset must be ==0 (<0 is invalid, and >0 cannot be handled * right now. */ assert(offset.u.HighPart==0); This->offset.u.HighPart = offset.u.HighPart; This->offset.u.LowPart = offset.u.LowPart; break; case SEEK_CUR: if (offset.u.HighPart < 0) { /* FIXME: is this negation correct ? */ offset.u.HighPart = -offset.u.HighPart; offset.u.LowPart = (0xffffffff ^ offset.u.LowPart)+1; assert(offset.u.HighPart==0); assert(This->offset.u.LowPart >= offset.u.LowPart); This->offset.u.LowPart -= offset.u.LowPart; } else { assert(offset.u.HighPart==0); This->offset.u.LowPart+= offset.u.LowPart; } break; case SEEK_END: assert(offset.u.HighPart==0); This->offset.u.LowPart = This->stde.pps_size-offset.u.LowPart; break; } if (This->offset.u.LowPart>This->stde.pps_size) This->offset.u.LowPart=This->stde.pps_size; if (newpos) *newpos = This->offset; return S_OK; } /****************************************************************************** * IStream16_Read [STORAGE.521] */ HRESULT CDECL IStream16_fnRead(IStream16 *iface, void *pv, ULONG cb, ULONG *pcbRead) { IStream16Impl *This = impl_from_IStream16(iface); BYTE block[BIGSIZE]; ULONG *bytesread=pcbRead,xxread; int blocknr; LPBYTE pbv = pv; TRACE_(relay)("(%p)->(%p,%d,%p)\n",This,pv,cb,pcbRead); if (!pcbRead) bytesread=&xxread; *bytesread = 0; if (cb>This->stde.pps_size-This->offset.u.LowPart) cb=This->stde.pps_size-This->offset.u.LowPart; if (This->stde.pps_size < 0x1000) { /* use small block reader */ blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE); while (cb) { unsigned int cc; if (!STORAGE_get_small_block(&This->str,blocknr,block)) { WARN("small block read failed!!!\n"); return E_FAIL; } cc = cb; if (cc>SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1))) cc=SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1)); memcpy(pbv,block+(This->offset.u.LowPart&(SMALLSIZE-1)),cc); This->offset.u.LowPart+=cc; pbv+=cc; *bytesread+=cc; cb-=cc; blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr); } } else { /* use big block reader */ blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE); while (cb) { unsigned int cc; if (!STORAGE_get_big_block(&This->str,blocknr,block)) { WARN("big block read failed!!!\n"); return E_FAIL; } cc = cb; if (cc>BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1))) cc=BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1)); memcpy(pbv,block+(This->offset.u.LowPart&(BIGSIZE-1)),cc); This->offset.u.LowPart+=cc; pbv+=cc; *bytesread+=cc; cb-=cc; blocknr=STORAGE_get_next_big_blocknr(&This->str,blocknr); } } return S_OK; } /****************************************************************************** * IStream16_Write [STORAGE.522] */ HRESULT CDECL IStream16_fnWrite(IStream16 *iface, const void *pv, ULONG cb, ULONG *pcbWrite) { IStream16Impl *This = impl_from_IStream16(iface); BYTE block[BIGSIZE]; ULONG *byteswritten=pcbWrite,xxwritten; int oldsize,newsize,i,curoffset=0,lastblocknr,blocknr,cc; const BYTE* pbv = pv; if (!pcbWrite) byteswritten=&xxwritten; *byteswritten = 0; TRACE_(relay)("(%p)->(%p,%d,%p)\n",This,pv,cb,pcbWrite); /* do we need to junk some blocks? */ newsize = This->offset.u.LowPart+cb; oldsize = This->stde.pps_size; if (newsize < oldsize) { if (oldsize < 0x1000) { /* only small blocks */ blocknr=STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,newsize/SMALLSIZE); assert(blocknr>=0); /* will set the rest of the chain to 'free' */ if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) return E_FAIL; } else { if (newsize >= 0x1000) { blocknr=STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,newsize/BIGSIZE); assert(blocknr>=0); /* will set the rest of the chain to 'free' */ if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) return E_FAIL; } else { /* Migrate large blocks to small blocks * (we just migrate newsize bytes) */ LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,newsize+BIGSIZE); HRESULT r = E_FAIL; cc = newsize; blocknr = This->stde.pps_sb; curdata = data; while (cc>0) { if (!STORAGE_get_big_block(&This->str,blocknr,curdata)) { HeapFree(GetProcessHeap(),0,data); return E_FAIL; } curdata += BIGSIZE; cc -= BIGSIZE; blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr); } /* frees complete chain for this stream */ if (!STORAGE_set_big_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE)) goto err; curdata = data; blocknr = This->stde.pps_sb = STORAGE_get_free_small_blocknr(&This->str); if (blocknr<0) goto err; cc = newsize; while (cc>0) { if (!STORAGE_put_small_block(&This->str,blocknr,curdata)) goto err; cc -= SMALLSIZE; if (cc<=0) { if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) goto err; break; } else { int newblocknr = STORAGE_get_free_small_blocknr(&This->str); if (newblocknr<0) goto err; if (!STORAGE_set_small_chain(&This->str,blocknr,newblocknr)) goto err; blocknr = newblocknr; } curdata += SMALLSIZE; } r = S_OK; err: HeapFree(GetProcessHeap(),0,data); if(r != S_OK) return r; } } This->stde.pps_size = newsize; } if (newsize > oldsize) { if (oldsize >= 0x1000) { /* should return the block right before the 'endofchain' */ blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/BIGSIZE); assert(blocknr>=0); lastblocknr = blocknr; for (i=oldsize/BIGSIZE;istr); if (blocknr<0) return E_FAIL; if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr)) return E_FAIL; lastblocknr = blocknr; } if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) return E_FAIL; } else { if (newsize < 0x1000) { /* find startblock */ if (!oldsize) This->stde.pps_sb = blocknr = STORAGE_get_free_small_blocknr(&This->str); else blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/SMALLSIZE); if (blocknr<0) return E_FAIL; /* allocate required new small blocks */ lastblocknr = blocknr; for (i=oldsize/SMALLSIZE;istr); if (blocknr<0) return E_FAIL; if (!STORAGE_set_small_chain(&This->str,lastblocknr,blocknr)) return E_FAIL; lastblocknr = blocknr; } /* and terminate the chain */ if (!STORAGE_set_small_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) return E_FAIL; } else { if (!oldsize) { /* no single block allocated yet */ blocknr=STORAGE_get_free_big_blocknr(&This->str); if (blocknr<0) return E_FAIL; This->stde.pps_sb = blocknr; } else { /* Migrate small blocks to big blocks */ LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,oldsize+BIGSIZE); HRESULT r = E_FAIL; cc = oldsize; blocknr = This->stde.pps_sb; curdata = data; /* slurp in */ while (cc>0) { if (!STORAGE_get_small_block(&This->str,blocknr,curdata)) goto err2; curdata += SMALLSIZE; cc -= SMALLSIZE; blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr); } /* free small block chain */ if (!STORAGE_set_small_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE)) goto err2; curdata = data; blocknr = This->stde.pps_sb = STORAGE_get_free_big_blocknr(&This->str); if (blocknr<0) goto err2; /* put the data into the big blocks */ cc = This->stde.pps_size; while (cc>0) { if (!STORAGE_put_big_block(&This->str,blocknr,curdata)) goto err2; cc -= BIGSIZE; if (cc<=0) { if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) goto err2; break; } else { int newblocknr = STORAGE_get_free_big_blocknr(&This->str); if (newblocknr<0) goto err2; if (!STORAGE_set_big_chain(&This->str,blocknr,newblocknr)) goto err2; blocknr = newblocknr; } curdata += BIGSIZE; } r = S_OK; err2: HeapFree(GetProcessHeap(),0,data); if(r != S_OK) return r; } /* generate big blocks to fit the new data */ lastblocknr = blocknr; for (i=oldsize/BIGSIZE;istr); if (blocknr<0) return E_FAIL; if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr)) return E_FAIL; lastblocknr = blocknr; } /* terminate chain */ if (!STORAGE_set_big_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) return E_FAIL; } } This->stde.pps_size = newsize; } /* There are just some cases where we didn't modify it, we write it out * every time */ if (!STORAGE_put_pps_entry(&This->str,This->ppsent,&(This->stde))) return E_FAIL; /* finally the write pass */ if (This->stde.pps_size < 0x1000) { blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE); assert(blocknr>=0); while (cb>0) { /* we ensured that it is allocated above */ assert(blocknr>=0); /* Read old block every time, since we can have * overlapping data at START and END of the write */ if (!STORAGE_get_small_block(&This->str,blocknr,block)) return E_FAIL; cc = SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1)); if (cc>cb) cc=cb; memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(SMALLSIZE-1)), pbv+curoffset, cc ); if (!STORAGE_put_small_block(&This->str,blocknr,block)) return E_FAIL; cb -= cc; curoffset += cc; pbv += cc; This->offset.u.LowPart += cc; *byteswritten += cc; blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr); } } else { blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE); assert(blocknr>=0); while (cb>0) { /* we ensured that it is allocated above, so it better is */ assert(blocknr>=0); /* read old block every time, since we can have * overlapping data at START and END of the write */ if (!STORAGE_get_big_block(&This->str,blocknr,block)) return E_FAIL; cc = BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1)); if (cc>cb) cc=cb; memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(BIGSIZE-1)), pbv+curoffset, cc ); if (!STORAGE_put_big_block(&This->str,blocknr,block)) return E_FAIL; cb -= cc; curoffset += cc; pbv += cc; This->offset.u.LowPart += cc; *byteswritten += cc; blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr); } } return S_OK; } /****************************************************************************** * _create_istream16 [Internal] */ static void _create_istream16(LPSTREAM16 *str) { IStream16Impl* lpst; if (!strvt16.QueryInterface) { HMODULE16 wp = GetModuleHandle16("STORAGE"); if (wp>=32) { /* FIXME: what is This GetProcAddress16. Should the name be IStream16_QueryInterface of IStream16_fnQueryInterface */ #define VTENT(xfn) strvt16.xfn = (void*)GetProcAddress16(wp,"IStream16_"#xfn);assert(strvt16.xfn) VTENT(QueryInterface); VTENT(AddRef); VTENT(Release); VTENT(Read); VTENT(Write); VTENT(Seek); VTENT(SetSize); VTENT(CopyTo); VTENT(Commit); VTENT(Revert); VTENT(LockRegion); VTENT(UnlockRegion); VTENT(Stat); VTENT(Clone); #undef VTENT segstrvt16 = (const IStream16Vtbl*)MapLS( &strvt16 ); } else { #define VTENT(xfn) strvt16.xfn = IStream16_fn##xfn; VTENT(QueryInterface); VTENT(AddRef); VTENT(Release); VTENT(Read); VTENT(Write); VTENT(Seek); /* VTENT(CopyTo); VTENT(Commit); VTENT(SetSize); VTENT(Revert); VTENT(LockRegion); VTENT(UnlockRegion); VTENT(Stat); VTENT(Clone); */ #undef VTENT segstrvt16 = &strvt16; } } lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) ); lpst->IStream16_iface.lpVtbl = segstrvt16; lpst->ref = 1; lpst->thisptr = MapLS( lpst ); lpst->str.hf = NULL; lpst->str.lockbytes = 0; *str = (void*)lpst->thisptr; } static inline IStorage16Impl *impl_from_IStorage16(IStorage16 *iface) { return CONTAINING_RECORD(iface, IStorage16Impl, IStorage16_iface); } /****************************************************************************** * IStorage16_QueryInterface [STORAGE.500] */ HRESULT CDECL IStorage16_fnQueryInterface(IStorage16 *iface, REFIID refiid, void **obj) { IStorage16Impl *This = impl_from_IStorage16(iface); TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj); if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) { *obj = This; return 0; } return OLE_E_ENUM_NOMORE; } /****************************************************************************** * IStorage16_AddRef [STORAGE.501] */ ULONG CDECL IStorage16_fnAddRef(IStorage16 *iface) { IStorage16Impl *This = impl_from_IStorage16(iface); return InterlockedIncrement(&This->ref); } /****************************************************************************** * IStorage16_Release [STORAGE.502] */ ULONG CDECL IStorage16_fnRelease(IStorage16 *iface) { IStorage16Impl *This = impl_from_IStorage16(iface); ULONG ref; ref = InterlockedDecrement(&This->ref); if (!ref) { UnMapLS( This->thisptr ); HeapFree( GetProcessHeap(), 0, This ); } return ref; } /****************************************************************************** * IStorage16_Stat [STORAGE.517] */ HRESULT CDECL IStorage16_fnStat(IStorage16 *iface, STATSTG16 *pstatstg, DWORD grfStatFlag) { IStorage16Impl *This = impl_from_IStorage16(iface); DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL ); LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len ); TRACE("(%p)->(%p,0x%08x)\n", This,pstatstg,grfStatFlag ); WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL ); pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA ); pstatstg->type = This->stde.pps_type; pstatstg->cbSize.u.LowPart = This->stde.pps_size; pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */ pstatstg->atime = This->stde.pps_ft2; /* FIXME */ pstatstg->ctime = This->stde.pps_ft2; /* FIXME */ pstatstg->grfMode = 0; /* FIXME */ pstatstg->grfLocksSupported = 0; /* FIXME */ pstatstg->clsid = This->stde.pps_guid; pstatstg->grfStateBits = 0; /* FIXME */ pstatstg->reserved = 0; return S_OK; } /****************************************************************************** * IStorage16_Commit [STORAGE.509] */ HRESULT CDECL IStorage16_fnCommit(IStorage16 *iface, DWORD commitflags) { IStorage16Impl *This = impl_from_IStorage16(iface); FIXME("(%p)->(0x%08x),STUB!\n", This,commitflags ); return S_OK; } /****************************************************************************** * IStorage16_CopyTo [STORAGE.507] */ HRESULT CDECL IStorage16_fnCopyTo(IStorage16 *iface, DWORD ciidExclude, const IID *rgiidExclude, SNB16 SNB16Exclude, IStorage16 *pstgDest) { IStorage16Impl *This = impl_from_IStorage16(iface); FIXME("IStorage16(%p)->(0x%08x,%s,%p,%p),stub!\n", This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest ); return S_OK; } /****************************************************************************** * IStorage16_CreateStorage [STORAGE.505] */ HRESULT CDECL IStorage16_fnCreateStorage(IStorage16 *iface, LPCOLESTR16 pwcsName, DWORD grfMode, DWORD dwStgFormat, DWORD reserved2, IStorage16 **ppstg) { IStorage16Impl *This = impl_from_IStorage16(iface); IStorage16Impl* lpstg; int ppsent,x; struct storage_pps_entry stde; struct storage_header sth; BOOL ret; int nPPSEntries; READ_HEADER(&This->str); TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n", This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg ); if (grfMode & STGM_TRANSACTED) FIXME("We do not support transacted Compound Storage. Using direct mode.\n"); _create_istorage16(ppstg); lpstg = MapSL((SEGPTR)*ppstg); if (This->str.hf) { DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(), &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS ); } else { lpstg->str.lockbytes = This->str.lockbytes; _ilockbytes16_addref(This->str.lockbytes); } ppsent=STORAGE_get_free_pps_entry(&lpstg->str); if (ppsent<0) return E_FAIL; stde=This->stde; if (stde.pps_dir==-1) { stde.pps_dir = ppsent; x = This->ppsent; } else { FIXME(" use prev chain too ?\n"); x=stde.pps_dir; if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde)) return E_FAIL; while (stde.pps_next!=-1) { x=stde.pps_next; if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde)) return E_FAIL; } stde.pps_next = ppsent; } ret = STORAGE_put_pps_entry(&lpstg->str,x,&stde); assert(ret); nPPSEntries = STORAGE_get_pps_entry(&lpstg->str,ppsent,&(lpstg->stde)); assert(nPPSEntries == 1); MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname, sizeof(lpstg->stde.pps_rawname)/sizeof(WCHAR)); lpstg->stde.pps_sizeofname = (strlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR); lpstg->stde.pps_next = -1; lpstg->stde.pps_prev = -1; lpstg->stde.pps_dir = -1; lpstg->stde.pps_sb = -1; lpstg->stde.pps_size = 0; lpstg->stde.pps_type = 1; lpstg->ppsent = ppsent; /* FIXME: timestamps? */ if (!STORAGE_put_pps_entry(&lpstg->str,ppsent,&(lpstg->stde))) return E_FAIL; return S_OK; } /****************************************************************************** * IStorage16_CreateStream [STORAGE.503] */ HRESULT CDECL IStorage16_fnCreateStream(IStorage16 *iface, LPCOLESTR16 pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream16 **ppstm) { IStorage16Impl *This = impl_from_IStorage16(iface); IStream16Impl* lpstr; int ppsent,x; struct storage_pps_entry stde; BOOL ret; int nPPSEntries; TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n", This,pwcsName,grfMode,reserved1,reserved2,ppstm ); if (grfMode & STGM_TRANSACTED) FIXME("We do not support transacted Compound Storage. Using direct mode.\n"); _create_istream16(ppstm); lpstr = MapSL((SEGPTR)*ppstm); if (This->str.hf) { DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(), &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS ); } else { lpstr->str.lockbytes = This->str.lockbytes; _ilockbytes16_addref(This->str.lockbytes); } lpstr->offset.u.LowPart = 0; lpstr->offset.u.HighPart= 0; ppsent=STORAGE_get_free_pps_entry(&lpstr->str); if (ppsent<0) return E_FAIL; stde=This->stde; if (stde.pps_next==-1) x=This->ppsent; else while (stde.pps_next!=-1) { x=stde.pps_next; if (1!=STORAGE_get_pps_entry(&lpstr->str,x,&stde)) return E_FAIL; } stde.pps_next = ppsent; ret = STORAGE_put_pps_entry(&lpstr->str,x,&stde); assert(ret); nPPSEntries = STORAGE_get_pps_entry(&lpstr->str,ppsent,&(lpstr->stde)); assert(nPPSEntries == 1); MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname, sizeof(lpstr->stde.pps_rawname)/sizeof(WCHAR)); lpstr->stde.pps_sizeofname = (strlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR); lpstr->stde.pps_next = -1; lpstr->stde.pps_prev = -1; lpstr->stde.pps_dir = -1; lpstr->stde.pps_sb = -1; lpstr->stde.pps_size = 0; lpstr->stde.pps_type = 2; lpstr->ppsent = ppsent; /* FIXME: timestamps? */ if (!STORAGE_put_pps_entry(&lpstr->str,ppsent,&(lpstr->stde))) return E_FAIL; return S_OK; } /****************************************************************************** * IStorage16_OpenStorage [STORAGE.506] */ HRESULT CDECL IStorage16_fnOpenStorage(IStorage16 *iface, LPCOLESTR16 pwcsName, IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg) { IStorage16Impl *This = impl_from_IStorage16(iface); IStream16Impl* lpstg; WCHAR name[33]; int newpps; TRACE("(%p)->(%s,%p,0x%08x,%p,0x%08x,%p)\n", This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg ); if (grfMode & STGM_TRANSACTED) FIXME("We do not support transacted Compound Storage. Using direct mode.\n"); _create_istorage16(ppstg); lpstg = MapSL((SEGPTR)*ppstg); if (This->str.hf) { DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(), &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS ); } else { lpstg->str.lockbytes = This->str.lockbytes; _ilockbytes16_addref(This->str.lockbytes); } MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR)); newpps = STORAGE_look_for_named_pps(&lpstg->str,This->stde.pps_dir,name); if (newpps==-1) { IStream16_fnRelease(&lpstg->IStream16_iface); return E_FAIL; } if (1!=STORAGE_get_pps_entry(&lpstg->str,newpps,&(lpstg->stde))) { IStream16_fnRelease(&lpstg->IStream16_iface); return E_FAIL; } lpstg->ppsent = newpps; return S_OK; } /****************************************************************************** * IStorage16_OpenStream [STORAGE.504] */ HRESULT CDECL IStorage16_fnOpenStream(IStorage16 *iface, LPCOLESTR16 pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream16 **ppstm) { IStorage16Impl *This = impl_from_IStorage16(iface); IStream16Impl* lpstr; WCHAR name[33]; int newpps; TRACE("(%p)->(%s,%p,0x%08x,0x%08x,%p)\n", This,pwcsName,reserved1,grfMode,reserved2,ppstm ); if (grfMode & STGM_TRANSACTED) FIXME("We do not support transacted Compound Storage. Using direct mode.\n"); _create_istream16(ppstm); lpstr = MapSL((SEGPTR)*ppstm); if (This->str.hf) { DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(), &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS ); } else { lpstr->str.lockbytes = This->str.lockbytes; _ilockbytes16_addref(This->str.lockbytes); } MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR)); newpps = STORAGE_look_for_named_pps(&lpstr->str,This->stde.pps_dir,name); if (newpps==-1) { IStream16_fnRelease(&lpstr->IStream16_iface); return E_FAIL; } if (1!=STORAGE_get_pps_entry(&lpstr->str,newpps,&(lpstr->stde))) { IStream16_fnRelease(&lpstr->IStream16_iface); return E_FAIL; } lpstr->offset.u.LowPart = 0; lpstr->offset.u.HighPart = 0; lpstr->ppsent = newpps; return S_OK; } /****************************************************************************** * _create_istorage16 [INTERNAL] */ static void _create_istorage16(LPSTORAGE16 *stg) { IStorage16Impl* lpst; if (!stvt16.QueryInterface) { HMODULE16 wp = GetModuleHandle16("STORAGE"); if (wp>=32) { #define VTENT(xfn) stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn); VTENT(QueryInterface) VTENT(AddRef) VTENT(Release) VTENT(CreateStream) VTENT(OpenStream) VTENT(CreateStorage) VTENT(OpenStorage) VTENT(CopyTo) VTENT(MoveElementTo) VTENT(Commit) VTENT(Revert) VTENT(EnumElements) VTENT(DestroyElement) VTENT(RenameElement) VTENT(SetElementTimes) VTENT(SetClass) VTENT(SetStateBits) VTENT(Stat) #undef VTENT segstvt16 = (const IStorage16Vtbl*)MapLS( &stvt16 ); } else { #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn; VTENT(QueryInterface) VTENT(AddRef) VTENT(Release) VTENT(CreateStream) VTENT(OpenStream) VTENT(CreateStorage) VTENT(OpenStorage) VTENT(CopyTo) VTENT(Commit) /* not (yet) implemented ... VTENT(MoveElementTo) VTENT(Revert) VTENT(EnumElements) VTENT(DestroyElement) VTENT(RenameElement) VTENT(SetElementTimes) VTENT(SetClass) VTENT(SetStateBits) VTENT(Stat) */ #undef VTENT segstvt16 = &stvt16; } } lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) ); lpst->IStorage16_iface.lpVtbl = segstvt16; lpst->str.hf = NULL; lpst->str.lockbytes = 0; lpst->ref = 1; lpst->thisptr = MapLS(lpst); *stg = (void*)lpst->thisptr; } /****************************************************************************** * Storage API functions */ /****************************************************************************** * StgCreateDocFileA [STORAGE.1] */ HRESULT WINAPI StgCreateDocFile16( LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen ) { HANDLE hf; int i,ret; IStorage16Impl* lpstg; struct storage_pps_entry stde; TRACE("(%s,0x%08x,0x%08x,%p)\n", pwcsName,grfMode,reserved,ppstgOpen ); _create_istorage16(ppstgOpen); hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0); if (hf==INVALID_HANDLE_VALUE) { WARN("couldn't open file for storage:%d\n",GetLastError()); return E_FAIL; } lpstg = MapSL((SEGPTR)*ppstgOpen); lpstg->str.hf = hf; lpstg->str.lockbytes = 0; /* FIXME: check for existence before overwriting? */ if (!STORAGE_init_storage(&lpstg->str)) { CloseHandle(hf); return E_FAIL; } i=0;ret=0; while (!ret) { /* neither 1 nor <0 */ ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde); if ((ret==1) && (stde.pps_type==5)) { lpstg->stde = stde; lpstg->ppsent = i; break; } i++; } if (ret!=1) { IStorage16_fnRelease(&lpstg->IStorage16_iface); return E_FAIL; } return S_OK; } /****************************************************************************** * StgIsStorageFile [STORAGE.5] */ HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) { UNICODE_STRING strW; HRESULT ret; RtlCreateUnicodeStringFromAsciiz(&strW, fn); ret = StgIsStorageFile( strW.Buffer ); RtlFreeUnicodeString( &strW ); return ret; } /****************************************************************************** * StgOpenStorage [STORAGE.3] */ HRESULT WINAPI StgOpenStorage16( LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode, SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen ) { HANDLE hf; int ret,i; IStorage16Impl* lpstg; struct storage_pps_entry stde; TRACE("(%s,%p,0x%08x,%p,%d,%p)\n", pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen ); _create_istorage16(ppstgOpen); hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if (hf==INVALID_HANDLE_VALUE) { WARN("Couldn't open file for storage\n"); return E_FAIL; } lpstg = MapSL((SEGPTR)*ppstgOpen); lpstg->str.hf = hf; i=0;ret=0; while (!ret) { /* neither 1 nor <0 */ ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde); if ((ret==1) && (stde.pps_type==5)) { lpstg->stde=stde; break; } i++; } if (ret!=1) { IStorage16_fnRelease(&lpstg->IStorage16_iface); return E_FAIL; } return S_OK; } /****************************************************************************** * StgIsStorageILockBytes [STORAGE.6] * * Determines if the ILockBytes contains a storage object. */ HRESULT WINAPI StgIsStorageILockBytes16(SEGPTR plkbyt) { DWORD args[6]; HRESULT hres; HANDLE16 hsig; args[0] = (DWORD)plkbyt; /* iface */ args[1] = args[2] = 0; /* ULARGE_INTEGER offset */ args[3] = WOWGlobalAllocLock16( 0, 8, &hsig ); /* sig */ args[4] = 8; args[5] = 0; if (!WOWCallback16Ex( (DWORD)((const ILockBytes16Vtbl*)MapSL( (SEGPTR)((LPLOCKBYTES16)MapSL(plkbyt))->lpVtbl) )->ReadAt, WCB16_PASCAL, 6*sizeof(DWORD), (LPVOID)args, (LPDWORD)&hres )) { ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %x\n",hres); return hres; } if (memcmp(MapSL(args[3]), STORAGE_magic, sizeof(STORAGE_magic)) == 0) { WOWGlobalUnlockFree16(args[3]); return S_OK; } WOWGlobalUnlockFree16(args[3]); return S_FALSE; } /****************************************************************************** * StgOpenStorageOnILockBytes [STORAGE.4] * * PARAMS * plkbyt FIXME: Should probably be an ILockBytes16 *. */ HRESULT WINAPI StgOpenStorageOnILockBytes16( SEGPTR plkbyt, IStorage16 *pstgPriority, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstgOpen) { IStorage16Impl* lpstg; int i,ret; struct storage_pps_entry stde; FIXME("(%x, %p, 0x%08x, %d, %x, %p)\n", plkbyt, pstgPriority, grfMode, (int)snbExclude, reserved, ppstgOpen); if ((plkbyt == 0) || (ppstgOpen == 0)) return STG_E_INVALIDPOINTER; *ppstgOpen = 0; _create_istorage16(ppstgOpen); lpstg = MapSL((SEGPTR)*ppstgOpen); lpstg->str.hf = NULL; lpstg->str.lockbytes = plkbyt; i=0;ret=0; while (!ret) { /* neither 1 nor <0 */ ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde); if ((ret==1) && (stde.pps_type==5)) { lpstg->stde=stde; break; } i++; } if (ret!=1) { IStorage16_fnRelease(&lpstg->IStorage16_iface); return E_FAIL; } return S_OK; }