From 7cbba2fb02dea310762cf8338ba0ae8195c1e2a0 Mon Sep 17 00:00:00 2001 From: Vincent Povirk Date: Tue, 20 Oct 2009 14:19:15 -0500 Subject: [PATCH] ole32: Create an abstract type for storage directory entries. This patch introduces a type called Dirent representing a reference to a directory entry in a storage file. Currently, the type consists of a top-level storage object and index. This is the only implementation that will work alongside current code that uses indexes directly. In the future (when indexes are no longer used directly), this will make it possible to create a local cache of directory entries. Additional information, such as a list of open stream objects, can then be stored in the cache. That will let us reliably implement locking semantics (since we can easily track open storage/stream objects), deletion of objects that are open, and transacted mode. --- dlls/ole32/storage32.c | 342 ++++++++++++++++++++++++++++++++++++++++++++---- dlls/ole32/storage32.h | 61 +++++++++ 2 files changed, 374 insertions(+), 29 deletions(-) diff --git a/dlls/ole32/storage32.c b/dlls/ole32/storage32.c index b9c2f29..0334cd8 100644 --- a/dlls/ole32/storage32.c +++ b/dlls/ole32/storage32.c @@ -1872,6 +1872,277 @@ static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg) } +/****************************************************************************** + * Directory entry functions + */ + +/* Read the raw data of the directory entry at a given index. + */ +static HRESULT StorageImpl_ReadRawDirent(StorageImpl* storage, ULONG index, BYTE *rawdata) +{ + ULARGE_INTEGER offsetInPropSet; + ULONG bytesRead; + + TRACE("(%p,%u)\n", storage, index); + + if (index == PROPERTY_NULL) + { + return STG_E_FILENOTFOUND; + } + + offsetInPropSet.u.HighPart = 0; + offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE; + + return BlockChainStream_ReadAt( + storage->rootBlockChain, + offsetInPropSet, + PROPSET_BLOCK_SIZE, + rawdata, + &bytesRead); +} + +/* Set the given fields in raw directory entry data. + */ +static void SetRawDirent(BYTE *rawdata, DWORD fields, const DirentData *data) +{ + if ((fields & DIRENT_SET_NAME)) + { + memcpy( + rawdata + OFFSET_PS_NAME, + data->name, + PROPERTY_NAME_BUFFER_LEN ); + + StorageUtl_WriteWord( + rawdata, + OFFSET_PS_NAMELENGTH, + data->sizeOfNameString); + } + + if ((fields & DIRENT_SET_TYPE)) + memcpy(rawdata + OFFSET_PS_PROPERTYTYPE, &data->direntType, 1); + + if ((fields & DIRENT_SET_LEFTCHILD)) + StorageUtl_WriteDWord( + rawdata, + OFFSET_PS_PREVIOUSPROP, + data->leftChild.index); + + if ((fields & DIRENT_SET_RIGHTCHILD)) + StorageUtl_WriteDWord( + rawdata, + OFFSET_PS_NEXTPROP, + data->rightChild.index); + + if ((fields & DIRENT_SET_DIRROOT)) + StorageUtl_WriteDWord( + rawdata, + OFFSET_PS_DIRPROP, + data->dirRoot.index); + + if ((fields & DIRENT_SET_GUID)) + StorageUtl_WriteGUID( + rawdata, + OFFSET_PS_GUID, + &data->guid); + + if ((fields & DIRENT_SET_CTIME)) + { + StorageUtl_WriteDWord( + rawdata, + OFFSET_PS_TSS1, + data->ctime.dwLowDateTime); + + StorageUtl_WriteDWord( + rawdata, + OFFSET_PS_TSD1, + data->ctime.dwHighDateTime); + } + + if ((fields & DIRENT_SET_MTIME)) + { + StorageUtl_WriteDWord( + rawdata, + OFFSET_PS_TSS2, + data->mtime.dwLowDateTime); + + StorageUtl_WriteDWord( + rawdata, + OFFSET_PS_TSD2, + data->mtime.dwHighDateTime); + } + + if ((fields & DIRENT_SET_STARTINGBLOCK)) + StorageUtl_WriteDWord( + rawdata, + OFFSET_PS_STARTBLOCK, + data->startingBlock); + + if ((fields & DIRENT_SET_SIZE)) + StorageUtl_WriteDWord( + rawdata, + OFFSET_PS_SIZE, + data->size.u.LowPart); + + /* FIXME: Setting all nodes to black is acceptable according to the MS spec, + * but maybe we should treat the tree as red/black? */ + rawdata[OFFSET_PS_COLOR] = PS_COLOR_BLACK; +} + +/* Write the raw data of the directory entry at a given index. + */ +static HRESULT StorageImpl_WriteRawDirent(StorageImpl* storage, ULONG index, BYTE *rawdata) +{ + ULARGE_INTEGER offsetInPropSet; + ULONG bytesWritten; + + TRACE("(%p,%u) %s\n", storage, index, debugstr_w((WCHAR*)rawdata)); + + if (index == PROPERTY_NULL) + { + return STG_E_FILENOTFOUND; + } + + offsetInPropSet.u.HighPart = 0; + offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE; + + return BlockChainStream_WriteAt( + storage->rootBlockChain, + offsetInPropSet, + PROPSET_BLOCK_SIZE, + rawdata, + &bytesWritten); +} + +/* Get the index from a Dirent. + * + * This function is needed for compatibility with current code but should be + * removed so that a Dirent does not need to have an index. + */ +ULONG Dirent_GetIndex(const Dirent *dirent) +{ + return dirent->index; +} + +/* Create a new directory in the same storage (or transacted substorage) as an + * existing directory entry, initializing it with the given data. + * + * data->sizeOfNameString must be non-zero because this is how blocks are + * reserved in the file. + * + * If this function returns successfully, the new directory entry is orphaned. + * To avoid leaking some memory and/or a slot in the file, a successful Create + * call must be followed by a successful call to Dirent_InsertIntoTree or + * Dirent_Destroy. + */ +HRESULT Dirent_Create(Dirent *dirent, const Dirent *existing, DirentData *data) +{ + BYTE currentProperty[PROPSET_BLOCK_SIZE]; + ULONG index=0; + WORD sizeOfNameString; + HRESULT hr=S_OK; + + TRACE("%s\n", debugstr_w(data->name)); + + /* Find a free property in the file. */ + while (1) + { + hr = StorageImpl_ReadRawDirent(existing->storage, + index, + currentProperty); + if (SUCCEEDED(hr)) + { + StorageUtl_ReadWord( + currentProperty, + OFFSET_PS_NAMELENGTH, + &sizeOfNameString); + + if (sizeOfNameString == 0) + break; + } + else + break; + + index++; + } + + if (SUCCEEDED(hr)) + { + TRACE("using index %u\n", index); + + memset(currentProperty, 0, PROPSET_BLOCK_SIZE); + SetRawDirent(currentProperty, DIRENT_SET_ALL, data); + + hr = StorageImpl_WriteRawDirent(existing->storage, index, currentProperty); + } + else if (hr == STG_E_READFAULT) + { + ULARGE_INTEGER newSize; + ULONG zeroedIndex; + ULONG lastIndex = 0; + ULONG blockCount = 0; + + TRACE("growing directory stream and using index %u\n", index); + + memset(currentProperty, 0, PROPSET_BLOCK_SIZE); + SetRawDirent(currentProperty, DIRENT_SET_ALL, data); + + /* obtain the new count of directory blocks */ + blockCount = BlockChainStream_GetCount( + existing->storage->rootBlockChain)+1; + + /* initialize the size used by the directory stream */ + newSize.u.HighPart = 0; + newSize.u.LowPart = existing->storage->bigBlockSize * blockCount; + + /* add a block to the directory stream */ + BlockChainStream_SetSize(existing->storage->rootBlockChain, newSize); + + /* initialize the first directory entry with the new data */ + hr = StorageImpl_WriteRawDirent(existing->storage, index, currentProperty); + + /* zero the other entries */ + if (SUCCEEDED(hr)) + { + memset(currentProperty, 0, PROPSET_BLOCK_SIZE); + + lastIndex = existing->storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount; + + for (zeroedIndex=index+1; zeroedIndex < lastIndex; zeroedIndex++) + /* Failures here aren't fatal. The worst that can happen is we have some + * orphaned entries in the file. */ + StorageImpl_WriteRawDirent(existing->storage, zeroedIndex, currentProperty); + } + } + + if (SUCCEEDED(hr)) + { + dirent->storage = existing->storage; + dirent->index = index; + } + + return hr; +} + +/* Initialize a Dirent from a top-level Storage and index. + */ +HRESULT Dirent_InitFromIndex(Dirent *dirent, StorageImpl *storage, ULONG index) +{ + dirent->storage = storage; + dirent->index = index; + return S_OK; +} + +BOOL Dirent_IsNull(const Dirent *dirent) +{ + return (dirent->index == PROPERTY_NULL); +} + +void Dirent_SetNull(Dirent *dirent) +{ + dirent->index = PROPERTY_NULL; +} + + /********************************************************************* * * Internal Method @@ -2312,8 +2583,6 @@ static HRESULT StorageImpl_Construct( BOOL create) { HRESULT hr = S_OK; - StgProperty currentProperty; - BOOL readSuccessful; ULONG currentPropertyIndex; if ( FAILED( validateSTGM(openFlags) )) @@ -2442,7 +2711,7 @@ static HRESULT StorageImpl_Construct( */ if (create) { - StgProperty rootProp; + DirentData rootProp; /* * Initialize the property chain */ @@ -2450,48 +2719,61 @@ static HRESULT StorageImpl_Construct( MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name, sizeof(rootProp.name)/sizeof(WCHAR) ); rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR); - rootProp.propertyType = PROPTYPE_ROOT; - rootProp.previousProperty = PROPERTY_NULL; - rootProp.nextProperty = PROPERTY_NULL; - rootProp.dirProperty = PROPERTY_NULL; + rootProp.direntType = PROPTYPE_ROOT; + Dirent_SetNull(&rootProp.leftChild); + Dirent_SetNull(&rootProp.rightChild); + Dirent_SetNull(&rootProp.dirRoot); rootProp.startingBlock = BLOCK_END_OF_CHAIN; rootProp.size.u.HighPart = 0; rootProp.size.u.LowPart = 0; - StorageImpl_WriteProperty(This, 0, &rootProp); - } - - /* - * Find the ID of the root in the property sets. - */ - currentPropertyIndex = 0; + /* Dirent_Create normally requires an existing dirent. Currently, only the + * storage needs to be set on the existing dirent, so we set that first. */ + This->base.dirent.storage = This; - do + hr = Dirent_Create(&This->base.dirent, &This->base.dirent, &rootProp); + } + else { - readSuccessful = StorageImpl_ReadProperty( - This, - currentPropertyIndex, - ¤tProperty); + BYTE direntRawData[PROPSET_BLOCK_SIZE]; + WORD sizeOfNameString; - if (readSuccessful) + /* + * Find the ID of the root directory entry. + */ + currentPropertyIndex = 0; + + do { - if ( (currentProperty.sizeOfNameString != 0 ) && - (currentProperty.propertyType == PROPTYPE_ROOT) ) + hr = StorageImpl_ReadRawDirent(This, currentPropertyIndex, direntRawData); + + if (SUCCEEDED(hr)) { - This->base.rootPropertySetIndex = currentPropertyIndex; - } - } + StorageUtl_ReadWord( + direntRawData, + OFFSET_PS_NAMELENGTH, + &sizeOfNameString); - currentPropertyIndex++; + if (sizeOfNameString != 0 && + direntRawData[OFFSET_PS_PROPERTYTYPE] == PROPTYPE_ROOT) + { + hr = Dirent_InitFromIndex(&This->base.dirent, This, currentPropertyIndex); + break; + } - } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) ); + currentPropertyIndex++; + } + } while (SUCCEEDED(hr)); + } - if (!readSuccessful) + if (FAILED(hr)) { /* TODO CLEANUP */ - return STG_E_READFAULT; + return hr; } + This->base.rootPropertySetIndex = Dirent_GetIndex(&This->base.dirent); + /* * Create the block chain abstraction for the small block root chain. */ @@ -4210,6 +4492,8 @@ static StorageInternalImpl* StorageInternalImpl_Construct( */ newStorage->base.rootPropertySetIndex = rootPropertyIndex; + Dirent_InitFromIndex(&newStorage->base.dirent, ancestorStorage, rootPropertyIndex); + return newStorage; } diff --git a/dlls/ole32/storage32.h b/dlls/ole32/storage32.h index fc4af32..bd1e17f 100644 --- a/dlls/ole32/storage32.h +++ b/dlls/ole32/storage32.h @@ -54,6 +54,7 @@ static const ULONG OFFSET_BBDEPOTSTART = 0x0000004C; static const ULONG OFFSET_PS_NAME = 0x00000000; static const ULONG OFFSET_PS_NAMELENGTH = 0x00000040; static const ULONG OFFSET_PS_PROPERTYTYPE = 0x00000042; +static const ULONG OFFSET_PS_COLOR = 0x00000043; static const ULONG OFFSET_PS_PREVIOUSPROP = 0x00000044; static const ULONG OFFSET_PS_NEXTPROP = 0x00000048; static const ULONG OFFSET_PS_DIRPROP = 0x0000004C; @@ -93,6 +94,10 @@ static const ULONG PROPERTY_NULL = 0xFFFFFFFF; #define PROPTYPE_STREAM 0x02 #define PROPTYPE_ROOT 0x05 +/* Color constants */ +#define PS_COLOR_RED 0 +#define PS_COLOR_BLACK 1 + /* * These defines assume a hardcoded blocksize. The code will assert * if the blocksize is different. Some changes will have to be done if it @@ -144,6 +149,57 @@ struct StgProperty ULARGE_INTEGER size; }; +/* Dirent is an "opaque" type representing a directory entry in the file. Most + * of the code should store these instead of the integer index of the directory + * entry and access them using functions that perform high-level operations on + * them. + */ +typedef struct Dirent +{ + StorageImpl *storage; + ULONG index; +} Dirent; + +/* A high-level structure representing the data stored in a Dirent */ +typedef struct DirentData +{ + WCHAR name[PROPERTY_NAME_MAX_LEN]; + WORD sizeOfNameString; + BYTE direntType; + Dirent leftChild; + Dirent rightChild; + Dirent dirRoot; + GUID guid; + FILETIME ctime; + FILETIME mtime; + ULONG startingBlock; + ULARGE_INTEGER size; +} DirentData; + +/* The low-level Dirent operations. */ +HRESULT Dirent_Create(Dirent *dirent, const Dirent *existing, DirentData *data); +HRESULT Dirent_GetData(const Dirent *dirent, DirentData *data); +HRESULT Dirent_SetData(const Dirent *dirent, DWORD fields, const DirentData *data); +HRESULT Dirent_Destroy(const Dirent *dirent); +HRESULT Dirent_InitFromIndex(Dirent *dirent, StorageImpl *storage, ULONG index); + +/* Flags for Dirent_SetData */ +#define DIRENT_SET_NAME 0x1 +#define DIRENT_SET_TYPE 0x2 +#define DIRENT_SET_LEFTCHILD 0x4 +#define DIRENT_SET_RIGHTCHILD 0x8 +#define DIRENT_SET_DIRROOT 0x10 +#define DIRENT_SET_GUID 0x20 +#define DIRENT_SET_CTIME 0x40 +#define DIRENT_SET_MTIME 0x80 +#define DIRENT_SET_STARTINGBLOCK 0x100 +#define DIRENT_SET_SIZE 0x200 +#define DIRENT_SET_ALL 0x3ff + +/* Transitional function needed while we still use indexes directly. */ +ULONG Dirent_GetIndex(const Dirent *dirent); + + /************************************************************************* * Big Block File support * @@ -213,9 +269,14 @@ struct StorageBaseImpl /* * Index of the property for the root of * this storage + * + * FIXME: This should be replaced by dirent. */ ULONG rootPropertySetIndex; + /* Dirent for the directory entry of this storage. */ + Dirent dirent; + /* * virtual Destructor method. */ -- 1.5.4.3