/* * copy.c - File copy handler module. */ /* Headers **********/ #include "project.h" #pragma hdrstop #include "stub.h" #include "oleutil.h" /* Constants ************/ /* size of file copy buffer in bytes */ #define COPY_BUF_SIZE (64 * 1024) /* Module Variables *******************/ /* lock count for file copy buffer */ PRIVATE_DATA ULONG MulcCopyBufLock = 0; /* buffer for file copying */ PRIVATE_DATA PBYTE MpbyteCopyBuf = NULL; /* length of file copy buffer in bytes */ PRIVATE_DATA UINT MucbCopyBufLen = 0; /***************************** Private Functions *****************************/ /* Module Prototypes ********************/ PRIVATE_CODE TWINRESULT SimpleCopy(PRECNODE, RECSTATUSPROC, LPARAM); PRIVATE_CODE TWINRESULT CreateDestinationFolders(PCRECNODE); PRIVATE_CODE TWINRESULT CreateCopyBuffer(void); PRIVATE_CODE void DestroyCopyBuffer(void); PRIVATE_CODE TWINRESULT CopyFileByHandle(HANDLE, HANDLE, RECSTATUSPROC, LPARAM, ULONG, PULONG); PRIVATE_CODE TWINRESULT CopyFileByName(PCRECNODE, PRECNODE, RECSTATUSPROC, LPARAM, ULONG, PULONG); PRIVATE_CODE ULONG DetermineCopyScale(PCRECNODE); PRIVATE_CODE BOOL IsCopyDestination(PCRECNODE); PRIVATE_CODE BOOL SetDestinationTimeStamps(PCRECNODE); PRIVATE_CODE BOOL DeleteFolderProc(LPCTSTR, PCWIN32_FIND_DATA, PVOID); #ifdef DEBUG PRIVATE_CODE BOOL CopyBufferIsOk(void); PRIVATE_CODE BOOL VerifyRECITEMAndSrcRECNODE(PCRECNODE); #endif /* ** SimpleCopy() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT SimpleCopy(PRECNODE prnSrc, RECSTATUSPROC rsp, LPARAM lpCallbackData) { TWINRESULT tr; ULONG ulScale; PRECNODE prnDest; ULONG ulCurrent = 0; /* lpCallbackData may be any value. */ ASSERT(IS_VALID_STRUCT_PTR(prnSrc, CRECNODE)); ASSERT(! rsp || IS_VALID_CODE_PTR(rsp, RECSTATUSPROC)); ulScale = DetermineCopyScale(prnSrc); /* Copy the source file to each destination file. */ tr = TR_SUCCESS; BeginCopy(); for (prnDest = prnSrc->priParent->prnFirst; prnDest; prnDest = prnDest->prnNext) { if (prnDest != prnSrc) { if (IsCopyDestination(prnDest)) { tr = CopyFileByName(prnSrc, prnDest, rsp, lpCallbackData, ulScale, &ulCurrent); if (tr != TR_SUCCESS) break; ASSERT(ulCurrent <= ulScale); } } } EndCopy(); return(tr); } /* ** CreateDestinationFolders() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT CreateDestinationFolders(PCRECNODE pcrnSrc) { TWINRESULT tr = TR_SUCCESS; PCRECNODE pcrnDest; for (pcrnDest = pcrnSrc->priParent->prnFirst; pcrnDest; pcrnDest = pcrnDest->prnNext) { if (pcrnDest->rnaction == RNA_COPY_TO_ME) { tr = CreateFolders(pcrnDest->pcszFolder, ((PCOBJECTTWIN)(pcrnDest->hObjectTwin))->hpath); if (tr != TR_SUCCESS) break; } } return(tr); } /* ** CreateCopyBuffer() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT CreateCopyBuffer(void) { TWINRESULT tr; ASSERT(CopyBufferIsOk()); /* Has the copy buffer already been allocated? */ if (MpbyteCopyBuf) /* Yes. */ tr = TR_SUCCESS; else { /* No. Allocate it. */ if (AllocateMemory(COPY_BUF_SIZE, &MpbyteCopyBuf)) { MucbCopyBufLen = COPY_BUF_SIZE; tr = TR_SUCCESS; TRACE_OUT((TEXT("CreateCopyBuffer(): %u byte file copy buffer allocated."), MucbCopyBufLen)); } else tr = TR_OUT_OF_MEMORY; } ASSERT(CopyBufferIsOk()); return(tr); } /* ** DestroyCopyBuffer() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE void DestroyCopyBuffer(void) { ASSERT(CopyBufferIsOk()); /* Has the copy buffer already been allocated? */ if (MpbyteCopyBuf) { /* Yes. Free it. */ FreeMemory(MpbyteCopyBuf); MpbyteCopyBuf = NULL; TRACE_OUT((TEXT("DestroyCopyBuffer(): %u byte file copy buffer freed."), MucbCopyBufLen)); MucbCopyBufLen = 0; } ASSERT(CopyBufferIsOk()); return; } /* ** CopyFileByHandle() ** ** Copies one file to another. ** ** Arguments: hfSrc - file handle to open source file ** hfDest - file handle to open destination file ** ** Returns: TWINRESULT ** ** Side Effects: Leaves the file pointer of each file at the end of the file. */ PRIVATE_CODE TWINRESULT CopyFileByHandle(HANDLE hfSrc, HANDLE hfDest, RECSTATUSPROC rsp, LPARAM lpCallbackData, ULONG ulScale, PULONG pulcbTotal) { TWINRESULT tr; /* lpCallbackData may be any value. */ /* ulScale may be any value. */ ASSERT(IS_VALID_HANDLE(hfSrc, FILE)); ASSERT(IS_VALID_HANDLE(hfDest, FILE)); ASSERT(! rsp || IS_VALID_CODE_PTR(rsp, RECSTATUSROC)); ASSERT(IS_VALID_WRITE_PTR(pulcbTotal, ULONG)); /* Make sure the copy buffer has been created. */ tr = CreateCopyBuffer(); if (tr == TR_SUCCESS) { BeginCopy(); /* Move to the beginning of the files. */ if (SetFilePointer(hfSrc, 0, NULL, FILE_BEGIN) != INVALID_SEEK_POSITION) { if (SetFilePointer(hfDest, 0, NULL, FILE_BEGIN) != INVALID_SEEK_POSITION) { do { DWORD dwcbRead; if (ReadFile(hfSrc, MpbyteCopyBuf, MucbCopyBufLen, &dwcbRead, NULL)) { if (dwcbRead) { DWORD dwcbWritten; if (WriteFile(hfDest, MpbyteCopyBuf, dwcbRead, &dwcbWritten, NULL) && dwcbWritten == dwcbRead) { RECSTATUSUPDATE rsu; ASSERT(*pulcbTotal <= ULONG_MAX - dwcbRead); *pulcbTotal += dwcbRead; rsu.ulProgress = *pulcbTotal; rsu.ulScale = ulScale; if (! NotifyReconciliationStatus(rsp, RS_DELTA_COPY, (LPARAM)&rsu, lpCallbackData)) tr = TR_ABORT; } else tr = TR_DEST_WRITE_FAILED; } else /* Hit EOF. Stop. */ break; } else tr = TR_SRC_READ_FAILED; } while (tr == TR_SUCCESS); } else tr = TR_DEST_WRITE_FAILED; } else tr = TR_SRC_READ_FAILED; EndCopy(); } return(tr); } // MakeAnsiPath // // Copys path pszIn to pszOut, ensuring that pszOut has a valid ANSI mapping void MakeAnsiPath(LPTSTR pszIn, LPTSTR pszOut, int cchMax) { #ifdef UNICODE CHAR szAnsi[MAX_PATH]; pszOut[0] = L'\0'; WideCharToMultiByte(CP_ACP, 0, pszIn, -1, szAnsi, ARRAYSIZE(szAnsi), NULL, NULL); MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, pszOut, cchMax); if (lstrcmp(pszOut, pszIn)) { // Cannot convert losslessly from Unicode -> Ansi, so get the short path lstrcpyn(pszOut, pszIn, cchMax); SheShortenPath(pszOut, TRUE); } #else lstrcpyn(pszOut, pszIn, cchMax); #endif } /* ** CopyFileByName() ** ** Copies one file over another. ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: Copies source file's time stamp to destination file. */ PRIVATE_CODE TWINRESULT CopyFileByName(PCRECNODE pcrnSrc, PRECNODE prnDest, RECSTATUSPROC rsp, LPARAM lpCallbackData, ULONG ulScale, PULONG pulcbTotal) { TWINRESULT tr; TCHAR rgchSrcPath[MAX_PATH_LEN]; TCHAR rgchDestPath[MAX_PATH_LEN]; /* lpCallbackData may be any value. */ /* ulScale may be any value. */ ASSERT(IS_VALID_STRUCT_PTR(pcrnSrc, CRECNODE)); ASSERT(IS_VALID_STRUCT_PTR(prnDest, CRECNODE)); ASSERT(! rsp || IS_VALID_CODE_PTR(rsp, RECSTATUSROC)); ASSERT(IS_VALID_WRITE_PTR(pulcbTotal, ULONG)); /* Create source path string. */ ComposePath(rgchSrcPath, pcrnSrc->pcszFolder, pcrnSrc->priParent->pcszName, ARRAYSIZE(rgchSrcPath)); ASSERT(lstrlen(rgchSrcPath) < ARRAYSIZE(rgchSrcPath)); /* Create destination path string. */ ComposePath(rgchDestPath, prnDest->pcszFolder, prnDest->priParent->pcszName, ARRAYSIZE(rgchDestPath)); ASSERT(lstrlen(rgchDestPath) < ARRAYSIZE(rgchDestPath)); /* Check volumes. */ if (MyIsPathOnVolume(rgchSrcPath, (HPATH)(pcrnSrc->hvid)) && MyIsPathOnVolume(rgchDestPath, (HPATH)(prnDest->hvid))) { FILESTAMP fsSrc; FILESTAMP fsDest; /* Compare current file stamps with recorded file stamps. */ MyGetFileStamp(rgchSrcPath, &fsSrc); MyGetFileStamp(rgchDestPath, &fsDest); if (! MyCompareFileStamps(&(pcrnSrc->fsCurrent), &fsSrc) && ! MyCompareFileStamps(&(prnDest->fsCurrent), &fsDest)) { HANDLE hfSrc; /* Open source file. Assume source file will be read sequentially. */ hfSrc = CreateFile(rgchSrcPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hfSrc != INVALID_HANDLE_VALUE) { HANDLE hfDest; /* * Create destination file. Assume destination file will be * written sequentially. */ TCHAR szAnsiPath[MAX_PATH]; MakeAnsiPath(rgchDestPath, szAnsiPath, ARRAYSIZE(szAnsiPath)); hfDest = CreateFile(szAnsiPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, (FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN), NULL); if (hfDest != INVALID_HANDLE_VALUE) { /* Everything is cool. Copy the file. */ tr = CopyFileByHandle(hfSrc, hfDest, rsp, lpCallbackData, ulScale, pulcbTotal); if (tr == TR_SUCCESS) { /* * Set the destination file's time stamp to the source * file's time stamp to assist clients that don't maintain * a persistent briefcase database, like MPR. Failure to * set the time stamp is not fatal. */ if (! SetFileTime(hfDest, NULL, NULL, &(pcrnSrc->fsCurrent.ftMod))) WARNING_OUT((TEXT("CopyFileByName(): Failed to set last modification time stamp of destination file %s to match source file %s."), rgchDestPath, rgchSrcPath)); } /* Failing to close the destination file is fatal here. */ if (! CloseHandle(hfDest) && tr == TR_SUCCESS) tr = TR_DEST_WRITE_FAILED; } else tr = TR_DEST_OPEN_FAILED; /* Failing to close the source file successfully is not fatal. */ CloseHandle(hfSrc); } else tr = TR_SRC_OPEN_FAILED; } else tr = TR_FILE_CHANGED; } else tr = TR_UNAVAILABLE_VOLUME; #ifdef DEBUG if (tr == TR_SUCCESS) TRACE_OUT((TEXT("CopyFileByName(): %s\\%s copied to %s\\%s."), pcrnSrc->pcszFolder, pcrnSrc->priParent->pcszName, prnDest->pcszFolder, prnDest->priParent->pcszName)); else TRACE_OUT((TEXT("CopyFileByName(): Failed to copy %s\\%s to %s\\%s."), pcrnSrc->pcszFolder, pcrnSrc->priParent->pcszName, prnDest->pcszFolder, prnDest->priParent->pcszName)); #endif return(tr); } /* ** DetermineCopyScale() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE ULONG DetermineCopyScale(PCRECNODE pcrnSrc) { DWORD dwcbSrcFileLen; PCRECNODE pcrn; ULONG ulScale = 0; ASSERT(IS_VALID_STRUCT_PTR(pcrnSrc, CRECNODE)); /* * RAIDRAID: (16257) If anyone tries to copy more than 4 Gb of files, this * scaling calculation is broken. */ ASSERT(! pcrnSrc->fsCurrent.dwcbHighLength); dwcbSrcFileLen = pcrnSrc->fsCurrent.dwcbLowLength; for (pcrn = pcrnSrc->priParent->prnFirst; pcrn; pcrn = pcrn->prnNext) { if (pcrn != pcrnSrc) { if (IsCopyDestination(pcrn)) { ASSERT(ulScale < ULONG_MAX - dwcbSrcFileLen); ulScale += dwcbSrcFileLen; } } } TRACE_OUT((TEXT("DetermineCopyScale(): Scale for %s is %lu."), pcrnSrc->priParent->pcszName, ulScale)); return(ulScale); } /* ** IsCopyDestination() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsCopyDestination(PCRECNODE pcrn) { BOOL bDest = FALSE; ASSERT(IS_VALID_STRUCT_PTR(pcrn, CRECNODE)); switch (pcrn->priParent->riaction) { case RIA_COPY: switch (pcrn->rnaction) { case RNA_COPY_TO_ME: bDest = TRUE; break; default: break; } break; case RIA_MERGE: switch (pcrn->rnaction) { case RNA_COPY_TO_ME: case RNA_MERGE_ME: bDest = TRUE; break; default: break; } break; default: ERROR_OUT((TEXT("IsCopyDestination(): Bad RECITEM action %d."), pcrn->priParent->riaction)); break; } return(bDest); } /* ** SetDestinationTimeStamps() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL SetDestinationTimeStamps(PCRECNODE pcrnSrc) { BOOL bResult = TRUE; PCRECNODE pcrn; ASSERT(IS_VALID_STRUCT_PTR(pcrnSrc, CRECNODE)); for (pcrn = pcrnSrc->priParent->prnFirst; pcrn; pcrn = pcrn->prnNext) { if (pcrn->rnaction == RNA_COPY_TO_ME) { TCHAR rgchPath[MAX_PATH_LEN]; HANDLE hfDest; ComposePath(rgchPath, pcrn->pcszFolder, pcrn->priParent->pcszName, ARRAYSIZE(rgchPath)); ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath)); hfDest = CreateFile(rgchPath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hfDest != INVALID_HANDLE_VALUE) { if (! SetFileTime(hfDest, NULL, NULL, &(pcrnSrc->fsCurrent.ftMod))) bResult = FALSE; if (! CloseHandle(hfDest)) bResult = FALSE; } else bResult = FALSE; if (bResult) TRACE_OUT((TEXT("SetDestinationTimeStamps(): Set last modification time stamp of %s to match last modification time stamp of %s\\%s."), rgchPath, pcrnSrc->pcszFolder, pcrnSrc->priParent->pcszName)); else WARNING_OUT((TEXT("SetDestinationTimeStamps(): Failed to set last modification time stamp of %s to match last modification time stamp of %s\\%s."), rgchPath, pcrnSrc->pcszFolder, pcrnSrc->priParent->pcszName)); } } return(bResult); } /* ** DeleteFolderProc() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL DeleteFolderProc(LPCTSTR pcszFolder, PCWIN32_FIND_DATA pcwfd, PVOID ptr) { ASSERT(IsCanonicalPath(pcszFolder)); ASSERT(IS_VALID_READ_PTR(pcwfd, CWIN32_FIND_DATA)); ASSERT(IS_VALID_WRITE_PTR(ptr, TWINRESULT)); if (IS_ATTR_DIR(pcwfd->dwFileAttributes)) { TCHAR rgchPath[MAX_PATH_LEN]; ComposePath(rgchPath, pcszFolder, pcwfd->cFileName, ARRAYSIZE(rgchPath)); ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath)); if (RemoveDirectory(rgchPath)) { WARNING_OUT((TEXT("DeleteFolderProc(): Removed folder %s."), rgchPath)); NotifyShell(rgchPath, NSE_DELETE_FOLDER); } else { WARNING_OUT((TEXT("DeleteFolderProc(): Failed to remove folder %s."), rgchPath)); *(PTWINRESULT)ptr = TR_DEST_WRITE_FAILED; } } else TRACE_OUT((TEXT("DeleteFolderProc(): Skipping file %s\\%s."), pcszFolder, pcwfd->cFileName)); return(TRUE); } #ifdef DEBUG /* ** CopyBufferIsOk() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL CopyBufferIsOk(void) { /* Are the module copy buffer variables in a correct state? */ return((! MucbCopyBufLen && ! MpbyteCopyBuf) || (MucbCopyBufLen > 0 && IS_VALID_WRITE_BUFFER_PTR(MpbyteCopyBuf, BYTE, MucbCopyBufLen))); } /* ** VerifyRECITEMAndSrcRECNODE() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL VerifyRECITEMAndSrcRECNODE(PCRECNODE pcrnSrc) { /* Do the RECITEM and source RECNODE actions match? */ return((pcrnSrc->priParent->riaction == RIA_COPY && pcrnSrc->rnaction == RNA_COPY_FROM_ME) || (pcrnSrc->priParent->riaction == RIA_MERGE && pcrnSrc->rnaction == RNA_MERGE_ME)); } #endif /****************************** Public Functions *****************************/ /* ** BeginCopy() ** ** Increments copy buffer lock count. ** ** Arguments: ** ** Returns: void ** ** Side Effects: none */ PUBLIC_CODE void BeginCopy(void) { ASSERT(CopyBufferIsOk()); ASSERT(MulcCopyBufLock < ULONG_MAX); MulcCopyBufLock++; ASSERT(CopyBufferIsOk()); return; } /* ** EndCopy() ** ** Decrements copy buffer lock count. ** ** Arguments: ** ** Returns: void ** ** Side Effects: Frees copy buffer if lock count goes to 0. */ PUBLIC_CODE void EndCopy(void) { ASSERT(CopyBufferIsOk()); /* Is the copy buffer still locked? */ if (! --MulcCopyBufLock) DestroyCopyBuffer(); ASSERT(CopyBufferIsOk()); return; } /* ** CopyHandler() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT CopyHandler(PRECNODE prnSrc, RECSTATUSPROC rsp, LPARAM lpCallbackData, DWORD dwFlags, HWND hwndOwner, HWND hwndProgressFeedback) { TWINRESULT tr; RECSTATUSUPDATE rsu; /* lpCallbackData may be any value. */ ASSERT(IS_VALID_STRUCT_PTR(prnSrc, CRECNODE)); ASSERT(! rsp || IS_VALID_CODE_PTR(rsp, RECSTATUSPROC)); ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_RI_FLAGS)); ASSERT(IS_FLAG_CLEAR(dwFlags, RI_FL_ALLOW_UI) || IS_VALID_HANDLE(hwndOwner, WND)); ASSERT(IS_FLAG_CLEAR(dwFlags, RI_FL_FEEDBACK_WINDOW_VALID) || IS_VALID_HANDLE(hwndProgressFeedback, WND)); ASSERT(VerifyRECITEMAndSrcRECNODE(prnSrc)); /* 0% complete. */ rsu.ulScale = 1; rsu.ulProgress = 0; if (NotifyReconciliationStatus(rsp, RS_BEGIN_COPY, (LPARAM)&rsu, lpCallbackData)) { tr = CreateDestinationFolders(prnSrc); if (tr == TR_SUCCESS) { TCHAR rgchPath[MAX_PATH_LEN]; CLSID clsidReconciler; HRESULT hr; ComposePath(rgchPath, prnSrc->pcszFolder, prnSrc->priParent->pcszName, ARRAYSIZE(rgchPath)); ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath)); if (SUCCEEDED(GetCopyHandlerClassID(rgchPath, &clsidReconciler))) { hr = OLECopy(prnSrc, &clsidReconciler, rsp, lpCallbackData, dwFlags, hwndOwner, hwndProgressFeedback); if (SUCCEEDED(hr)) { if (hr != S_FALSE) { /* * Set the destination files' time stamps to the source * file's time stamp to assist clients that don't maintain * a persistent briefcase database, like MPR. Failure to * set the time stamps is not fatal. */ ASSERT(hr == REC_S_IDIDTHEUPDATES); TRACE_OUT((TEXT("CopyHandler(): OLECopy() on %s returned %s."), rgchPath, GetHRESULTString(hr))); if (! SetDestinationTimeStamps(prnSrc)) WARNING_OUT((TEXT("CopyHandler(): SetDestinationTimeStamps() failed. Not all destination files have been marked with source file's time stamp."))); tr = TR_SUCCESS; } else { WARNING_OUT((TEXT("CopyHandler(): OLECopy() on %s returned %s. Resorting to internal copy routine."), rgchPath, GetHRESULTString(hr))); /* * Update the source RECNODE's file stamp in case it was * changed by the reconciler. */ MyGetFileStampByHPATH(((PCOBJECTTWIN)(prnSrc->hObjectTwin))->hpath, GetString(((PCOBJECTTWIN)(prnSrc->hObjectTwin))->ptfParent->hsName), &(prnSrc->fsCurrent)); tr = SimpleCopy(prnSrc, rsp, lpCallbackData); } } else tr = TranslateHRESULTToTWINRESULT(hr); } else tr = SimpleCopy(prnSrc, rsp, lpCallbackData); if (tr == TR_SUCCESS) { /* 100% complete. */ rsu.ulScale = 1; rsu.ulProgress = 1; /* Don't allow abort here. */ NotifyReconciliationStatus(rsp, RS_END_COPY, (LPARAM)&rsu, lpCallbackData); } } } else tr = TR_ABORT; return(tr); } /* ** NotifyReconciliationStatus() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL NotifyReconciliationStatus(RECSTATUSPROC rsp, UINT uMsg, LPARAM lp, LPARAM lpCallbackData) { BOOL bContinue; /* lp may be any value. */ /* lpCallbackData may be any value. */ ASSERT(! rsp || IS_VALID_CODE_PTR(rsp, RECSTATUSROC)); ASSERT(IsValidRecStatusProcMsg(uMsg)); if (rsp) { TRACE_OUT((TEXT("NotifyReconciliationStatus(): Calling RECSTATUSPROC with message %s, ulProgress %lu, ulScale %lu, callback data %#lx."), GetRECSTATUSPROCMSGString(uMsg), ((PCRECSTATUSUPDATE)lp)->ulProgress, ((PCRECSTATUSUPDATE)lp)->ulScale, lpCallbackData)); bContinue = (*rsp)(uMsg, lp, lpCallbackData); } else { TRACE_OUT((TEXT("NotifyReconciliationStatus(): Not calling NULL RECSTATUSPROC with message %s, ulProgress %lu, ulScale %lu, callback data %#lx."), GetRECSTATUSPROCMSGString(uMsg), ((PCRECSTATUSUPDATE)lp)->ulProgress, ((PCRECSTATUSUPDATE)lp)->ulScale, lpCallbackData)); bContinue = TRUE; } if (! bContinue) WARNING_OUT((TEXT("NotifyReconciliationStatus(): Client callback aborted reconciliation."))); return(bContinue); } /* ** CreateFolders() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT CreateFolders(LPCTSTR pcszPath, HPATH hpath) { TWINRESULT tr; ASSERT(IsCanonicalPath(pcszPath)); ASSERT(IS_VALID_HANDLE(hpath, PATH)); if (MyIsPathOnVolume(pcszPath, hpath)) { TCHAR rgchPath[MAX_PATH_LEN]; LPTSTR pszRootEnd; LPTSTR pszHackSlash; /* Create working copy of path. */ ASSERT(lstrlen(pcszPath) < ARRAYSIZE(rgchPath)); lstrcpyn(rgchPath, pcszPath, ARRAYSIZE(rgchPath)); pszRootEnd = FindEndOfRootSpec(rgchPath, hpath); /* * Hack off the path at each successive slash, and check to see if that * folder needs to be created. */ tr = TR_SUCCESS; pszHackSlash = pszRootEnd; while (*pszHackSlash) { TCHAR chReplaced; while (*pszHackSlash && *pszHackSlash != TEXT('\\')) pszHackSlash = CharNext(pszHackSlash); /* Replace the slash with a null terminator to set the current folder. */ chReplaced = *pszHackSlash; *pszHackSlash = TEXT('\0'); /* Does the folder exist? */ if (! PathExists(rgchPath)) { /* No. Try to create it. */ TCHAR szAnsiPath[MAX_PATH]; MakeAnsiPath(rgchPath, szAnsiPath, ARRAYSIZE(szAnsiPath)); if (CreateDirectory(szAnsiPath, NULL)) { WARNING_OUT((TEXT("CreateFolders(): Created folder %s."), rgchPath)); NotifyShell(rgchPath, NSE_CREATE_FOLDER); } else { WARNING_OUT((TEXT("CreateFolders(): Failed to create folder %s."), rgchPath)); tr = TR_DEST_OPEN_FAILED; break; } } *pszHackSlash = chReplaced; if (chReplaced) pszHackSlash++; } } else tr = TR_UNAVAILABLE_VOLUME; return(tr); } /* ** DestroySubtree() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT DestroySubtree(LPCTSTR pcszPath, HPATH hpath) { TWINRESULT tr; ASSERT(IsCanonicalPath(pcszPath)); ASSERT(IS_VALID_HANDLE(hpath, PATH)); if (MyIsPathOnVolume(pcszPath, hpath)) { tr = ExpandSubtree(hpath, &DeleteFolderProc, &tr); if (tr == TR_SUCCESS) { if (RemoveDirectory(pcszPath)) { WARNING_OUT((TEXT("DestroySubtree(): Subtree %s removed successfully."), pcszPath)); NotifyShell(pcszPath, NSE_DELETE_FOLDER); } else { if (PathExists(pcszPath)) { /* Still there. */ WARNING_OUT((TEXT("DestroySubtree(): Failed to remove subtree root %s."), pcszPath)); tr = TR_DEST_WRITE_FAILED; } else /* Already gone. */ tr = TR_SUCCESS; } } } else tr = TR_UNAVAILABLE_VOLUME; return(tr); } #ifdef DEBUG /* ** IsValidRecStatusProcMsg() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsValidRecStatusProcMsg(UINT uMsg) { BOOL bResult; switch (uMsg) { case RS_BEGIN_COPY: case RS_DELTA_COPY: case RS_END_COPY: case RS_BEGIN_MERGE: case RS_DELTA_MERGE: case RS_END_MERGE: case RS_BEGIN_DELETE: case RS_DELTA_DELETE: case RS_END_DELETE: bResult = TRUE; break; default: bResult = FALSE; ERROR_OUT((TEXT("IsValidRecStatusProcMsg(): Invalid RecStatusProc() message %u."), uMsg)); break; } return(bResult); } #endif