1 /* 2 * Compound Storage (32 bit version) 3 * 4 * Implemented using the documentation of the LAOLA project at 5 * <URL:http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/index.html> 6 * (Thanks to Martin Schwartz <schwartz@cs.tu-berlin.de>) 7 * 8 * This include file contains definitions of types and function 9 * prototypes that are used in the many files implementing the 10 * storage functionality 11 * 12 * Copyright 1998,1999 Francis Beaudet 13 * Copyright 1998,1999 Thuy Nguyen 14 * Copyright 2010 Vincent Povirk for CodeWeavers 15 * 16 * This library is free software; you can redistribute it and/or 17 * modify it under the terms of the GNU Lesser General Public 18 * License as published by the Free Software Foundation; either 19 * version 2.1 of the License, or (at your option) any later version. 20 * 21 * This library is distributed in the hope that it will be useful, 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 * Lesser General Public License for more details. 25 * 26 * You should have received a copy of the GNU Lesser General Public 27 * License along with this library; if not, write to the Free Software 28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 29 */ 30 31 #ifndef __STORAGE32_H__ 32 #define __STORAGE32_H__ 33 34 /* 35 * Definitions for the file format offsets. 36 */ 37 static const ULONG OFFSET_MINORVERSION = 0x00000018; 38 static const ULONG OFFSET_MAJORVERSION = 0x0000001a; 39 static const ULONG OFFSET_BYTEORDERMARKER = 0x0000001c; 40 static const ULONG OFFSET_BIGBLOCKSIZEBITS = 0x0000001e; 41 static const ULONG OFFSET_SMALLBLOCKSIZEBITS = 0x00000020; 42 static const ULONG OFFSET_DIRSECTORCOUNT = 0x00000028; 43 static const ULONG OFFSET_BBDEPOTCOUNT = 0x0000002C; 44 static const ULONG OFFSET_ROOTSTARTBLOCK = 0x00000030; 45 static const ULONG OFFSET_TRANSACTIONSIG = 0x00000034; 46 static const ULONG OFFSET_SMALLBLOCKLIMIT = 0x00000038; 47 static const ULONG OFFSET_SBDEPOTSTART = 0x0000003C; 48 static const ULONG OFFSET_SBDEPOTCOUNT = 0x00000040; 49 static const ULONG OFFSET_EXTBBDEPOTSTART = 0x00000044; 50 static const ULONG OFFSET_EXTBBDEPOTCOUNT = 0x00000048; 51 static const ULONG OFFSET_BBDEPOTSTART = 0x0000004C; 52 static const ULONG OFFSET_PS_NAME = 0x00000000; 53 static const ULONG OFFSET_PS_NAMELENGTH = 0x00000040; 54 static const ULONG OFFSET_PS_STGTYPE = 0x00000042; 55 static const ULONG OFFSET_PS_LEFTCHILD = 0x00000044; 56 static const ULONG OFFSET_PS_RIGHTCHILD = 0x00000048; 57 static const ULONG OFFSET_PS_DIRROOT = 0x0000004C; 58 static const ULONG OFFSET_PS_GUID = 0x00000050; 59 static const ULONG OFFSET_PS_CTIMELOW = 0x00000064; 60 static const ULONG OFFSET_PS_CTIMEHIGH = 0x00000068; 61 static const ULONG OFFSET_PS_MTIMELOW = 0x0000006C; 62 static const ULONG OFFSET_PS_MTIMEHIGH = 0x00000070; 63 static const ULONG OFFSET_PS_STARTBLOCK = 0x00000074; 64 static const ULONG OFFSET_PS_SIZE = 0x00000078; 65 static const ULONG OFFSET_PS_SIZE_HIGH = 0x0000007C; 66 static const WORD DEF_BIG_BLOCK_SIZE_BITS = 0x0009; 67 static const WORD MIN_BIG_BLOCK_SIZE_BITS = 0x0009; 68 static const WORD MAX_BIG_BLOCK_SIZE_BITS = 0x000c; 69 static const WORD DEF_SMALL_BLOCK_SIZE_BITS = 0x0006; 70 static const WORD DEF_BIG_BLOCK_SIZE = 0x0200; 71 static const WORD DEF_SMALL_BLOCK_SIZE = 0x0040; 72 static const ULONG BLOCK_FIRST_SPECIAL = 0xFFFFFFFB; 73 static const ULONG BLOCK_EXTBBDEPOT = 0xFFFFFFFC; 74 static const ULONG BLOCK_SPECIAL = 0xFFFFFFFD; 75 static const ULONG BLOCK_END_OF_CHAIN = 0xFFFFFFFE; 76 static const ULONG BLOCK_UNUSED = 0xFFFFFFFF; 77 static const ULONG DIRENTRY_NULL = 0xFFFFFFFF; 78 79 #define DIRENTRY_NAME_MAX_LEN 0x20 80 #define DIRENTRY_NAME_BUFFER_LEN 0x40 81 82 #define RAW_DIRENTRY_SIZE 0x00000080 83 84 #define HEADER_SIZE 512 85 86 #define MIN_BIG_BLOCK_SIZE 0x200 87 #define MAX_BIG_BLOCK_SIZE 0x1000 88 89 /* 90 * Type of child entry link 91 */ 92 #define DIRENTRY_RELATION_PREVIOUS 0 93 #define DIRENTRY_RELATION_NEXT 1 94 #define DIRENTRY_RELATION_DIR 2 95 96 /* 97 * type constant used in files for the root storage 98 */ 99 #define STGTY_ROOT 0x05 100 101 #define COUNT_BBDEPOTINHEADER 109 102 103 /* FIXME: This value is stored in the header, but we hard-code it to 0x1000. */ 104 #define LIMIT_TO_USE_SMALL_BLOCK 0x1000 105 106 #define STGM_ACCESS_MODE(stgm) ((stgm)&0x0000f) 107 #define STGM_SHARE_MODE(stgm) ((stgm)&0x000f0) 108 #define STGM_CREATE_MODE(stgm) ((stgm)&0x0f000) 109 110 #define STGM_KNOWN_FLAGS (0xf0ff | \ 111 STGM_TRANSACTED | STGM_CONVERT | STGM_PRIORITY | STGM_NOSCRATCH | \ 112 STGM_NOSNAPSHOT | STGM_DIRECT_SWMR | STGM_DELETEONRELEASE | STGM_SIMPLE) 113 114 /* 115 * Forward declarations of all the structures used by the storage 116 * module. 117 */ 118 typedef struct StorageBaseImpl StorageBaseImpl; 119 typedef struct StorageBaseImplVtbl StorageBaseImplVtbl; 120 typedef struct StorageImpl StorageImpl; 121 typedef struct BlockChainStream BlockChainStream; 122 typedef struct SmallBlockChainStream SmallBlockChainStream; 123 typedef struct IEnumSTATSTGImpl IEnumSTATSTGImpl; 124 typedef struct DirEntry DirEntry; 125 typedef struct StgStreamImpl StgStreamImpl; 126 127 /* 128 * A reference to a directory entry in the file or a transacted cache. 129 */ 130 typedef ULONG DirRef; 131 132 /* 133 * This utility structure is used to read/write the information in a directory 134 * entry. 135 */ 136 struct DirEntry 137 { 138 WCHAR name[DIRENTRY_NAME_MAX_LEN]; 139 WORD sizeOfNameString; 140 BYTE stgType; 141 DirRef leftChild; 142 DirRef rightChild; 143 DirRef dirRootEntry; 144 GUID clsid; 145 FILETIME ctime; 146 FILETIME mtime; 147 ULONG startingBlock; 148 ULARGE_INTEGER size; 149 }; 150 151 HRESULT FileLockBytesImpl_Construct(HANDLE hFile, DWORD openFlags, LPCWSTR pwcsName, ILockBytes **pLockBytes) DECLSPEC_HIDDEN; 152 153 /************************************************************************* 154 * Ole Convert support 155 */ 156 157 HRESULT STORAGE_CreateOleStream(IStorage*, DWORD) DECLSPEC_HIDDEN; 158 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName) DECLSPEC_HIDDEN; 159 160 enum swmr_mode 161 { 162 SWMR_None, 163 SWMR_Writer, 164 SWMR_Reader 165 }; 166 167 /**************************************************************************** 168 * StorageBaseImpl definitions. 169 * 170 * This structure defines the base information contained in all implementations 171 * of IStorage contained in this file storage implementation. 172 * 173 * In OOP terms, this is the base class for all the IStorage implementations 174 * contained in this file. 175 */ 176 struct StorageBaseImpl 177 { 178 IStorage IStorage_iface; 179 IPropertySetStorage IPropertySetStorage_iface; /* interface for adding a properties stream */ 180 IDirectWriterLock IDirectWriterLock_iface; 181 LONG ref; 182 183 /* 184 * Stream tracking list 185 */ 186 187 struct list strmHead; 188 189 /* 190 * Storage tracking list 191 */ 192 struct list storageHead; 193 194 /* 195 * TRUE if this object has been invalidated 196 */ 197 BOOL reverted; 198 199 /* 200 * Index of the directory entry of this storage 201 */ 202 DirRef storageDirEntry; 203 204 /* 205 * virtual methods. 206 */ 207 const StorageBaseImplVtbl *baseVtbl; 208 209 /* 210 * flags that this storage was opened or created with 211 */ 212 DWORD openFlags; 213 214 /* 215 * State bits appear to only be preserved while running. No in the stream 216 */ 217 DWORD stateBits; 218 219 BOOL create; /* Was the storage created or opened. 220 The behaviour of STGM_SIMPLE depends on this */ 221 /* 222 * If this storage was opened in transacted mode, the object that implements 223 * the transacted snapshot or cache. 224 */ 225 StorageBaseImpl *transactedChild; 226 enum swmr_mode lockingrole; 227 }; 228 229 /* virtual methods for StorageBaseImpl objects */ 230 struct StorageBaseImplVtbl { 231 void (*Destroy)(StorageBaseImpl*); 232 void (*Invalidate)(StorageBaseImpl*); 233 HRESULT (*Flush)(StorageBaseImpl*); 234 HRESULT (*GetFilename)(StorageBaseImpl*,LPWSTR*); 235 HRESULT (*CreateDirEntry)(StorageBaseImpl*,const DirEntry*,DirRef*); 236 HRESULT (*WriteDirEntry)(StorageBaseImpl*,DirRef,const DirEntry*); 237 HRESULT (*ReadDirEntry)(StorageBaseImpl*,DirRef,DirEntry*); 238 HRESULT (*DestroyDirEntry)(StorageBaseImpl*,DirRef); 239 HRESULT (*StreamReadAt)(StorageBaseImpl*,DirRef,ULARGE_INTEGER,ULONG,void*,ULONG*); 240 HRESULT (*StreamWriteAt)(StorageBaseImpl*,DirRef,ULARGE_INTEGER,ULONG,const void*,ULONG*); 241 HRESULT (*StreamSetSize)(StorageBaseImpl*,DirRef,ULARGE_INTEGER); 242 HRESULT (*StreamLink)(StorageBaseImpl*,DirRef,DirRef); 243 HRESULT (*GetTransactionSig)(StorageBaseImpl*,ULONG*,BOOL); 244 HRESULT (*SetTransactionSig)(StorageBaseImpl*,ULONG); 245 HRESULT (*LockTransaction)(StorageBaseImpl*,BOOL); 246 HRESULT (*UnlockTransaction)(StorageBaseImpl*,BOOL); 247 }; 248 249 static inline void StorageBaseImpl_Destroy(StorageBaseImpl *This) 250 { 251 This->baseVtbl->Destroy(This); 252 } 253 254 static inline void StorageBaseImpl_Invalidate(StorageBaseImpl *This) 255 { 256 This->baseVtbl->Invalidate(This); 257 } 258 259 static inline HRESULT StorageBaseImpl_Flush(StorageBaseImpl *This) 260 { 261 return This->baseVtbl->Flush(This); 262 } 263 264 static inline HRESULT StorageBaseImpl_GetFilename(StorageBaseImpl *This, LPWSTR *result) 265 { 266 return This->baseVtbl->GetFilename(This, result); 267 } 268 269 static inline HRESULT StorageBaseImpl_CreateDirEntry(StorageBaseImpl *This, 270 const DirEntry *newData, DirRef *index) 271 { 272 return This->baseVtbl->CreateDirEntry(This, newData, index); 273 } 274 275 static inline HRESULT StorageBaseImpl_WriteDirEntry(StorageBaseImpl *This, 276 DirRef index, const DirEntry *data) 277 { 278 return This->baseVtbl->WriteDirEntry(This, index, data); 279 } 280 281 static inline HRESULT StorageBaseImpl_ReadDirEntry(StorageBaseImpl *This, 282 DirRef index, DirEntry *data) 283 { 284 return This->baseVtbl->ReadDirEntry(This, index, data); 285 } 286 287 static inline HRESULT StorageBaseImpl_DestroyDirEntry(StorageBaseImpl *This, 288 DirRef index) 289 { 290 return This->baseVtbl->DestroyDirEntry(This, index); 291 } 292 293 /* Read up to size bytes from this directory entry's stream at the given offset. */ 294 static inline HRESULT StorageBaseImpl_StreamReadAt(StorageBaseImpl *This, 295 DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead) 296 { 297 return This->baseVtbl->StreamReadAt(This, index, offset, size, buffer, bytesRead); 298 } 299 300 /* Write size bytes to this directory entry's stream at the given offset, 301 * growing the stream if necessary. */ 302 static inline HRESULT StorageBaseImpl_StreamWriteAt(StorageBaseImpl *This, 303 DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten) 304 { 305 return This->baseVtbl->StreamWriteAt(This, index, offset, size, buffer, bytesWritten); 306 } 307 308 static inline HRESULT StorageBaseImpl_StreamSetSize(StorageBaseImpl *This, 309 DirRef index, ULARGE_INTEGER newsize) 310 { 311 return This->baseVtbl->StreamSetSize(This, index, newsize); 312 } 313 314 /* Make dst point to the same stream that src points to. Other stream operations 315 * will not work properly for entries that point to the same stream, so this 316 * must be a very temporary state, and only one entry pointing to a given stream 317 * may be reachable at any given time. */ 318 static inline HRESULT StorageBaseImpl_StreamLink(StorageBaseImpl *This, 319 DirRef dst, DirRef src) 320 { 321 return This->baseVtbl->StreamLink(This, dst, src); 322 } 323 324 static inline HRESULT StorageBaseImpl_GetTransactionSig(StorageBaseImpl *This, 325 ULONG* result, BOOL refresh) 326 { 327 return This->baseVtbl->GetTransactionSig(This, result, refresh); 328 } 329 330 static inline HRESULT StorageBaseImpl_SetTransactionSig(StorageBaseImpl *This, 331 ULONG value) 332 { 333 return This->baseVtbl->SetTransactionSig(This, value); 334 } 335 336 static inline HRESULT StorageBaseImpl_LockTransaction(StorageBaseImpl *This, BOOL write) 337 { 338 return This->baseVtbl->LockTransaction(This, write); 339 } 340 341 static inline HRESULT StorageBaseImpl_UnlockTransaction(StorageBaseImpl *This, BOOL write) 342 { 343 return This->baseVtbl->UnlockTransaction(This, write); 344 } 345 346 /**************************************************************************** 347 * StorageBaseImpl stream list handlers 348 */ 349 350 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm) DECLSPEC_HIDDEN; 351 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm) DECLSPEC_HIDDEN; 352 353 /* Number of BlockChainStream objects to cache in a StorageImpl */ 354 #define BLOCKCHAIN_CACHE_SIZE 4 355 356 /**************************************************************************** 357 * StorageImpl definitions. 358 * 359 * This implementation of the IStorage interface represents a root 360 * storage. Basically, a document file. 361 */ 362 struct StorageImpl 363 { 364 struct StorageBaseImpl base; 365 366 /* 367 * File header 368 */ 369 WORD bigBlockSizeBits; 370 WORD smallBlockSizeBits; 371 ULONG bigBlockSize; 372 ULONG smallBlockSize; 373 ULONG bigBlockDepotCount; 374 ULONG rootStartBlock; 375 ULONG smallBlockLimit; 376 ULONG smallBlockDepotStart; 377 ULONG extBigBlockDepotStart; 378 ULONG *extBigBlockDepotLocations; 379 ULONG extBigBlockDepotLocationsSize; 380 ULONG extBigBlockDepotCount; 381 ULONG bigBlockDepotStart[COUNT_BBDEPOTINHEADER]; 382 ULONG transactionSig; 383 384 ULONG extBlockDepotCached[MAX_BIG_BLOCK_SIZE / 4]; 385 ULONG indexExtBlockDepotCached; 386 387 ULONG blockDepotCached[MAX_BIG_BLOCK_SIZE / 4]; 388 ULONG indexBlockDepotCached; 389 ULONG prevFreeBlock; 390 391 /* All small blocks before this one are known to be in use. */ 392 ULONG firstFreeSmallBlock; 393 394 /* 395 * Abstraction of the big block chains for the chains of the header. 396 */ 397 BlockChainStream* rootBlockChain; 398 BlockChainStream* smallBlockDepotChain; 399 BlockChainStream* smallBlockRootChain; 400 401 /* Cache of block chain streams objects for directory entries */ 402 BlockChainStream* blockChainCache[BLOCKCHAIN_CACHE_SIZE]; 403 UINT blockChainToEvict; 404 405 ULONG locks_supported; 406 407 ILockBytes* lockBytes; 408 409 ULONG locked_bytes[8]; 410 }; 411 412 /**************************************************************************** 413 * StgStreamImpl definitions. 414 * 415 * This class implements the IStream interface and represents a stream 416 * located inside a storage object. 417 */ 418 struct StgStreamImpl 419 { 420 IStream IStream_iface; 421 LONG ref; 422 423 /* 424 * We are an entry in the storage object's stream handler list 425 */ 426 struct list StrmListEntry; 427 428 /* 429 * Storage that is the parent(owner) of the stream 430 */ 431 StorageBaseImpl* parentStorage; 432 433 /* 434 * Access mode of this stream. 435 */ 436 DWORD grfMode; 437 438 /* 439 * Index of the directory entry that owns (points to) this stream. 440 */ 441 DirRef dirEntry; 442 443 /* 444 * This is the current position of the cursor in the stream 445 */ 446 ULARGE_INTEGER currentPosition; 447 }; 448 449 static inline StgStreamImpl *impl_from_IStream( IStream *iface ) 450 { 451 return CONTAINING_RECORD(iface, StgStreamImpl, IStream_iface); 452 } 453 454 /* 455 * Method definition for the StgStreamImpl class. 456 */ 457 StgStreamImpl* StgStreamImpl_Construct( 458 StorageBaseImpl* parentStorage, 459 DWORD grfMode, 460 DirRef dirEntry) DECLSPEC_HIDDEN; 461 462 463 /* Range lock constants. 464 * 465 * The storage format reserves the region from 0x7fffff00-0x7fffffff for 466 * locking and synchronization. Because it reserves the entire block containing 467 * that range, and the minimum block size is 512 bytes, 0x7ffffe00-0x7ffffeff 468 * also cannot be used for any other purpose. 469 * Unfortunately, the spec doesn't say which bytes 470 * within that range are used, and for what. These are guesses based on testing. 471 * In particular, ends of ranges may be wrong. 472 473 0x0 through 0x57: Unknown. Causes read-only exclusive opens to fail. 474 0x58 through 0x6b: Priority mode. 475 0x6c through 0x7f: No snapshot mode. 476 0x80: Commit lock. 477 0x81 through 0x91: Priority mode, again. Not sure why it uses two regions. 478 0x92: Lock-checking lock. Held while opening so ranges can be tested without 479 causing spurious failures if others try to grab or test those ranges at the 480 same time. 481 0x93 through 0xa6: Read mode. 482 0xa7 through 0xba: Write mode. 483 0xbb through 0xce: Deny read. 484 0xcf through 0xe2: Deny write. 485 0xe2 through 0xff: Unknown. Causes read-only exclusive opens to fail. 486 */ 487 488 #define RANGELOCK_UNK1_FIRST 0x7ffffe00 489 #define RANGELOCK_UNK1_LAST 0x7fffff57 490 #define RANGELOCK_PRIORITY1_FIRST 0x7fffff58 491 #define RANGELOCK_PRIORITY1_LAST 0x7fffff6b 492 #define RANGELOCK_NOSNAPSHOT_FIRST 0x7fffff6c 493 #define RANGELOCK_NOSNAPSHOT_LAST 0x7fffff7f 494 #define RANGELOCK_COMMIT 0x7fffff80 495 #define RANGELOCK_PRIORITY2_FIRST 0x7fffff81 496 #define RANGELOCK_PRIORITY2_LAST 0x7fffff91 497 #define RANGELOCK_CHECKLOCKS 0x7fffff92 498 #define RANGELOCK_READ_FIRST 0x7fffff93 499 #define RANGELOCK_READ_LAST 0x7fffffa6 500 #define RANGELOCK_WRITE_FIRST 0x7fffffa7 501 #define RANGELOCK_WRITE_LAST 0x7fffffba 502 #define RANGELOCK_DENY_READ_FIRST 0x7fffffbb 503 #define RANGELOCK_DENY_READ_LAST 0x7fffffce 504 #define RANGELOCK_DENY_WRITE_FIRST 0x7fffffcf 505 #define RANGELOCK_DENY_WRITE_LAST 0x7fffffe2 506 #define RANGELOCK_UNK2_FIRST 0x7fffffe3 507 #define RANGELOCK_UNK2_LAST 0x7fffffff 508 #define RANGELOCK_TRANSACTION_FIRST RANGELOCK_COMMIT 509 #define RANGELOCK_TRANSACTION_LAST RANGELOCK_CHECKLOCKS 510 #define RANGELOCK_FIRST RANGELOCK_UNK1_FIRST 511 #define RANGELOCK_LAST RANGELOCK_UNK2_LAST 512 513 /* internal value for LockRegion/UnlockRegion */ 514 #define WINE_LOCK_READ 0x80000000 515 516 517 /****************************************************************************** 518 * Endian conversion macros 519 */ 520 #ifdef WORDS_BIGENDIAN 521 522 #ifndef htole32 523 #define htole32(x) RtlUlongByteSwap(x) 524 #endif 525 #ifndef htole16 526 #define htole16(x) RtlUshortByteSwap(x) 527 #endif 528 #define lendian32toh(x) RtlUlongByteSwap(x) 529 #define lendian16toh(x) RtlUshortByteSwap(x) 530 531 #else 532 533 #ifndef htole32 534 #define htole32(x) (x) 535 #endif 536 #ifndef htole16 537 #define htole16(x) (x) 538 #endif 539 #define lendian32toh(x) (x) 540 #define lendian16toh(x) (x) 541 542 #endif 543 544 /****************************************************************************** 545 * The StorageUtl_ functions are miscellaneous utility functions. Most of which 546 * are abstractions used to read values from file buffers without having to 547 * worry about bit order 548 */ 549 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value) DECLSPEC_HIDDEN; 550 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value) DECLSPEC_HIDDEN; 551 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value) DECLSPEC_HIDDEN; 552 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value) DECLSPEC_HIDDEN; 553 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset, 554 ULARGE_INTEGER* value) DECLSPEC_HIDDEN; 555 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset, 556 const ULARGE_INTEGER *value) DECLSPEC_HIDDEN; 557 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value) DECLSPEC_HIDDEN; 558 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value) DECLSPEC_HIDDEN; 559 void StorageUtl_CopyDirEntryToSTATSTG(StorageBaseImpl *storage,STATSTG* destination, 560 const DirEntry* source, int statFlags) DECLSPEC_HIDDEN; 561 562 563 #endif /* __STORAGE32_H__ */ 564