/****************************************************************************** * * 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 #include #include #include #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; }