windows-nt/Source/XPSP1/NT/shell/shlwapi/datablok.cpp
2020-09-26 16:20:57 +08:00

346 lines
10 KiB
C++

#include "priv.h"
#include "stream.h"
#define _DBLNext(pdbList) ((LPDBLIST)(((LPBYTE)(pdbList)) + (pdbList)->cbSize ))
#define DBSIG_WRAP ((DWORD)-1)
STDAPI SHWriteDataBlockList(IStream* pstm, LPDBLIST pdbList)
{
HRESULT hr = S_OK;
if (pdbList)
{
for ( ; pdbList->cbSize; pdbList = _DBLNext(pdbList))
{
LPDATABLOCK_HEADER pdb;
ULONG cbBytes;
pdb = pdbList;
if (DBSIG_WRAP == pdb->dwSignature)
pdb++;
TraceMsg(TF_DBLIST, "Writing extra data block, size:%x sig:%x", pdb->cbSize, pdb->dwSignature);
if (FAILED(hr = ((CMemStream*)pstm)->Write((LPBYTE)pdb, pdb->cbSize, &cbBytes)))
break;
if (cbBytes != pdb->cbSize)
{
hr = STG_E_MEDIUMFULL;
break;
}
}
}
// NULL terminate the list
if (SUCCEEDED(hr))
{
DWORD dwData = 0;
DWORD cbBytes;
hr = ((CMemStream*)pstm)->Write(&dwData, sizeof(dwData), &cbBytes);
}
return(hr);
}
STDAPI SHReadDataBlockList(IStream* pstm, LPDBLIST * ppdbList)
{
HRESULT hr;
BYTE buf[200]; // all blocks today fit in this size (tested at 5)
LPDATABLOCK_HEADER lpBuf = (LPDATABLOCK_HEADER)buf;
DWORD cbBuf = sizeof(buf);
DWORD dwSizeToRead, cbBytes;
if (*ppdbList)
{
LocalFree((HLOCAL)(*ppdbList));
*ppdbList = NULL;
}
while (TRUE)
{
DWORD cbSize;
dwSizeToRead = sizeof(cbSize);
hr = ((CMemStream*)pstm)->Read(&cbSize, dwSizeToRead, &cbBytes);
if (SUCCEEDED(hr) && (cbBytes == dwSizeToRead))
{
// Windows 95 and NT 4 shipped a CShellLink that did NOT
// NULL terminate the data it wrote out to the stream.
// If more data was persisted after the CShellLink then
// we will read in garbage. No real harm comes of this (*)
// (because it is unlikely we'll get a dwSignature match)
// but if the first dword is huge, we'll allocate a ton
// of memory and page it in. This can take MINUTES on Win95.
// Assume anything over 64K is from one of these
// bogus streams.
//
// (*) actually, real harm comes because we don't leave the
// stream in the correct place. Forms^3 put a work-around
// in for this bug.
//
if (cbSize > 0x0000FFFF)
{
ULARGE_INTEGER liStart;
LARGE_INTEGER liMove;
// We read a DWORD of data that wasn't ours, back up.
// NOTE: all of our stream implementations assume
// HighPart == 0
//
liMove.HighPart = liMove.LowPart = 0;
if (SUCCEEDED(((CMemStream*)pstm)->Seek(liMove, STREAM_SEEK_CUR, &liStart)))
{
ASSERT(liStart.HighPart == 0);
ASSERT(liStart.LowPart >= sizeof(cbSize));
liMove.LowPart = liStart.LowPart - sizeof(cbSize);
((CMemStream*)pstm)->Seek(liMove, STREAM_SEEK_SET, NULL);
}
TraceMsg(TF_DBLIST, "ASSUMING NO NULL TERMINATION (FOR SIZE 0x%x)", cbSize);
cbSize = 0;
}
// If we hit the 0 terminator, we're done.
//
if (cbSize < sizeof(DATABLOCK_HEADER))
break;
// Make sure we can read this block in.
//
if (cbSize > cbBuf)
{
HLOCAL pTemp;
if (lpBuf == (LPDATABLOCK_HEADER)buf)
pTemp = LocalAlloc(LPTR, cbSize);
else
pTemp = LocalReAlloc((HLOCAL)lpBuf, cbSize, LMEM_ZEROINIT | LMEM_MOVEABLE);
if (pTemp)
{
lpBuf = (LPDATABLOCK_HEADER)pTemp;
cbBuf = cbSize;
}
else
{
hr = E_OUTOFMEMORY;
break;
}
}
// Read in data block
//
lpBuf->cbSize = cbSize;
dwSizeToRead = cbSize - sizeof(cbSize);
hr = ((CMemStream*)pstm)->Read((LPBYTE)&(lpBuf->dwSignature), dwSizeToRead, &cbBytes);
if (SUCCEEDED(hr) && (cbBytes == dwSizeToRead))
{
TraceMsg(TF_DBLIST, "Reading extra data block, size:%x sig:%x", lpBuf->cbSize, lpBuf->dwSignature);
SHAddDataBlock(ppdbList, lpBuf);
}
else
break;
}
else
break;
}
// Free any allocated buffer
//
if (lpBuf != (LPDATABLOCK_HEADER)buf)
{
LocalFree((HLOCAL)lpBuf);
}
return hr;
}
STDAPI_(void) SHFreeDataBlockList(LPDBLIST pdbList)
{
if (pdbList)
{
LocalFree((HLOCAL)pdbList);
}
}
STDAPI_(BOOL) SHAddDataBlock(LPDBLIST * ppdbList, LPDATABLOCK_HEADER pdb)
{
LPDBLIST pdbCopyTo = NULL;
DWORD dwSize;
// Don't let anyone use our special signature
//
if (DBSIG_WRAP == pdb->dwSignature ||
pdb->cbSize < sizeof(*pdb))
{
TraceMsg(TF_DBLIST, "SHAddDataBlock invalid datablock! (sig:%x size:%x)", pdb->dwSignature, pdb->cbSize);
return FALSE;
}
// Figure out how much space we need to hold this block
//
dwSize = pdb->cbSize;
if (pdb->cbSize & 0x3)
{
dwSize = ((dwSize + 3) & ~0x3) + sizeof(DATABLOCK_HEADER);
TraceMsg(TF_DBLIST, "Adding non-DWORD data block, size:%x sig:%x", pdb->cbSize, pdb->dwSignature);
}
else
{
TraceMsg(TF_DBLIST, "Adding data block, size:%x sig:%x", pdb->cbSize, pdb->dwSignature);
}
// Allocate the space
//
if (!*ppdbList)
{
*ppdbList = (LPDBLIST)LocalAlloc(LPTR, dwSize + sizeof(DWORD)); // include NULL terminator
pdbCopyTo = *ppdbList;
}
else
{
DWORD dwTotalSize = 0;
LPDBLIST pdbList;
HLOCAL lpTmp;
for (pdbList = *ppdbList ; pdbList->cbSize ; pdbList = _DBLNext(pdbList))
dwTotalSize += pdbList->cbSize;
lpTmp = LocalReAlloc((HLOCAL)*ppdbList, dwTotalSize + dwSize + sizeof(DWORD), // include NULL terminator
LMEM_ZEROINIT | LMEM_MOVEABLE);
if (lpTmp)
{
*ppdbList = (LPDBLIST)lpTmp;
pdbCopyTo = (LPDBLIST)(((LPBYTE)lpTmp) + dwTotalSize);
}
}
// Copy the data block
//
if (pdbCopyTo)
{
LPBYTE pTmp = (LPBYTE)pdbCopyTo;
// This block would cause other blocks to be
// unaligned, wrap it
//
ASSERT(0 == (dwSize & 0x3));
if (dwSize != pdb->cbSize)
{
pdbCopyTo->cbSize = dwSize;
pdbCopyTo->dwSignature = DBSIG_WRAP;
pTmp = (LPBYTE)(pdbCopyTo + 1);
}
CopyMemory(pTmp, pdb, pdb->cbSize);
// NULL terminate the list
_DBLNext(pdbCopyTo)->cbSize = 0;
return TRUE;
}
return FALSE;
}
STDAPI_(BOOL) SHRemoveDataBlock(LPDBLIST * ppdbList, DWORD dwSignature)
{
LPDBLIST pdbRemove = NULL;
// Can't call SHFindDataBlock because that returnes the
// block that was wrapped, we want the block that wraps.
//
if (*ppdbList)
{
LPDBLIST pdbList = *ppdbList;
for ( ; pdbList->cbSize ; pdbList = _DBLNext(pdbList))
{
if (dwSignature == pdbList->dwSignature)
{
TraceMsg(TF_DBLIST, "Removing data block, size:%x sig:%x ptr:%x", pdbList->cbSize, pdbList->dwSignature, pdbList);
pdbRemove = pdbList;
break;
}
else if (DBSIG_WRAP == pdbList->dwSignature)
{
LPDBLIST pdbWrap = pdbList + 1;
if (dwSignature == pdbWrap->dwSignature)
{
TraceMsg(TF_DBLIST, "Removing non-DWORD data block, size:%x sig:%x ptr:", pdbWrap->cbSize, pdbWrap->dwSignature, pdbWrap);
pdbRemove = pdbList;
break;
}
}
}
}
if (pdbRemove)
{
LPDBLIST pdbNext = _DBLNext(pdbRemove);
LPDBLIST pdbEnd;
DWORD dwSizeOfBlockToRemove;
LONG lNewSize;
for (pdbEnd = pdbNext ; pdbEnd->cbSize ; pdbEnd = _DBLNext(pdbEnd))
;
dwSizeOfBlockToRemove = pdbRemove->cbSize;
// Move remaining memory down
MoveMemory(pdbRemove, pdbNext, (DWORD_PTR)pdbEnd - (DWORD_PTR)pdbNext + sizeof(DWORD));
// Shrink our buffer
lNewSize = (LONG) LocalSize(*ppdbList ) - dwSizeOfBlockToRemove;
if (lNewSize > sizeof(DWORD))
{
void *lpVoid = LocalReAlloc( (HLOCAL)*ppdbList, lNewSize, LMEM_ZEROINIT | LMEM_MOVEABLE );
if (NULL != lpVoid)
{
*ppdbList = (LPDBLIST)lpVoid;
}
}
else
{
// We've removed the last section, delete the whole deal
LocalFree( (HLOCAL)(*ppdbList) );
*ppdbList = NULL;
}
return TRUE;
}
return FALSE;
}
STDAPI_(void *) SHFindDataBlock(LPDBLIST pdbList, DWORD dwSignature)
{
if (pdbList)
{
for ( ; pdbList->cbSize ; pdbList = _DBLNext(pdbList))
{
if (dwSignature == pdbList->dwSignature)
{
TraceMsg(TF_DBLIST, "Found data block, size:%x sig:%x ptr:%x", pdbList->cbSize, pdbList->dwSignature, pdbList);
return (void *)pdbList;
}
else if (DBSIG_WRAP == pdbList->dwSignature)
{
LPDBLIST pdbWrap = pdbList + 1;
if (dwSignature == pdbWrap->dwSignature)
{
TraceMsg(TF_DBLIST, "Found non-DWORD data block, size:%x sig:%x ptr:%x", pdbWrap->cbSize, pdbWrap->dwSignature, pdbWrap);
return (void *)pdbWrap;
}
}
}
}
return NULL;
}