You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1435 lines
36 KiB
1435 lines
36 KiB
/******************************************************************************
|
|
*
|
|
* Copyright (c) 1999 Microsoft Corporation
|
|
*
|
|
* Module Name:
|
|
* reslist.cpp
|
|
*
|
|
* Abstract:
|
|
* This file contains the implementation of Restore List.
|
|
*
|
|
* Revision History:
|
|
* Brijesh Krishnaswami (brijeshk) 06/02/00
|
|
* created
|
|
*
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include "restmap.h"
|
|
#include "reslist.h"
|
|
#include "shlwapi.h"
|
|
#include "utils.h"
|
|
|
|
#ifdef THIS_FILE
|
|
#undef THIS_FILE
|
|
#endif
|
|
|
|
static char __szTraceSourceFile[] = __FILE__;
|
|
#define THIS_FILE __szTraceSourceFile
|
|
|
|
|
|
#include "dbgtrace.h"
|
|
|
|
|
|
// copy acl
|
|
|
|
void
|
|
CopyAcl(CNode *pNode, BYTE *pbAcl, DWORD cbAcl, BOOL fAclInline)
|
|
{
|
|
if (pbAcl)
|
|
{
|
|
HEAP_FREE(pNode->m_pbAcl);
|
|
pNode->m_pbAcl = (BYTE *) HEAP_ALLOC(cbAcl);
|
|
if (pNode->m_pbAcl)
|
|
memcpy(pNode->m_pbAcl, pbAcl, cbAcl);
|
|
pNode->m_cbAcl = cbAcl;
|
|
pNode->m_fAclInline = fAclInline;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// GetReverseOperation : This function will return the reverse operation of the
|
|
// current changelog operation.
|
|
//
|
|
|
|
DWORD
|
|
GetReverseOperation(
|
|
DWORD dwOpr
|
|
)
|
|
{
|
|
DWORD dwRestoreOpr = OPR_UNKNOWN;
|
|
|
|
switch( dwOpr )
|
|
{
|
|
case OPR_FILE_DELETE:
|
|
dwRestoreOpr = OPR_FILE_ADD;
|
|
break;
|
|
|
|
case OPR_FILE_ADD :
|
|
dwRestoreOpr = OPR_FILE_DELETE;
|
|
break;
|
|
|
|
case OPR_FILE_MODIFY:
|
|
dwRestoreOpr = OPR_FILE_MODIFY;
|
|
break;
|
|
|
|
case OPR_SETATTRIB:
|
|
dwRestoreOpr = OPR_SETATTRIB;
|
|
break;
|
|
|
|
case OPR_FILE_RENAME:
|
|
dwRestoreOpr = OPR_FILE_RENAME;
|
|
break;
|
|
|
|
case OPR_DIR_RENAME:
|
|
dwRestoreOpr = OPR_DIR_RENAME;
|
|
break;
|
|
|
|
case OPR_DIR_DELETE:
|
|
dwRestoreOpr = OPR_DIR_CREATE;
|
|
break;
|
|
|
|
case OPR_DIR_CREATE:
|
|
dwRestoreOpr = OPR_DIR_DELETE;
|
|
break;
|
|
|
|
case OPR_SETACL:
|
|
dwRestoreOpr = OPR_SETACL;
|
|
break;
|
|
|
|
default:
|
|
dwRestoreOpr = OPR_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
return dwRestoreOpr;
|
|
}
|
|
|
|
|
|
//
|
|
// AllocateNode : allocates a list node
|
|
//
|
|
|
|
CNode *
|
|
AllocateNode(
|
|
LPWSTR pPath1,
|
|
LPWSTR pPath2)
|
|
{
|
|
CNode * pNode = NULL;
|
|
BOOL fRet = FALSE;
|
|
|
|
if (! pPath1)
|
|
{
|
|
fRet = TRUE; // BUGBUG - is this a valid case?
|
|
goto Exit;
|
|
}
|
|
|
|
pNode = (CNode *) HEAP_ALLOC( sizeof(CNode) );
|
|
if (! pNode )
|
|
goto Exit;
|
|
|
|
// all pointers set to NULL
|
|
|
|
if ( pPath1 )
|
|
STRCOPY(pNode->m_pPath1, pPath1);
|
|
|
|
if ( pPath2 )
|
|
STRCOPY(pNode->m_pPath2, pPath2);
|
|
|
|
pNode->m_dwOperation = OPR_UNKNOWN;
|
|
pNode->m_dwAttributes = 0xFFFFFFFF;
|
|
pNode->m_pPrev = NULL;
|
|
pNode->m_pNext = NULL;
|
|
|
|
fRet = TRUE;
|
|
|
|
Exit:
|
|
if (! fRet)
|
|
{
|
|
// something failed, so cleanup
|
|
|
|
FreeNode(pNode);
|
|
pNode = NULL;
|
|
}
|
|
|
|
return pNode;
|
|
}
|
|
|
|
|
|
//
|
|
// FreeNode : This function frees a list node
|
|
//
|
|
|
|
VOID
|
|
FreeNode(
|
|
CNode * pNode
|
|
)
|
|
{
|
|
if ( pNode )
|
|
{
|
|
HEAP_FREE(pNode->m_pPath1);
|
|
HEAP_FREE(pNode->m_pPath2);
|
|
HEAP_FREE(pNode->m_pszTemp);
|
|
HEAP_FREE(pNode->m_pbAcl);
|
|
HEAP_FREE(pNode);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Looks at an existing node and the new operation coming and decides
|
|
// if they can be merged or not.
|
|
//
|
|
|
|
BOOL
|
|
CanMerge(
|
|
DWORD dwExistingOpr,
|
|
DWORD dwNewOpr,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
//
|
|
// Don't optimize directory renames
|
|
//
|
|
|
|
if ( OPR_DIR_RENAME == dwExistingOpr ||
|
|
OPR_DIR_RENAME == dwNewOpr )
|
|
goto Exit;
|
|
|
|
//
|
|
// Don't optimize for rename if the node we find is a delete node
|
|
// and the current opr is rename. In such cases since rename is
|
|
// dependent on deletion to succeed.
|
|
// Example : A -> B , Create A should generate Delete A, B -> A
|
|
//
|
|
|
|
if ( IsRename(dwNewOpr) &&
|
|
( OPR_DIR_DELETE == dwExistingOpr || OPR_FILE_DELETE == dwExistingOpr )
|
|
)
|
|
goto Exit;
|
|
|
|
//
|
|
// Also Del A, A->B should not merge this can happen if the file is
|
|
// getting renamed out of the directory and back.
|
|
//
|
|
|
|
if ( IsRename(dwExistingOpr) &&
|
|
( OPR_DIR_DELETE == dwNewOpr ||
|
|
OPR_DIR_CREATE == dwNewOpr ||
|
|
OPR_FILE_DELETE == dwNewOpr )
|
|
)
|
|
goto Exit;
|
|
|
|
|
|
if ( IsRename(dwNewOpr) &&
|
|
( OPR_DIR_DELETE == dwExistingOpr ||
|
|
OPR_DIR_CREATE == dwExistingOpr )
|
|
)
|
|
goto Exit;
|
|
|
|
//
|
|
// Dir + File operations don't merge
|
|
//
|
|
|
|
if ( ( OPR_FILE_DELETE == dwExistingOpr ||
|
|
OPR_FILE_MODIFY == dwExistingOpr ||
|
|
OPR_FILE_ADD == dwExistingOpr ) &&
|
|
( OPR_DIR_CREATE == dwNewOpr ||
|
|
OPR_DIR_DELETE == dwNewOpr )
|
|
)
|
|
goto Exit;
|
|
|
|
|
|
if ( ( OPR_FILE_DELETE == dwNewOpr ||
|
|
OPR_FILE_ADD == dwNewOpr ||
|
|
OPR_FILE_MODIFY == dwNewOpr ) &&
|
|
( OPR_DIR_CREATE == dwExistingOpr ||
|
|
OPR_DIR_DELETE == dwExistingOpr )
|
|
)
|
|
goto Exit;
|
|
|
|
|
|
// can merge
|
|
|
|
fRet = TRUE;
|
|
|
|
Exit:
|
|
return fRet;
|
|
}
|
|
|
|
|
|
//
|
|
// CRestoreList implementation.
|
|
//
|
|
|
|
CRestoreList::CRestoreList()
|
|
{
|
|
m_pListHead = m_pListTail = NULL;
|
|
}
|
|
|
|
|
|
CRestoreList::~CRestoreList()
|
|
{
|
|
//
|
|
// Destroy the list
|
|
//
|
|
|
|
CNode * pNodeNext = m_pListHead;
|
|
|
|
CNode * pNode = NULL;
|
|
while( pNodeNext )
|
|
{
|
|
pNode = pNodeNext;
|
|
pNodeNext = pNodeNext->m_pNext;
|
|
FreeNode( pNode );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// AddNode : Alocates and Adds a tree node to the restore tree.
|
|
//
|
|
|
|
CNode *
|
|
CRestoreList::AppendNode(
|
|
LPWSTR pPath1,
|
|
LPWSTR pPath2)
|
|
{
|
|
CNode * pNode = NULL;
|
|
|
|
if ( pPath1 )
|
|
|
|
{
|
|
pNode = AllocateNode( pPath1, pPath2 );
|
|
|
|
if ( !pNode )
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( m_pListHead )
|
|
{
|
|
CNode * pPrevNode = m_pListTail;
|
|
pPrevNode->m_pNext = pNode;
|
|
pNode->m_pPrev = pPrevNode;
|
|
pNode->m_pNext = NULL;
|
|
m_pListTail = pNode;
|
|
}
|
|
else
|
|
{
|
|
m_pListHead = pNode;
|
|
m_pListTail = pNode;
|
|
pNode->m_pPrev = NULL;
|
|
pNode->m_pNext = NULL;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
return pNode;
|
|
}
|
|
|
|
//
|
|
// RemoveNode: Removes a node from the list
|
|
//
|
|
|
|
CNode *
|
|
CRestoreList::RemoveNode(
|
|
CNode * pNode
|
|
)
|
|
{
|
|
if ( pNode )
|
|
{
|
|
CNode * pPrevNode = pNode->m_pPrev;
|
|
CNode * pNextNode = pNode->m_pNext;
|
|
|
|
if (pPrevNode)
|
|
{
|
|
pPrevNode->m_pNext = pNode->m_pNext;
|
|
}
|
|
|
|
if (pNextNode)
|
|
{
|
|
pNextNode->m_pPrev = pNode->m_pPrev;
|
|
}
|
|
|
|
if ( pNode == m_pListHead )
|
|
{
|
|
m_pListHead = pNextNode;
|
|
}
|
|
|
|
if ( pNode == m_pListTail )
|
|
{
|
|
m_pListTail = pPrevNode;
|
|
}
|
|
|
|
pNode->m_pPrev = NULL;
|
|
pNode->m_pNext = NULL;
|
|
}
|
|
|
|
|
|
return pNode;
|
|
}
|
|
|
|
|
|
//
|
|
// GetLastNode : finds the most recent candidate to merge
|
|
//
|
|
CNode *
|
|
CRestoreList::GetLastNode(
|
|
LPWSTR pPath1,
|
|
LPWSTR pPath2,
|
|
BOOL fFailOnPrefixMatch
|
|
)
|
|
{
|
|
CNode * pNode = NULL;
|
|
|
|
pNode = m_pListTail;
|
|
|
|
while( pNode )
|
|
{
|
|
//
|
|
// If the node has invalid operation skip it.
|
|
//
|
|
|
|
if ( OPR_UNKNOWN == pNode->m_dwOperation )
|
|
{
|
|
pNode = pNode->m_pPrev;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If node matches, return it
|
|
//
|
|
|
|
if (0 == lstrcmpi(pPath1, pNode->m_pPath1))
|
|
goto Exit;
|
|
|
|
//
|
|
// Check for a dependent rename operation, if found we should not
|
|
// merge beyond it.
|
|
//
|
|
|
|
if ( pPath1 &&
|
|
IsRename(pNode->m_dwOperation) &&
|
|
0 == lstrcmpi(pPath1, pNode->m_pPath2) )
|
|
{
|
|
pNode = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Check for swap conditions, if so don't do any optimization.
|
|
//
|
|
|
|
if ( pPath2 &&
|
|
IsRename(pNode->m_dwOperation) &&
|
|
0 == lstrcmpi(pPath2, pNode->m_pPath2) )
|
|
{
|
|
pNode = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Check if the entire path matches as a prefix, in such a
|
|
// case we should fail search because an operation under that
|
|
// directory is found.
|
|
//
|
|
|
|
if ( fFailOnPrefixMatch )
|
|
{
|
|
//
|
|
// Check if entire path matches a prefix that means this
|
|
// directory has other operations so don't merge.
|
|
//
|
|
|
|
if (StrStrI(pNode->m_pPath1, pPath1))
|
|
{
|
|
pNode = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
if ( IsRename(pNode->m_dwOperation) &&
|
|
StrStrI(pNode->m_pPath2, pPath1))
|
|
{
|
|
pNode = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Check if prefix of the path matches with the full path in
|
|
// nodelist, means we are moving across directory's lifespan.
|
|
// ToDo: Only check for directory life operations
|
|
//
|
|
|
|
if ( IsRename(pNode->m_dwOperation) ||
|
|
OPR_DIR_DELETE == pNode->m_dwOperation ||
|
|
OPR_DIR_CREATE == pNode->m_dwOperation )
|
|
{
|
|
if ( StrStrI(pPath1, pNode->m_pPath1) )
|
|
{
|
|
pNode = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
if ( IsRename(pNode->m_dwOperation) &&
|
|
StrStrI(pPath1, pNode->m_pPath2) )
|
|
{
|
|
pNode = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
if ( pPath2 )
|
|
{
|
|
if ( StrStrI(pPath2, pNode->m_pPath1) )
|
|
{
|
|
pNode = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
if ( IsRename(pNode->m_dwOperation) &&
|
|
StrStrI(pPath2, pNode->m_pPath2) )
|
|
{
|
|
pNode = NULL;
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pNode = pNode->m_pPrev;
|
|
}
|
|
|
|
Exit:
|
|
|
|
//
|
|
// We have found a potential merge candidate, now check if this is
|
|
// a rename node and dest prefix has any operations on it.
|
|
//
|
|
|
|
if ( pNode && IsRename(pNode->m_dwOperation))
|
|
{
|
|
CNode * pTmpNode = m_pListTail;
|
|
|
|
while( pTmpNode && pTmpNode != pNode )
|
|
{
|
|
if ( IsRename(pTmpNode->m_dwOperation) ||
|
|
OPR_DIR_DELETE == pTmpNode->m_dwOperation ||
|
|
OPR_DIR_CREATE == pTmpNode->m_dwOperation )
|
|
{
|
|
if ( StrStrI(pNode->m_pPath2, pTmpNode->m_pPath1) )
|
|
{
|
|
pNode = NULL;
|
|
break;
|
|
}
|
|
|
|
if ( IsRename(pTmpNode->m_dwOperation) &&
|
|
StrStrI(pNode->m_pPath2, pTmpNode->m_pPath2) )
|
|
{
|
|
pNode = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pTmpNode = pTmpNode->m_pPrev;
|
|
}
|
|
}
|
|
|
|
return pNode;
|
|
}
|
|
|
|
//
|
|
// CopyNode : Copies node information, destination opr, copy data, etc.
|
|
//
|
|
|
|
BOOL
|
|
CRestoreList::CopyNode(
|
|
CNode * pSrcNode,
|
|
CNode * pDesNode,
|
|
BOOL fReplacePPath2
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
if (pSrcNode && pDesNode)
|
|
{
|
|
pSrcNode->m_dwOperation = pDesNode->m_dwOperation ;
|
|
pSrcNode->m_dwAttributes = pDesNode->m_dwAttributes;
|
|
|
|
STRCOPY(pSrcNode->m_pszTemp, pDesNode->m_pszTemp);
|
|
CopyAcl(pSrcNode, pDesNode->m_pbAcl, pDesNode->m_cbAcl, pDesNode->m_fAclInline);
|
|
|
|
if ( IsRename(pDesNode->m_dwOperation) && fReplacePPath2 )
|
|
{
|
|
HEAP_FREE( pSrcNode->m_pPath2 );
|
|
pSrcNode->m_pPath2 = pDesNode->m_pPath2;
|
|
pDesNode->m_pPath2 = NULL;
|
|
}
|
|
|
|
fRet = TRUE;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
//
|
|
// CreateRestoreNode : Creates appropriate restore node
|
|
//
|
|
|
|
CRestoreList::CreateRestoreNode(
|
|
CNode * pNode,
|
|
DWORD dwOpr,
|
|
DWORD dwAttr,
|
|
DWORD dwFlags,
|
|
LPWSTR pTmpFile,
|
|
LPWSTR pPath1,
|
|
LPWSTR pPath2,
|
|
BYTE* pbAcl,
|
|
DWORD cbAcl,
|
|
BOOL fAclInline)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
if (pNode)
|
|
{
|
|
fRet = TRUE;
|
|
DWORD dwRestoreOpr = GetReverseOperation( dwOpr );
|
|
|
|
//
|
|
// If Source / Dest paths are same then remove this just
|
|
// added node.
|
|
//
|
|
|
|
if ( IsRename(dwRestoreOpr) &&
|
|
0 == lstrcmpi(pNode->m_pPath1,
|
|
pNode->m_pPath2) )
|
|
{
|
|
RemoveNode( pNode );
|
|
FreeNode( pNode );
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Rename optimizations should not be done for directories.
|
|
//
|
|
|
|
if ( OPR_FILE_RENAME == dwRestoreOpr )
|
|
{
|
|
//
|
|
// Check if the des node already exists in the tree, if so
|
|
// copy all the data from that node and delete it. Current
|
|
// node effectively represents that node.
|
|
//
|
|
|
|
DWORD dwCurNodeOpr = pNode->m_dwOperation;
|
|
CNode *pNodeDes = GetLastNode( pPath2, pPath1, TRUE );
|
|
|
|
if (pNodeDes &&
|
|
OPR_UNKNOWN != pNodeDes->m_dwOperation)
|
|
{
|
|
//
|
|
// Check for Cycle if so remove the current node also
|
|
//
|
|
|
|
if ( OPR_FILE_RENAME == pNodeDes->m_dwOperation &&
|
|
!lstrcmpi (pNode->m_pPath1,
|
|
pNodeDes->m_pPath2) )
|
|
{
|
|
//
|
|
// The existing node may be a hybrid one so preserve the
|
|
// hybrid operations cancel only rename
|
|
//
|
|
|
|
if (! pNodeDes->m_pszTemp &&
|
|
pNodeDes->m_dwAttributes == 0xFFFFFFFF &&
|
|
pNodeDes->m_pbAcl == NULL)
|
|
{
|
|
//
|
|
// Not a hybrid node - remove it.
|
|
//
|
|
|
|
RemoveNode( pNodeDes );
|
|
FreeNode( pNodeDes );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Hybrid Node so change the node to the other
|
|
// operations only.
|
|
//
|
|
|
|
HEAP_FREE( pNodeDes->m_pPath1 );
|
|
pNodeDes->m_pPath1 = pNodeDes->m_pPath2;
|
|
pNodeDes->m_pPath2 = NULL;
|
|
pNodeDes->m_dwOperation = OPR_SETATTRIB;
|
|
|
|
if (pNodeDes->m_pbAcl)
|
|
{
|
|
pNodeDes->m_dwOperation = OPR_SETACL;
|
|
}
|
|
|
|
if (pNodeDes->m_pszTemp)
|
|
{
|
|
pNodeDes->m_dwOperation = OPR_FILE_MODIFY;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the just add node as the operation cancels
|
|
// returning false will have that effect
|
|
|
|
fRet = FALSE;
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Remove the matching node from the list
|
|
//
|
|
|
|
RemoveNode( pNodeDes );
|
|
|
|
//
|
|
// Merge the matching node into the current rename node
|
|
//
|
|
|
|
CopyNode( pNode, pNodeDes, TRUE );
|
|
|
|
//
|
|
// Since all the information for modify/attrib/acl is
|
|
// copied over, change the opr to rename.
|
|
//
|
|
|
|
if (pNodeDes->m_dwOperation == OPR_SETATTRIB ||
|
|
pNodeDes->m_dwOperation == OPR_SETACL ||
|
|
pNodeDes->m_dwOperation == OPR_FILE_MODIFY)
|
|
{
|
|
pNode->m_dwOperation = dwRestoreOpr;
|
|
STRCOPY(pNode->m_pPath2, pPath2);
|
|
}
|
|
|
|
//
|
|
// If current opr on the node was delete and the newly
|
|
// copied operation is create we need to merge these and
|
|
// and change the operation to modify
|
|
//
|
|
// BUGBUG - will this code ever be executed?
|
|
// dwCurNodeOpr always seems to be OPR_UNKNOWN
|
|
|
|
if ( OPR_FILE_ADD == pNodeDes->m_dwOperation &&
|
|
OPR_FILE_DELETE == dwCurNodeOpr )
|
|
{
|
|
pNode->m_dwOperation = OPR_FILE_MODIFY;
|
|
}
|
|
|
|
if ( OPR_DIR_CREATE == pNodeDes->m_dwOperation &&
|
|
OPR_DIR_DELETE == dwCurNodeOpr )
|
|
{
|
|
pNode->m_dwOperation = OPR_SETATTRIB;
|
|
}
|
|
|
|
//
|
|
// If the operation is changed from a rename then pPath2 should
|
|
// not exist
|
|
//
|
|
|
|
if ( OPR_FILE_RENAME != pNode->m_dwOperation )
|
|
{
|
|
if ( pNode->m_pPath2 )
|
|
{
|
|
HEAP_FREE(pNode->m_pPath2);
|
|
pNode->m_pPath2 = NULL ;
|
|
}
|
|
}
|
|
|
|
FreeNode( pNodeDes );
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the necessary information into this node.
|
|
//
|
|
|
|
pNode->m_dwOperation = dwRestoreOpr;
|
|
pNode->m_dwAttributes = dwAttr;
|
|
STRCOPY(pNode->m_pszTemp, pTmpFile);
|
|
CopyAcl(pNode, pbAcl, cbAcl, fAclInline);
|
|
}
|
|
|
|
Exit:
|
|
return fRet;
|
|
}
|
|
|
|
|
|
//
|
|
// MergeRestoreNode : Merge the new information into the current retore node
|
|
//
|
|
|
|
BOOL
|
|
CRestoreList::MergeRestoreNode(
|
|
CNode * pNode,
|
|
DWORD dwOpr,
|
|
DWORD dwAttr,
|
|
DWORD dwFlags,
|
|
LPWSTR pTmpFile,
|
|
LPWSTR pPath1,
|
|
LPWSTR pPath2,
|
|
BYTE* pbAcl,
|
|
DWORD cbAcl,
|
|
BOOL fAclInline)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
if (pNode)
|
|
{
|
|
DWORD dwRestoreOpr = GetReverseOperation( dwOpr );
|
|
|
|
// note that dwOpr cannot be a rename operation,
|
|
// because we handle that separately in CreateRestoreNode
|
|
|
|
ASSERT( ! IsRename(dwOpr) );
|
|
|
|
switch( dwOpr )
|
|
{
|
|
case OPR_FILE_ADD :
|
|
{
|
|
//
|
|
// If current opr is add and the node already encountered a
|
|
// delete opr then remove this node because this is a no-op
|
|
//
|
|
|
|
if ( OPR_FILE_ADD == pNode->m_dwOperation )
|
|
{
|
|
RemoveNode( pNode );
|
|
FreeNode ( pNode );
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
if ( IsRename(pNode->m_dwOperation) )
|
|
{
|
|
//
|
|
// Change the original node to a delete operation, to
|
|
// retain proper order.
|
|
//
|
|
|
|
pNode->m_dwOperation = OPR_FILE_DELETE;
|
|
|
|
//
|
|
// Delete operation should be generated on PPath2
|
|
//
|
|
|
|
if (pNode->m_pPath1)
|
|
{
|
|
HEAP_FREE( pNode->m_pPath1);
|
|
pNode->m_pPath1 = NULL;
|
|
}
|
|
|
|
if (pNode->m_pPath2)
|
|
{
|
|
pNode->m_pPath1 = pNode->m_pPath2;
|
|
pNode->m_pPath2 = NULL;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case OPR_FILE_DELETE :
|
|
{
|
|
//
|
|
// Delete followd by an add should result in modify
|
|
//
|
|
|
|
if (OPR_FILE_DELETE == pNode->m_dwOperation)
|
|
{
|
|
pNode->m_dwOperation = OPR_FILE_MODIFY;
|
|
pNode->m_dwAttributes = dwAttr;
|
|
STRCOPY(pNode->m_pszTemp, pTmpFile);
|
|
CopyAcl(pNode, pbAcl, cbAcl, fAclInline);
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case OPR_FILE_MODIFY:
|
|
{
|
|
//
|
|
// Copy the modified file copy location, don't change
|
|
// the current restore operation.
|
|
//
|
|
|
|
if ( OPR_FILE_ADD == pNode->m_dwOperation ||
|
|
IsRename(pNode->m_dwOperation) )
|
|
{
|
|
STRCOPY(pNode->m_pszTemp, pTmpFile);
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
case OPR_SETATTRIB:
|
|
{
|
|
//
|
|
// Don't change the current restore operation just set the attr.
|
|
//
|
|
|
|
if ( OPR_UNKNOWN != pNode->m_dwOperation )
|
|
{
|
|
pNode->m_dwAttributes = dwAttr;
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case OPR_SETACL:
|
|
{
|
|
// setacl followed by any op
|
|
// just copy the acl to the new op
|
|
|
|
if (OPR_UNKNOWN != pNode->m_dwOperation)
|
|
{
|
|
CopyAcl(pNode, pbAcl, cbAcl, fAclInline);
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case OPR_DIR_DELETE :
|
|
{
|
|
//
|
|
// if Dir delete followed by a dir create then this
|
|
// operation should condense to set attrib + setacl
|
|
|
|
if ( OPR_DIR_DELETE == pNode->m_dwOperation )
|
|
{
|
|
//
|
|
// Need to change the oprn to set attrib if
|
|
// Attribute changed
|
|
//
|
|
|
|
pNode->m_dwOperation = OPR_SETATTRIB;
|
|
pNode->m_dwAttributes = dwAttr;
|
|
CopyAcl(pNode, pbAcl, cbAcl, fAclInline);
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case OPR_DIR_CREATE :
|
|
{
|
|
if ( OPR_DIR_CREATE == pNode->m_dwOperation )
|
|
{
|
|
RemoveNode( pNode );
|
|
FreeNode( pNode );
|
|
goto Exit;
|
|
}
|
|
|
|
if ( IsRename(pNode->m_dwOperation) )
|
|
{
|
|
//
|
|
// Check if the existing node has some file operations
|
|
// afterwards then don't optimize
|
|
//
|
|
|
|
if ( GetLastNode(
|
|
pNode->m_pPath1,
|
|
NULL,
|
|
TRUE) )
|
|
{
|
|
//
|
|
// Change the last node to a delete operation, to
|
|
// retain proper order.
|
|
//
|
|
|
|
pNode->m_dwOperation = OPR_DIR_DELETE;
|
|
|
|
//
|
|
// Delete operation should be generated on PPath2
|
|
//
|
|
|
|
if (pNode->m_pPath1)
|
|
{
|
|
HEAP_FREE( pNode->m_pPath1);
|
|
pNode->m_pPath1 = NULL;
|
|
}
|
|
|
|
if (pNode->m_pPath2)
|
|
{
|
|
pNode->m_pPath1 = pNode->m_pPath2;
|
|
pNode->m_pPath2 = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CNode * pNewNode = NULL;
|
|
|
|
//
|
|
// Create a new dir delete node
|
|
//
|
|
|
|
if (pNewNode = AppendNode(pPath1, pPath2) )
|
|
{
|
|
fRet = CreateRestoreNode(
|
|
pNewNode,
|
|
dwOpr,
|
|
dwAttr,
|
|
dwFlags,
|
|
pTmpFile,
|
|
pPath1,
|
|
pPath2,
|
|
pbAcl,
|
|
cbAcl,
|
|
fAclInline);
|
|
}
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
pNode->m_dwOperation = dwRestoreOpr;
|
|
|
|
//
|
|
// Change the node's attribute only if the new attrib exists
|
|
//
|
|
|
|
if (dwAttr != 0xFFFFFFFF)
|
|
pNode->m_dwAttributes = dwAttr;
|
|
|
|
STRCOPY(pNode->m_pszTemp, pTmpFile);
|
|
CopyAcl(pNode, pbAcl, cbAcl, fAclInline);
|
|
}
|
|
|
|
Exit:
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CRestoreList::CheckIntegrity(
|
|
LPWSTR pszDrive,
|
|
DWORD dwOpr,
|
|
DWORD dwAttr,
|
|
DWORD dwFlags,
|
|
LPWSTR pTmpFile,
|
|
LPWSTR pPath1,
|
|
LPWSTR pPath2,
|
|
BYTE * pbAcl,
|
|
DWORD cbAcl,
|
|
BOOL fAclInline)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
WCHAR szPath[MAX_PATH];
|
|
|
|
// source name MUST be present
|
|
|
|
if (! pPath1)
|
|
{
|
|
fRet = FALSE;
|
|
goto done;
|
|
}
|
|
|
|
// if acl is present and it's not inline,
|
|
// then temp file must exist
|
|
|
|
if (pbAcl && ! fAclInline)
|
|
{
|
|
MakeRestorePath(szPath, pszDrive, (LPWSTR) pbAcl);
|
|
if (-1 == GetFileAttributes(szPath))
|
|
{
|
|
fRet = FALSE;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
|
|
switch (dwOpr)
|
|
{
|
|
case OPR_FILE_RENAME:
|
|
case OPR_DIR_RENAME:
|
|
// renames should have dest path and no temp file
|
|
if (! pPath2 || pTmpFile)
|
|
fRet = FALSE;
|
|
break;
|
|
|
|
case OPR_FILE_MODIFY:
|
|
// modify should not have dest path but must have temp file
|
|
if (pPath2 || ! pTmpFile)
|
|
{
|
|
fRet = FALSE;
|
|
break;
|
|
}
|
|
|
|
// and the temp file must exist inside the datastore
|
|
MakeRestorePath(szPath, pszDrive, pTmpFile);
|
|
|
|
if (-1 == GetFileAttributes(szPath))
|
|
{
|
|
fRet = FALSE;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case OPR_SETACL:
|
|
// acl operation should have acl (either inline or not)
|
|
if (! pbAcl)
|
|
{
|
|
fRet = FALSE;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
done:
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
// AddMergeElement : Adds or merge the current element as appropriate
|
|
//
|
|
|
|
BOOL
|
|
CRestoreList::AddMergeElement(
|
|
LPWSTR pszDrive,
|
|
DWORD dwOpr,
|
|
DWORD dwAttr,
|
|
DWORD dwFlags,
|
|
LPWSTR pTmpFile,
|
|
LPWSTR pPath1,
|
|
LPWSTR pPath2,
|
|
BYTE * pbAcl,
|
|
DWORD cbAcl,
|
|
BOOL fAclInline)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
CNode * pNode = NULL;
|
|
|
|
// check to see if the entry is consistent
|
|
|
|
fRet = CheckIntegrity(pszDrive,
|
|
dwOpr,
|
|
dwAttr,
|
|
dwFlags,
|
|
pTmpFile,
|
|
pPath1,
|
|
pPath2,
|
|
pbAcl,
|
|
cbAcl,
|
|
fAclInline);
|
|
if (FALSE == fRet)
|
|
{
|
|
ASSERT(-1);
|
|
goto Exit;
|
|
}
|
|
|
|
if ( pPath1 )
|
|
{
|
|
if (
|
|
//
|
|
// Merge for renames are handled inside the Create/Merge funcs
|
|
//
|
|
|
|
! IsRename(dwOpr) &&
|
|
|
|
//
|
|
// Merge should only be allowed within directory life, check
|
|
// node paths to see if there are any directory life oprs.
|
|
//
|
|
|
|
( pNode = GetLastNode( pPath1, pPath2, TRUE ) ) &&
|
|
CanMerge( pNode->m_dwOperation , dwOpr, dwFlags )
|
|
|
|
|
|
)
|
|
{
|
|
fRet = MergeRestoreNode(
|
|
pNode,
|
|
dwOpr,
|
|
dwAttr,
|
|
dwFlags,
|
|
pTmpFile,
|
|
pPath1,
|
|
pPath2,
|
|
pbAcl,
|
|
cbAcl,
|
|
fAclInline);
|
|
|
|
if (!fRet)
|
|
goto Exit;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (pNode = AppendNode(pPath1, pPath2) )
|
|
{
|
|
fRet = CreateRestoreNode(
|
|
pNode,
|
|
dwOpr,
|
|
dwAttr,
|
|
dwFlags,
|
|
pTmpFile,
|
|
pPath1,
|
|
pPath2,
|
|
pbAcl,
|
|
cbAcl,
|
|
fAclInline);
|
|
|
|
if (!fRet)
|
|
{
|
|
//
|
|
// We failed to create the node properly, free this node
|
|
//
|
|
|
|
RemoveNode( pNode );
|
|
FreeNode( pNode );
|
|
|
|
//
|
|
// We still want to continue
|
|
//
|
|
|
|
fRet = TRUE;
|
|
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
return fRet;
|
|
}
|
|
|
|
|
|
//
|
|
// GenerateRestoreMap : Walk the tree and generates the restore map.
|
|
//
|
|
|
|
BOOL
|
|
CRestoreList::GenerateRestoreMap (
|
|
HANDLE hFile
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
if (hFile)
|
|
{
|
|
CNode * pNode = m_pListHead;
|
|
|
|
while (pNode)
|
|
{
|
|
if (! GenerateOperation( pNode , hFile ))
|
|
goto Exit;
|
|
|
|
pNode = pNode->m_pNext;
|
|
}
|
|
|
|
fRet = TRUE;
|
|
}
|
|
|
|
Exit:
|
|
return fRet;
|
|
}
|
|
|
|
//
|
|
// GenerateRenameEntry : Callback to write out the renames
|
|
//
|
|
|
|
BOOL
|
|
CRestoreList::GenerateRenameEntry(
|
|
CNode * pNode ,
|
|
HANDLE hFile
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
if( !pNode || ! IsRename(pNode->m_dwOperation) )
|
|
goto Exit;
|
|
|
|
//
|
|
// Check if this rename is no-op , src/des are the same
|
|
//
|
|
|
|
if ( IsRename(pNode->m_dwOperation) &&
|
|
lstrcmpi(pNode->m_pPath1, pNode->m_pPath2) == 0)
|
|
{
|
|
goto SkipMainNodeEntry;
|
|
}
|
|
|
|
// add rename operation
|
|
|
|
fRet = AppendRestoreMapEntry(
|
|
hFile,
|
|
pNode->m_dwOperation,
|
|
0xFFFFFFFF,
|
|
NULL,
|
|
pNode->m_pPath1,
|
|
pNode->m_pPath2,
|
|
NULL,
|
|
0,
|
|
0);
|
|
|
|
SkipMainNodeEntry:
|
|
|
|
// add modify operation if temp file exists
|
|
|
|
if ( pNode->m_pszTemp )
|
|
{
|
|
fRet = AppendRestoreMapEntry(
|
|
hFile,
|
|
OPR_FILE_MODIFY,
|
|
0xFFFFFFFF,
|
|
pNode->m_pszTemp,
|
|
pNode->m_pPath1,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
// add setattrib operation if attrib exists
|
|
|
|
if ( pNode->m_dwAttributes != 0xFFFFFFFF &&
|
|
pNode->m_dwAttributes != FILE_ATTRIBUTE_DIRECTORY )
|
|
{
|
|
fRet = AppendRestoreMapEntry(
|
|
hFile,
|
|
OPR_SETATTRIB,
|
|
pNode->m_dwAttributes,
|
|
NULL,
|
|
pNode->m_pPath1,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
// add setacl operation if acl exists
|
|
|
|
if ( pNode->m_pbAcl != NULL &&
|
|
pNode->m_cbAcl != 0)
|
|
{
|
|
fRet = AppendRestoreMapEntry(
|
|
hFile,
|
|
OPR_SETACL,
|
|
0xFFFFFFFF,
|
|
NULL,
|
|
pNode->m_pPath1,
|
|
NULL,
|
|
pNode->m_pbAcl,
|
|
pNode->m_cbAcl,
|
|
pNode->m_fAclInline);
|
|
}
|
|
|
|
|
|
Exit:
|
|
return fRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CRestoreList::GenerateOperation(
|
|
CNode * pNode ,
|
|
HANDLE hFile
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
BYTE bData[4096];
|
|
|
|
RestoreMapEntry * pResEntry = (RestoreMapEntry *)bData;
|
|
|
|
if( !pNode || OPR_UNKNOWN == pNode->m_dwOperation)
|
|
goto Exit;
|
|
|
|
if (IsRename(pNode->m_dwOperation) )
|
|
{
|
|
fRet = GenerateRenameEntry( pNode, hFile );
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Generate operations for Add/Modify/SetAttrib
|
|
//
|
|
|
|
// ensure that each persisted entry contains only necessary data
|
|
// e.g. a setattrib entry will not contain a temp filename, or acl
|
|
|
|
fRet = AppendRestoreMapEntry(
|
|
hFile,
|
|
pNode->m_dwOperation,
|
|
(pNode->m_dwOperation == OPR_SETATTRIB) ? pNode->m_dwAttributes : 0xFFFFFFFF,
|
|
(pNode->m_dwOperation == OPR_FILE_MODIFY ||
|
|
pNode->m_dwOperation == OPR_FILE_ADD) ? pNode->m_pszTemp : NULL,
|
|
pNode->m_pPath1,
|
|
NULL, // pPath2 should matter only for renames, which are handled separately
|
|
(pNode->m_dwOperation == OPR_SETACL) ? pNode->m_pbAcl : NULL,
|
|
(pNode->m_dwOperation == OPR_SETACL) ? pNode->m_cbAcl : 0,
|
|
(pNode->m_dwOperation == OPR_SETACL) ? pNode->m_fAclInline : 0);
|
|
|
|
|
|
//
|
|
// Generate an explicit set attrib operation
|
|
// entries except set attrib itself and delete.
|
|
//
|
|
|
|
if ( OPR_SETATTRIB != pNode->m_dwOperation &&
|
|
OPR_FILE_DELETE != pNode->m_dwOperation &&
|
|
OPR_DIR_DELETE != pNode->m_dwOperation &&
|
|
pNode->m_dwAttributes != 0xFFFFFFFF &&
|
|
pNode->m_dwAttributes != FILE_ATTRIBUTE_DIRECTORY )
|
|
{
|
|
fRet = AppendRestoreMapEntry(
|
|
hFile,
|
|
OPR_SETATTRIB,
|
|
pNode->m_dwAttributes,
|
|
NULL,
|
|
pNode->m_pPath1,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
//
|
|
// Generate an explicit set acl if needed
|
|
//
|
|
|
|
if ( pNode->m_pbAcl != NULL &&
|
|
pNode->m_cbAcl != 0 &&
|
|
OPR_SETACL != pNode->m_dwOperation &&
|
|
OPR_FILE_DELETE != pNode->m_dwOperation &&
|
|
OPR_DIR_DELETE != pNode->m_dwOperation)
|
|
{
|
|
fRet = AppendRestoreMapEntry(
|
|
hFile,
|
|
OPR_SETACL,
|
|
0xFFFFFFFF,
|
|
NULL,
|
|
pNode->m_pPath1,
|
|
NULL,
|
|
pNode->m_pbAcl,
|
|
pNode->m_cbAcl,
|
|
pNode->m_fAclInline);
|
|
}
|
|
|
|
Exit:
|
|
return fRet;
|
|
}
|
|
|
|
|