//************************************************************* // // Functions to copy the profile directory // // Microsoft Confidential // Copyright (c) Microsoft Corporation 1995 // All rights reserved // //************************************************************* #include "uenv.h" #include "strsafe.h" // // Local function proto-types // BOOL RecurseDirectory (HANDLE hTokenUser, LPTSTR lpSrcDir, DWORD cchSrcDir, LPTSTR lpDestDir, DWORD cchDestDir, DWORD dwFlags, LPFILEINFO *llSrcDirs, LPFILEINFO *llSrcFiles, BOOL bSkipNtUser, LPTSTR lpExcludeList, BOOL bRecurseDest); BOOL AddFileInfoNode (LPFILEINFO *lpFileInfo, LPTSTR lpSrcFile, LPTSTR lpDestFile, LPFILETIME ftLastWrite, LPFILETIME ftCreate, DWORD dwFileSize, DWORD dwFileAttribs, BOOL bHive); BOOL FreeFileInfoList (LPFILEINFO lpFileInfo); BOOL SyncItems (LPFILEINFO lpSrcItems, LPFILEINFO lpDestItems, BOOL bFile, LPFILETIME ftDelRefTime); void CopyFileFunc (LPTHREADINFO lpThreadInfo); LPTSTR ConvertExclusionList (LPCTSTR lpSourceDir, LPCTSTR lpExclusionList); DWORD FindDirectorySize(LPTSTR lpDir, LPFILEINFO lpFiles, DWORD dwFlags, DWORD* pdwLargestHiveFile, DWORD* pdwTotalFiles); DWORD FindTotalDiskSpaceNeeded(DWORD dwTotalSrcFiles, DWORD dwTotalDestFiles, DWORD dwLargestHiveFile, LPFILEINFO lpSrcFiles, DWORD dwFlags); DWORD FindTotalNMaxFileSize(LPFILEINFO lpSrcFiles, DWORD dwNumOfFiles); BOOL ReconcileDirectory(LPTSTR lpSrcDir, LPTSTR lpDestDir, DWORD dwFlags, DWORD dwSrcAttribs); //************************************************************* // // CopyProfileDirectoryEx() // // Purpose: Copies the profile directory from the source // to the destination // // // Parameters: LPCTSTR lpSourceDir - Source directory // LPCTSTR lpDestDir - Destination directory // DWORD dwFlags - Flags // LPFILETIME ftDelRefTime - Delete file reference time // LPCTSTR lpExclusionList - List of directories to exclude // // // Return: (BOOL) TRUE if successful // FALSE if an error occurs // // // Comments: Called after impersonating the user. // // // History: Date Author Comment // 5/24/95 ericflo Created // 4/09/98 ericflo Converted to CopyProfileDirectoryEx // 9/28/98 ushaji Modified to check for free space // 3/14/00 weiruc Modified to copy hive file even if // hive is still loaded and ignore copy // hive file errors. // //************************************************************* BOOL CopyProfileDirectoryEx (LPCTSTR lpSourceDir, LPCTSTR lpDestinationDir, DWORD dwFlags, LPFILETIME ftDelRefTime, LPCTSTR lpExclusionList) { LPTSTR lpSrcDir = NULL, lpDestDir = NULL; LPTSTR lpSrcEnd, lpDestEnd; LPTSTR lpExcludeListSrc = NULL; LPTSTR lpExcludeListDest = NULL; LPFILEINFO lpSrcFiles = NULL, lpDestFiles = NULL; LPFILEINFO lpSrcDirs = NULL, lpDestDirs = NULL; LPFILEINFO lpTemp; THREADINFO ThreadInfo = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL}; DWORD dwThreadId; HANDLE hThreads[NUM_COPY_THREADS]; HANDLE hStatusThread = 0; DWORD dwThreadCount = 0; HANDLE hFile; WIN32_FIND_DATA fd; BOOL bResult = FALSE; BOOL bSynchronize = FALSE; UINT i; DWORD dwTotalSrcFiles = 0; DWORD dwTotalDestFiles = 0; DWORD dwLargestHiveFile; DWORD dwTotalDiskSpaceNeeded; ULARGE_INTEGER ulFreeBytesAvailableToCaller, ulTotalNumberOfBytes, ulTotalNumberOfFreeBytes; DWORD dwErr, dwErr1=0; TCHAR szErr[MAX_PATH]; TCHAR szTmpHive[MAX_PATH]; BOOL bReconcileHiveSucceeded; HKEY hkCurrentUser = NULL; HANDLE hTokenUser = NULL; size_t cchSrc, cchDest; DWORD cchSrcEnd, cchDestEnd; HRESULT hr; dwErr = GetLastError(); // // Validate parameters // if (!lpSourceDir || !lpDestinationDir) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: received NULL pointer"))); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } hr = StringCchLength((LPTSTR)lpSourceDir, MAX_PATH, &cchSrc); if (FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: invalid src dir"))); SetLastError(HRESULT_CODE(hr)); return FALSE; } hr = StringCchLength((LPTSTR)lpDestinationDir, MAX_PATH, &cchDest); if (FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: invalid dest dir"))); SetLastError(HRESULT_CODE(hr)); return FALSE; } // // Verbose output // DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Entering, lpSourceDir = <%s>, lpDestinationDir = <%s>, dwFlags = 0x%x"), lpSourceDir, lpDestinationDir, dwFlags)); // // Get the caller's token // if (!OpenThreadToken (GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE, TRUE, &hTokenUser)) { if (!OpenProcessToken(GetCurrentProcess(), TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE, &hTokenUser)) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Fail to get process token with error %d."), GetLastError())); return FALSE; } } // // If there is an exclusion list, convert it into an array of null // terminate strings (double null at the end) based upon the source // directory // if ((dwFlags & CPD_USEEXCLUSIONLIST) && lpExclusionList) { DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: lpExclusionList = <%s>"), lpExclusionList)); lpExcludeListSrc = ConvertExclusionList (lpSourceDir, lpExclusionList); if (!lpExcludeListSrc) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to convert exclusion list for source"))); dwErr = ERROR_INVALID_DATA; goto Exit; } if (!(dwFlags & CPD_DELDESTEXCLUSIONS)) { lpExcludeListDest = ConvertExclusionList (lpDestinationDir, lpExclusionList); if (!lpExcludeListDest) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to convert exclusion list for destination"))); dwErr = ERROR_INVALID_DATA; goto Exit; } } } // // Get the desktop handle // ThreadInfo.hDesktop = GetThreadDesktop(GetCurrentThreadId()); // // Is this a full sync copy (delete extra files / directories in dest). // if (dwFlags & CPD_SYNCHRONIZE) { bSynchronize = TRUE; } // // Test / Create the destination directory // if (!CreateNestedDirectory(lpDestinationDir, NULL)) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to create the destination directory. Error = %d"), GetLastError())); dwErr = GetLastError(); goto Exit; } // // Create and set up the directory buffers // cchSrc = cchDest = 2 * MAX_PATH; lpSrcDir = LocalAlloc(LPTR, cchSrc * sizeof(TCHAR)); lpDestDir = LocalAlloc(LPTR, cchDest * sizeof(TCHAR)); if (!lpSrcDir || !lpDestDir) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to allocate memory for working directories"))); dwErr = GetLastError(); goto Exit; } StringCchCopy (lpSrcDir, cchSrc, lpSourceDir); StringCchCopy (lpDestDir, cchDest, lpDestinationDir); // // Setup ending pointers // lpSrcEnd = CheckSlashEx (lpSrcDir, cchSrc, &cchSrcEnd); lpDestEnd = CheckSlashEx (lpDestDir, cchDest, &cchDestEnd); // // Recurse through the folders gathering info // if (!(dwFlags & CPD_COPYHIVEONLY)) { // // Recurse the source directory // if (!RecurseDirectory(hTokenUser, lpSrcDir, cchSrc, lpDestDir, cchDest, dwFlags, &lpSrcDirs, &lpSrcFiles, TRUE, lpExcludeListSrc, FALSE)) { DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: RecurseDirectory returned FALSE"))); dwErr = GetLastError(); goto Exit; } if (bSynchronize) { // // Recurse the destination directory // if (!RecurseDirectory(hTokenUser, lpDestDir, cchDest, lpSrcDir, cchSrc, dwFlags, &lpDestDirs, &lpDestFiles, TRUE, lpExcludeListDest, TRUE)) { DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: RecurseDirectory returned FALSE"))); dwErr = GetLastError(); goto Exit; } } } // // determine the source and destination sizes // if(FindDirectorySize(lpSrcDir, lpSrcFiles, dwFlags, &dwLargestHiveFile, &dwTotalSrcFiles) != ERROR_SUCCESS) { DebugMsg((DM_WARNING, TEXT("FindDirectorySize: Error = %08x"), GetLastError())); dwErr = GetLastError(); goto Exit; } if(FindDirectorySize(lpDestDir, lpDestFiles, dwFlags, NULL, &dwTotalDestFiles) != ERROR_SUCCESS) { DebugMsg((DM_WARNING, TEXT("FindDirectorySize: Error = %08x"), GetLastError())); dwErr = GetLastError(); goto Exit; } // // determine the disk space needed // dwTotalDiskSpaceNeeded = FindTotalDiskSpaceNeeded(dwTotalSrcFiles, dwTotalDestFiles, dwLargestHiveFile, lpSrcFiles, dwFlags); // // CopyProfileDirectoryEx is called with impersonation on // if (!GetDiskFreeSpaceEx(lpDestDir, &ulFreeBytesAvailableToCaller, &ulTotalNumberOfBytes, &ulTotalNumberOfFreeBytes)) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to get the Free Disk Space <%s>. Error = %d"), lpDestDir, GetLastError())); dwErr = GetLastError(); goto Exit; } DebugMsg((DM_VERBOSE, TEXT("Available\t\t%d"), ulFreeBytesAvailableToCaller.QuadPart)); DebugMsg((DM_VERBOSE, TEXT("Needed\t\t%d"), dwTotalDiskSpaceNeeded)); DebugMsg((DM_VERBOSE, TEXT("Src size\t\t%d"), dwTotalSrcFiles)); DebugMsg((DM_VERBOSE, TEXT("Dest size\t\t%d"), dwTotalDestFiles)); DebugMsg((DM_VERBOSE, TEXT("Largest hive file\t\t%d"), dwLargestHiveFile)); if(dwTotalDiskSpaceNeeded > ulFreeBytesAvailableToCaller.QuadPart) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Not enough disk space on <%s>"), lpDestDir)); dwErr = ERROR_DISK_FULL; goto Exit; } // // Synchronize the directories and files if appropriate // if (bSynchronize) { // // Files first... // SyncItems (lpSrcFiles, lpDestFiles, TRUE, (dwFlags & CPD_USEDELREFTIME) ? ftDelRefTime : NULL); // // Now the directories... // SyncItems (lpSrcDirs, lpDestDirs, FALSE, (dwFlags & CPD_USEDELREFTIME) ? ftDelRefTime : NULL); } // // Copy the actual hive, log, ini files first // if (!(dwFlags & CPD_IGNOREHIVE)) { // // Search for all user hives // if (dwFlags & CPD_WIN95HIVE) { StringCchCopy (lpSrcEnd, cchSrcEnd, c_szUserStar); } else { StringCchCopy (lpSrcEnd, cchSrcEnd, c_szNTUserStar); } // // Enumerate // hFile = FindFirstFile(lpSrcDir, &fd); if (hFile != INVALID_HANDLE_VALUE) { do { // // Setup the filename // if((dwFlags & CPD_USETMPHIVEFILE) && (lstrcmpi(fd.cFileName, c_szNTUserMan) == 0 || lstrcmpi(fd.cFileName, c_szNTUserDat) == 0)) { DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Hive is still loaded, use temporary hive file"))); // // Use temporary hive file because unloading hive failed. // StringCchCopy(lpSrcEnd, cchSrcEnd, c_szNTUserTmp); } else { StringCchCopy (lpSrcEnd, cchSrcEnd, fd.cFileName); } StringCchCopy (lpDestEnd, cchDestEnd, fd.cFileName); // // Do not reconcile the log file if we are using the tmp hive // file. Skip it. // if((dwFlags & CPD_USETMPHIVEFILE) && lstrcmpi(fd.cFileName + lstrlen(fd.cFileName) - lstrlen(c_szLog), c_szLog) == 0) { DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Log file %s skipped"), lpDestDir)); continue; } // // Skip the temporary hive file here. Will deal with it when // we find an actual hive file. // if(lstrcmpi(fd.cFileName, c_szNTUserTmp) == 0) { DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: %s skipped"), fd.cFileName)); continue; } DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Found hive file %s"), fd.cFileName)); if(!ReconcileFile(lpSrcDir, lpDestDir, dwFlags, NULL, fd.nFileSizeLow, TRUE)) { dwErr1 = GetLastError(); DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: ReconcileFile failed with error = %d"), dwErr1)); if (!(dwFlags & CPD_IGNORECOPYERRORS)) { dwErr = dwErr1; ReportError(hTokenUser, PI_NOUI, 3, EVENT_COPYERROR, lpSrcDir, lpDestDir, GetErrString(dwErr, szErr)); FindClose(hFile); goto Exit; } else { ReportError(hTokenUser, PI_NOUI | EVENT_WARNING_TYPE, 3, EVENT_COPYERROR, lpSrcDir, lpDestDir, GetErrString(dwErr1, szErr)); } } // // Find the next entry // } while (FindNextFile(hFile, &fd)); FindClose(hFile); dwErr = ERROR_SUCCESS; } else { dwErr = GetLastError(); DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: FindFirstFile failed to find a hive!. Error = %d"), dwErr)); } } // // Create all the directories // if (!(dwFlags & CPD_COPYHIVEONLY)) { DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Calling ReconcileDirectory for all Directories"))); lpTemp = lpSrcDirs; while (lpTemp) { if (!ReconcileDirectory(lpTemp->szSrc, lpTemp->szDest, dwFlags, lpTemp->dwFileAttribs)) { dwErr1 = GetLastError(); DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to create the destination directory <%s>. Error = %d"), lpTemp->szDest, GetLastError())); if (!(dwFlags & CPD_IGNORECOPYERRORS)) { // // Show the error UI and bail out. // ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR, lpTemp->szSrc, lpTemp->szDest, GetErrString(dwErr1, szErr)); dwErr = dwErr1; goto Exit; } else { ReportError(hTokenUser, PI_NOUI | EVENT_WARNING_TYPE, 3, EVENT_COPYERROR, lpTemp->szSrc, lpTemp->szDest, GetErrString(dwErr1, szErr)); } } lpTemp = lpTemp->pNext; } DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Reconcile Directory Done for all Directories"))); // // Copy the files // if (dwFlags & CPD_SLOWCOPY) { // // Copy the files one at a time... // lpTemp = lpSrcFiles; while (lpTemp) { if (lpTemp->bHive) { // // Hive files have already been copied.. // lpTemp = lpTemp->pNext; continue; } if (!ReconcileFile (lpTemp->szSrc, lpTemp->szDest, dwFlags, &lpTemp->ftLastWrite, lpTemp->dwFileSize, FALSE)) { dwErr1 = GetLastError(); DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to copy the file <%s> to <%s> due to error = %d"), lpTemp->szSrc, lpTemp->szDest, GetLastError())); if (!(dwFlags & CPD_IGNORECOPYERRORS)) { // // Show the error UI and since the user picks to abort // then we leave now. // ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR, lpTemp->szSrc, lpTemp->szDest, GetErrString(dwErr1, szErr)); dwErr = dwErr1; goto Exit; } else { ReportError(hTokenUser, PI_NOUI | EVENT_WARNING_TYPE, 3, EVENT_COPYERROR, lpTemp->szSrc, lpTemp->szDest, GetErrString(dwErr1, szErr)); } } lpTemp = lpTemp->pNext; } } else { if (lpSrcFiles) { HANDLE hThreadToken=NULL; if (!OpenThreadToken (GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE, TRUE, &hThreadToken)) { DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Failed to get token with %d. This is ok if thread is not impersonating"), GetLastError())); } // // Multi-threaded copy // // Null sd, auto set, initially signalled, unnamed.. if (!(ThreadInfo.hCopyEvent = CreateEvent(NULL, FALSE, TRUE, NULL))) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: CreateEvent for CopyEvent failed with error %d"), GetLastError())); dwErr = GetLastError(); goto Exit; } ThreadInfo.dwFlags = dwFlags; ThreadInfo.lpSrcFiles = lpSrcFiles; ThreadInfo.hTokenUser = hTokenUser; // // Required for PrivCopyFileEx to work, threads should be created using the // process token // RevertToSelf(); // // Create the file copy threads // for (i = 0; i < NUM_COPY_THREADS; i++) { if (hThreads[dwThreadCount] = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) CopyFileFunc, (LPVOID) &ThreadInfo, CREATE_SUSPENDED, &dwThreadId)) { dwThreadCount++; } } // // Put the token back. // if (!SetThreadToken(NULL, hThreadToken)) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Impersonation failed with error %d"), GetLastError())); dwErr = GetLastError(); // terminate and close handles for all the threads for (i = 0; i < dwThreadCount; i++) { TerminateThread (hThreads[i], 1); CloseHandle (hThreads[i]); } if (hThreadToken) CloseHandle (hThreadToken); goto Exit; } for (i = 0; i < dwThreadCount; i++) { if (!SetThreadToken(&hThreads[i], hThreadToken) || !ResumeThread (hThreads[i])) { ThreadInfo.dwError = GetLastError(); TerminateThread(hThreads[i], 1); } } // // Wait for the threads to finish // if (WaitForMultipleObjects (dwThreadCount, hThreads, TRUE, INFINITE) == WAIT_FAILED) { ThreadInfo.dwError = GetLastError(); } // // Clean up // if (hThreadToken) CloseHandle (hThreadToken); for (i = 0; i < dwThreadCount; i++) { CloseHandle (hThreads[i]); } if (ThreadInfo.dwError) { dwErr = ThreadInfo.dwError; goto Exit; } } } } // // Restore the time on the directories to be the same as from Src. // This is required because the times on directory have been modified by // creation and deletion of files above. // DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Setting Directory TimeStamps all Directories"))); lpTemp = lpSrcDirs; while (lpTemp) { HANDLE hDestFile; SetFileAttributes (lpTemp->szDest, FILE_ATTRIBUTE_NORMAL); hDestFile = CreateFile(lpTemp->szDest, GENERIC_WRITE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hDestFile != INVALID_HANDLE_VALUE) { if (!SetFileTime(hDestFile, NULL, NULL, &(lpTemp->ftLastWrite))) { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to set the write time on the directory <%s>. Error = %d"), lpTemp->szDest, GetLastError())); } CloseHandle(hDestFile); } else { DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: CreateFile failed for directory %s with error %d"), lpTemp->szDest, GetLastError())); } SetFileAttributes (lpTemp->szDest, lpTemp->dwFileAttribs); lpTemp = lpTemp->pNext; } DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Set times on all directories"))); // // Success // bResult = TRUE; Exit: if (ThreadInfo.hCopyEvent) { CloseHandle (ThreadInfo.hCopyEvent); } if ( ThreadInfo.hDesktop ) { CloseDesktop( ThreadInfo.hDesktop ); } // // Free the memory allocated above // if (hTokenUser) { CloseHandle(hTokenUser); } if (lpSrcDir) { LocalFree(lpSrcDir); } if (lpDestDir) { LocalFree(lpDestDir); } if (lpExcludeListSrc) { LocalFree (lpExcludeListSrc); } if (lpExcludeListDest) { LocalFree (lpExcludeListDest); } if (lpSrcFiles) { FreeFileInfoList(lpSrcFiles); } if (lpDestFiles) { FreeFileInfoList(lpDestFiles); } if (lpSrcDirs) { FreeFileInfoList(lpSrcDirs); } if (lpDestDirs) { FreeFileInfoList(lpDestDirs); } SetLastError(dwErr); // // Verbose output // DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Leaving with a return value of %d"), bResult)); return bResult; } //************************************************************* // // RecurseDirectory() // // Purpose: Recurses through the subdirectory coping files. // // Parameters: hTokenUser - User's token // lpSrcDir - Source directory working buffer // lpDestDir - Destination directory working buffer // dwFlags - dwFlags // llSrcDirs - Link list of directories // llSrcFiles - Link list of files // bSkipNtUser - Skip ntuser.* files // lpExcludeList - List of directories to exclude // bRecurseDest - The destination Dir is being recursed // // Return: TRUE if successful // FALSE if an error occurs // // Comments: 1) The source and dest directories will already have // the trailing backslash when entering this function. // 2) The current working directory is the source directory. // // // Notes: // CPD_SYSTEMDIRSONLY Do not keep track of anything unless the dir is // marked with system bit. // CPD_SYSTEMFILES Only Systemfiles // CPD_NONENCRYPTEDONLY Only Non EncryptedFile/Directory. // // History: Date Author Comment // 5/25/95 ericflo Created // //************************************************************* BOOL RecurseDirectory (HANDLE hTokenUser, LPTSTR lpSrcDir, DWORD cchSrcDir, LPTSTR lpDestDir, DWORD cchDestDir, DWORD dwFlags, LPFILEINFO *llSrcDirs, LPFILEINFO *llSrcFiles, BOOL bMarkNtUser, LPTSTR lpExcludeList, BOOL bRecurseDest) { HANDLE hFile = INVALID_HANDLE_VALUE; LPTSTR lpSrcEnd, lpDestEnd, lpTemp; BOOL bResult = FALSE; BOOL bSkip; DWORD dwErr, dwErr1 = 0; LPTSTR szErr = NULL; WIN32_FIND_DATA* pfd = NULL; BOOL bHive; DWORD cchSrcEnd, cchDestEnd; HRESULT hr; dwErr = GetLastError(); // // Setup the ending pointers // lpSrcEnd = CheckSlashEx (lpSrcDir, cchSrcDir, &cchSrcEnd); if (!lpSrcEnd) { dwErr = ERROR_INSUFFICIENT_BUFFER; DebugMsg((DM_WARNING, TEXT("RecurseDirectory: failed to append slash to src dir."))); goto RecurseDir_Exit; } lpDestEnd = CheckSlashEx (lpDestDir, cchDestDir, &cchDestEnd); if (!lpDestEnd) { dwErr = ERROR_INSUFFICIENT_BUFFER; DebugMsg((DM_WARNING, TEXT("RecurseDirectory: failed to append slash to dest dir."))); goto RecurseDir_Exit; } // // Append *.* to the source directory // hr = StringCchCopy (lpSrcEnd, cchSrcEnd, c_szStarDotStar); if (FAILED(hr)) { dwErr = HRESULT_CODE(hr); DebugMsg((DM_WARNING, TEXT("RecurseDirectory: failed to append '*.*' to src dir."))); goto RecurseDir_Exit; } // // Allocate fd. Since this is a recursive func, we don't want to use a lot of stack space // pfd = (WIN32_FIND_DATA *) LocalAlloc (LPTR, sizeof(WIN32_FIND_DATA)); if (!pfd) { dwErr = GetLastError(); DebugMsg((DM_WARNING, TEXT("RecurseDirectory: failed to allocate WIN32_FIND_DATA."))); goto RecurseDir_Exit; } // // Allocate szErr. // szErr = (LPTSTR) LocalAlloc (LPTR, MAX_PATH * sizeof(TCHAR)); if (!szErr) { dwErr = GetLastError(); DebugMsg((DM_WARNING, TEXT("RecurseDirectory: failed to allocate szErr."))); goto RecurseDir_Exit; } // // Search through the source directory // hFile = FindFirstFile(lpSrcDir, pfd); if (hFile == INVALID_HANDLE_VALUE) { if ( (GetLastError() == ERROR_FILE_NOT_FOUND) || (GetLastError() == ERROR_PATH_NOT_FOUND) ) { bResult = TRUE; } else { dwErr1 = GetLastError(); DebugMsg((DM_WARNING, TEXT("RecurseDirectory: FindFirstFile failed. Error = %d"), dwErr1)); *lpSrcEnd = TEXT('\0'); *lpDestEnd = TEXT('\0'); if (!(dwFlags & CPD_IGNORECOPYERRORS)) { if (!bRecurseDest) { ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR, lpSrcDir, lpDestDir, GetErrString(dwErr1, szErr)); } else { ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR, lpDestDir, lpSrcDir, GetErrString(dwErr1, szErr)); } dwErr = dwErr1; bResult = FALSE; } else // Ignore copy error, so we just set return value to TRUE { bResult = TRUE; } } goto RecurseDir_Exit; } do { bHive = FALSE; // // Check whether we have enough space in our buffers // *lpSrcEnd = TEXT('\0'); *lpDestEnd = TEXT('\0'); if ((lstrlen(lpSrcDir)+lstrlen(pfd->cFileName) >= MAX_PATH) || (lstrlen(lpDestDir)+lstrlen(pfd->cFileName) >= MAX_PATH)) { LPTSTR lpErrSrc=NULL, lpErrDest=NULL; DWORD cchErrSrc, cchErrDest; BOOL bRet; DebugMsg((DM_WARNING, TEXT("RecurseDirectory: %s is too long. src = %s, dest = %s"), pfd->cFileName, lpSrcDir, lpDestDir)); if (dwFlags & CPD_IGNORELONGFILENAMES) continue; // // Allocate a buffer to show the file names // cchErrSrc = lstrlen(lpSrcDir)+lstrlen(pfd->cFileName)+1; cchErrDest = lstrlen(lpDestDir)+lstrlen(pfd->cFileName)+1; lpErrSrc = LocalAlloc(LPTR, cchErrSrc * sizeof(TCHAR)); lpErrDest = LocalAlloc(LPTR, cchErrDest * sizeof(TCHAR)); // // Show the UI // if ((!lpErrSrc) || (!lpErrDest)) { if (!bRecurseDest) { ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR, lpSrcDir, lpDestDir, GetErrString(ERROR_FILENAME_EXCED_RANGE, szErr)); } else { ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR, lpDestDir, lpSrcDir, GetErrString(ERROR_FILENAME_EXCED_RANGE, szErr)); } } else { StringCchCopy(lpErrSrc, cchErrSrc, lpSrcDir); StringCchCat(lpErrSrc, cchErrSrc, pfd->cFileName); StringCchCopy(lpErrDest, cchErrDest, lpDestDir); StringCchCat(lpErrDest, cchErrDest, pfd->cFileName); if (!bRecurseDest) { ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR, lpErrSrc, lpErrDest, GetErrString(ERROR_FILENAME_EXCED_RANGE, szErr)); } else { ReportError(hTokenUser, ((dwFlags & CPD_NOERRORUI)? PI_NOUI:0), 3, EVENT_COPYERROR, lpErrDest, lpErrSrc, GetErrString(ERROR_FILENAME_EXCED_RANGE, szErr)); } } if (lpErrSrc) LocalFree(lpErrSrc); if (lpErrDest) LocalFree(lpErrDest); // // Set the error and quit. // dwErr = ERROR_FILENAME_EXCED_RANGE; bResult = FALSE; goto RecurseDir_Exit; } // // Append the file / directory name to the working buffers // StringCchCopy (lpSrcEnd, cchSrcEnd, pfd->cFileName); StringCchCopy (lpDestEnd, cchDestEnd, pfd->cFileName); if (pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // // Check for "." and ".." // if (!lstrcmpi(pfd->cFileName, c_szDot)) { continue; } if (!lstrcmpi(pfd->cFileName, c_szDotDot)) { continue; } // // Check for reparse point // if (pfd->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { DebugMsg((DM_WARNING, TEXT("RecurseDirectory: Found a reparse point <%s>, skip it!"), lpSrcDir)); continue; } // // Check if this directory should be excluded // if (lpExcludeList) { bSkip = FALSE; lpTemp = lpExcludeList; while (*lpTemp) { if (lstrcmpi (lpTemp, lpSrcDir) == 0) { bSkip = TRUE; break; } lpTemp += lstrlen (lpTemp) + 1; } if (bSkip) { DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> due to exclusion list."), lpSrcDir)); continue; } } // // Found a directory. // // 1) Change into that subdirectory on the source drive. // 2) Recurse down that tree. // 3) Back up one level. // // // Add to the list of directories // if (dwFlags & CPD_SYSTEMDIRSONLY) { // // if it is encrypted, don't recurse into it // if (!(pfd->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) { DWORD dwNewFlags = dwFlags; // // Add to the list of directories only if marked as system, o/w just recurse through // if (pfd->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) { if (!AddFileInfoNode (llSrcDirs, lpSrcDir, lpDestDir, &pfd->ftLastWriteTime, &pfd->ftCreationTime, 0, pfd->dwFileAttributes, bHive)) { DebugMsg((DM_WARNING, TEXT("RecurseDirectory: AddFileInfoNode failed"))); dwErr = GetLastError(); goto RecurseDir_Exit; } dwNewFlags ^= CPD_SYSTEMDIRSONLY; DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Adding %s to the list of directories because system bit is on"), lpSrcDir)); } // // Recurse the subdirectory // if (!RecurseDirectory(hTokenUser, lpSrcDir, cchSrcDir, lpDestDir, cchDestDir, dwNewFlags, llSrcDirs, llSrcFiles, FALSE, lpExcludeList, bRecurseDest)) { DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: RecurseDirectory returned FALSE"))); bResult = FALSE; dwErr = GetLastError(); goto RecurseDir_Exit; } } else { DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> since the encrypted attribute is set."), lpSrcDir)); } continue; } // // Popup time // if (dwFlags & CPD_NONENCRYPTEDONLY) { if (pfd->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Detected Encrypted file %s, Aborting.."), lpSrcDir)); dwErr = ERROR_FILE_ENCRYPTED; bResult = FALSE; goto RecurseDir_Exit; } } // // Ignore encrypted file // if (dwFlags & CPD_IGNOREENCRYPTEDFILES) { if (pfd->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> since the encrypted attribute is set..."), lpSrcDir)); continue; } } // // Add it to the list // if (!AddFileInfoNode (llSrcDirs, lpSrcDir, lpDestDir, &pfd->ftLastWriteTime, &pfd->ftCreationTime, 0, pfd->dwFileAttributes, bHive)) { DebugMsg((DM_WARNING, TEXT("RecurseDirectory: AddFileInfoNode failed"))); dwErr = GetLastError(); goto RecurseDir_Exit; } // // Recurse the subdirectory // if (!RecurseDirectory(hTokenUser, lpSrcDir, cchSrcDir, lpDestDir, cchDestDir, dwFlags, llSrcDirs, llSrcFiles, FALSE, lpExcludeList, bRecurseDest)) { DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: RecurseDirectory returned FALSE"))); bResult = FALSE; dwErr = GetLastError(); goto RecurseDir_Exit; } DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Adding %s to the list of directories"), lpSrcDir)); } else { // // if the directories only bit is set, don't copy anything else // if (dwFlags & CPD_SYSTEMDIRSONLY) { DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> since the system directories only attribute is set."), lpSrcDir)); continue; } // // If the filename found starts with "ntuser", then ignore // it because the hive will be copied below (if appropriate). // if (bMarkNtUser && lstrlen(pfd->cFileName) >= 6) { if (CompareString (LOCALE_INVARIANT, NORM_IGNORECASE, pfd->cFileName, 6, TEXT("ntuser"), 6) == CSTR_EQUAL) { bHive = TRUE; } } // // Check if this file should be excluded // if (dwFlags & CPD_SYSTEMFILES) { if (!(pfd->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) { DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> since the system attribute is not set."), lpSrcDir)); continue; } } // // if it is systemfile, it can not be encrypted. // if (dwFlags & CPD_NONENCRYPTEDONLY) { if (pfd->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Detected Encrypted file %s, Aborting..."), lpSrcDir)); dwErr = ERROR_FILE_ENCRYPTED; bResult = FALSE; goto RecurseDir_Exit; } } if (dwFlags & CPD_IGNOREENCRYPTEDFILES) { if (pfd->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { DebugMsg((DM_VERBOSE, TEXT("RecurseDirectory: Skipping <%s> since the encrypted attribute is set."), lpSrcDir)); continue; } } // // We found a file. Add it to the list. // if (!AddFileInfoNode (llSrcFiles, lpSrcDir, lpDestDir, &pfd->ftLastWriteTime, &pfd->ftCreationTime, pfd->nFileSizeLow, pfd->dwFileAttributes, bHive)) { DebugMsg((DM_WARNING, TEXT("RecurseDirectory: AddFileInfoNode failed"))); dwErr = GetLastError(); goto RecurseDir_Exit; } } // // Find the next entry // } while (FindNextFile(hFile, pfd)); bResult = TRUE; RecurseDir_Exit: if (pfd) LocalFree(pfd); if (szErr) LocalFree(szErr); // // Remove the file / directory name appended above // if (lpSrcEnd) *lpSrcEnd = TEXT('\0'); if (lpDestEnd) *lpDestEnd = TEXT('\0'); // // Close the search handle // if (hFile != INVALID_HANDLE_VALUE) { FindClose(hFile); } SetLastError(dwErr); return bResult; } //************************************************************* // // CopyProgressRoutine() // // Purpose: Callback function for CopyFileEx // // Parameters: See doc's. // // Return: PROGRESS_CONTINUE // //************************************************************* DWORD WINAPI CopyProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData) { switch (dwCallbackReason) { case PRIVCALLBACK_ENCRYPTION_FAILED: case PRIVCALLBACK_COMPRESSION_FAILED: case PRIVCALLBACK_SPARSE_FAILED: case PRIVCALLBACK_OWNER_GROUP_FAILED: case PRIVCALLBACK_DACL_ACCESS_DENIED: case PRIVCALLBACK_SACL_ACCESS_DENIED: case PRIVCALLBACK_OWNER_GROUP_ACCESS_DENIED: return PROGRESS_CANCEL; default: return PROGRESS_CONTINUE; //all other conditions can be safely ignored } } //************************************************************* // // ReconcileDirectory() // // Purpose: Compares the source and destination file. // If the source is newer, then it is copied // over the destination. // // Parameters: lpSrcDir - source filename // lpDestDir - destination filename // dwFlags - flags // dwSrcAttribs Source Attributes for decompression, // decryption later on. // // // Return: 1 if successful (no file copied) // 0 if an error occurs // // Comments: // // History: Date Author Comment // 2/26/99 ushaji Created // //************************************************************* BOOL ReconcileDirectory(LPTSTR lpSrcDir, LPTSTR lpDestDir, DWORD dwFlags, DWORD dwSrcAttribs) { DWORD dwCopyFlags=0, dwErr; BOOL bCancel = FALSE; // // Clear any existing attributes // SetFileAttributes (lpDestDir, FILE_ATTRIBUTE_NORMAL); if (!CreateNestedDirectory(lpDestDir, NULL)) { DebugMsg((DM_WARNING, TEXT("ReconcileDirectory: Failed to create the destination directory <%s>. Error = %d"), lpDestDir, GetLastError())); return FALSE; } // // Set up the copy flags to copy the encryption/compression on dirs over. // if (!(dwFlags & CPD_IGNORESECURITY)) dwCopyFlags = PRIVCOPY_FILE_METADATA | PRIVCOPY_FILE_SKIP_DACL; dwCopyFlags |= PRIVCOPY_FILE_DIRECTORY | PRIVCOPY_FILE_SUPERSEDE; if (!PrivCopyFileExW(lpSrcDir, lpDestDir, (LPPROGRESS_ROUTINE) CopyProgressRoutine, NULL, &bCancel, dwCopyFlags)) { dwErr = GetLastError(); DebugMsg((DM_WARNING, TEXT("ReconcileDirectory: Failed to copy over the attributes src <%s> to destination directory <%s>. Error = %d"), lpSrcDir, lpDestDir, dwErr)); RemoveDirectory(lpDestDir); SetLastError(dwErr); return FALSE; } return TRUE; } //************************************************************* // // ReconcileFile() // // Purpose: Compares the source and destination file. // If the source is newer, then it is copied // over the destination. // // Parameters: lpSrcFile - source filename // lpDestFile - destination filename // dwFlags - flags // ftSrcTime - Src file time (can be NULL) // dwFileSize - File size // bHiveFile - Flag to indicate hive file // // // Return: 1 if successful (no file copied) // 2 if successful (and a file was copied) // 0 if an error occurs // // Comments: // // History: Date Author Comment // 5/25/95 ericflo Created // 7/20/00 santanuc added flag bHiveFile // //************************************************************* INT ReconcileFile (LPCTSTR lpSrcFile, LPCTSTR lpDestFile, DWORD dwFlags, LPFILETIME ftSrcTime, DWORD dwFileSize, BOOL bHiveFile) { WIN32_FILE_ATTRIBUTE_DATA fad; FILETIME ftWriteSrc, ftWriteDest; INT iCopyFile = 0; DWORD dwErr = ERROR_SUCCESS, dwErr1 = 0; // // If the flags have CPD_FORCECOPY, then skip to the // copy file call without checking the timestamps. // if (!(dwFlags & CPD_FORCECOPY)) { // // If we were given a source file time, use that // if (ftSrcTime) { ftWriteSrc.dwLowDateTime = ftSrcTime->dwLowDateTime; ftWriteSrc.dwHighDateTime = ftSrcTime->dwHighDateTime; } else { // // Query for the source file time // if (!GetFileAttributesEx (lpSrcFile, GetFileExInfoStandard, &fad)) { dwErr = GetLastError(); DebugMsg((DM_WARNING, TEXT("ReconcileFile: GetFileAttributes on the source failed with error = %d"), dwErr)); goto Exit; } ftWriteSrc.dwLowDateTime = fad.ftLastWriteTime.dwLowDateTime; ftWriteSrc.dwHighDateTime = fad.ftLastWriteTime.dwHighDateTime; } // // Attempt to open the destination file // if (!GetFileAttributesEx (lpDestFile, GetFileExInfoStandard, &fad)) { DWORD dwError; // // GetFileAttributesEx failed to query the destination // file. If the last error is file not found // then we automaticaly will copy the file. // dwError = GetLastError(); if (dwError == ERROR_FILE_NOT_FOUND) { iCopyFile = 1; } else { // // GetFileAttributesEx failed with some other error // DebugMsg((DM_WARNING, TEXT("ReconcileFile: GetFileAttributesEx on the destination failed with error = %d"), dwError)); dwErr = dwError; goto Exit; } } else { ftWriteDest.dwLowDateTime = fad.ftLastWriteTime.dwLowDateTime; ftWriteDest.dwHighDateTime = fad.ftLastWriteTime.dwHighDateTime; } } else { // // The CPD_FORCECOPY flag is turned on, set iCopyFile to 1. // iCopyFile = 1; } // // If iCopyFile is still zero, then we need to compare // the last write time stamps. // if (!iCopyFile) { LONG lResult; // // If the source is later than the destination // we need to copy the file. // lResult = CompareFileTime(&ftWriteSrc, &ftWriteDest); if (lResult == 1) { iCopyFile = 1; } if ( (dwFlags & CPD_COPYIFDIFFERENT) && (lResult == -1) ) { iCopyFile = 1; } } // // Copy the file if appropriate // if (iCopyFile) { BOOL bCancel = FALSE; TCHAR szTempFile[MAX_PATH]; TCHAR szTempDir[MAX_PATH]; LPTSTR lpTemp; DWORD dwCopyFlags; // // Clear any existing attributes // SetFileAttributes (lpDestFile, FILE_ATTRIBUTE_NORMAL); if (!(dwFlags & CPD_IGNORESECURITY)) dwCopyFlags = PRIVCOPY_FILE_METADATA | PRIVCOPY_FILE_SKIP_DACL; else dwCopyFlags = 0; dwCopyFlags |= PRIVCOPY_FILE_SUPERSEDE; // // Figure out what the destination directory is // StringCchCopy (szTempDir, ARRAYSIZE(szTempDir), lpDestFile); lpTemp = szTempDir + lstrlen (szTempDir); while ((lpTemp > szTempDir) && (*lpTemp != TEXT('\\'))) { lpTemp--; } if (lpTemp == szTempDir) { StringCchCopy (szTempDir, ARRAYSIZE(szTempDir), TEXT(".")); } else { *lpTemp = TEXT('\0'); } // // Generate a temporary file name // if (GetTempFileName (szTempDir, TEXT("prf"), 0, szTempFile)) { // // Copy the file to the temp file name // if (PrivCopyFileExW(lpSrcFile, szTempFile, (LPPROGRESS_ROUTINE) CopyProgressRoutine, NULL, &bCancel, dwCopyFlags)) { // If it is hive file then open the temporary file, flush and close it to make it more transacted if (bHiveFile) { HANDLE hTempFile; hTempFile = CreateFile(szTempFile, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if ( hTempFile != INVALID_HANDLE_VALUE ) { if ( !FlushFileBuffers(hTempFile) ) DebugMsg((DM_WARNING, TEXT("ReconcileFile: Unable to flush temporary file"))); if ( !CloseHandle(hTempFile) ) DebugMsg((DM_WARNING, TEXT("ReconcileFile: Unable to close temporary file handle"))); } else DebugMsg((DM_WARNING, TEXT("ReconcileFile: Unable to open temporary file"))); } // // Delete the original file // if (!DeleteFile (lpDestFile)) { if (GetLastError() != ERROR_FILE_NOT_FOUND) { dwErr1 = GetLastError(); DebugMsg((DM_WARNING, TEXT("ReconcileFile: Failed to delete file <%s> with error = %d"), lpDestFile, dwErr1)); DeleteFile(szTempFile); goto CopyError; } } // // Rename the temp file to the original file name // if (!MoveFile (szTempFile, lpDestFile)) { DWORD dwError = ERROR_SUCCESS; //isolate MoveFile error from other below dwErr1 = GetLastError(); // // If we get access denied, let's try to remove the READ ONLY attribute (can't rename files // with +r attribute on a Netware Server) from the temp file, do the rename and restore the // attributes // if ( dwErr1 == ERROR_ACCESS_DENIED ) { if (!GetFileAttributesEx (szTempFile, GetFileExInfoStandard, &fad)) { dwError = GetLastError(); DebugMsg((DM_WARNING, TEXT("ReconcileFile: GetFileAttributes on file <%s> failed with error = %d\n"), szTempFile,dwError)); } else { if ( fad.dwFileAttributes & FILE_ATTRIBUTE_READONLY ) { dwErr1 = ERROR_SUCCESS ; if (!SetFileAttributes (szTempFile, fad.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) { dwError = GetLastError(); DebugMsg((DM_WARNING, TEXT("ReconcileFile: SetFileAttributes on file <%s> failed with error = %d\n"), szTempFile,dwError)); } else { if (!MoveFile (szTempFile,lpDestFile)) { // Debug message displayed below dwErr1 = GetLastError(); } else { if ( !SetFileAttributes (lpDestFile,fad.dwFileAttributes) ) { dwError = GetLastError(); DebugMsg((DM_WARNING, TEXT("ReconcileFile: SetFileAttributes on file <%s> failed with error = %d\n"), szTempFile,dwError)); } } } } } } // End of ERROR_ACCESS_DENIED test if (dwErr1 != ERROR_SUCCESS || dwError != ERROR_SUCCESS) { DebugMsg((DM_WARNING, TEXT("ReconcileFile: Failed to rename file <%s> to <%s> with error = %d"), szTempFile, lpDestFile, dwErr1)); // do not remove it in this case. goto CopyError; } } DebugMsg((DM_VERBOSE, TEXT("ReconcileFile: %s ==> %s [OK]"), lpSrcFile, lpDestFile)); iCopyFile = 2; } else { dwErr1 = GetLastError(); DeleteFile(szTempFile); DebugMsg((DM_WARNING, TEXT("ReconcileFile: %s ==> %s [FAILED!!!]"), lpSrcFile, szTempFile)); DebugMsg((DM_WARNING, TEXT("ReconcileFile: CopyFile failed with error = %d"), dwErr1)); goto CopyError; } } else { dwErr1 = GetLastError(); DebugMsg((DM_WARNING, TEXT("ReconcileFile: GetTempFileName failed with %d"), dwErr1)); goto CopyError; } } else { // // No need to copy the file since the time stamps are the same // Set iCopyFile to 1 so the return value is success without // copying a file. // iCopyFile = 1; } goto Exit; CopyError: iCopyFile = 0; dwErr = dwErr1; Exit: SetLastError(dwErr); return iCopyFile; } //************************************************************* // // AddFileInfoNode() // // Purpose: Adds a node to the linklist of files // // Parameters: lpFileInfo - Link list to add to // lpSrcFile - Source filename // lpDestFile - Destination filename // ftLastWrite - Last write time stamp // ftCreationTime - File creation time // dwFileSize - Size of the file // dwFileAttribs - File attributes // // Return: TRUE if successful // FALSE if an error occurs // // Comments: // // History: Date Author Comment // 9/28/95 ericflo Created // //************************************************************* BOOL AddFileInfoNode (LPFILEINFO *lpFileInfo, LPTSTR lpSrcFile, LPTSTR lpDestFile, LPFILETIME ftLastWrite, LPFILETIME ftCreationTime, DWORD dwFileSize, DWORD dwFileAttribs, BOOL bHive) { LPFILEINFO lpNode; lpNode = (LPFILEINFO) LocalAlloc(LPTR, sizeof(FILEINFO)); if (!lpNode) { return FALSE; } StringCchCopy (lpNode->szSrc, MAX_PATH, lpSrcFile); StringCchCopy (lpNode->szDest, MAX_PATH, lpDestFile); lpNode->ftLastWrite.dwLowDateTime = ftLastWrite->dwLowDateTime; lpNode->ftLastWrite.dwHighDateTime = ftLastWrite->dwHighDateTime; lpNode->ftCreationTime.dwLowDateTime = ftCreationTime->dwLowDateTime; lpNode->ftCreationTime.dwHighDateTime = ftCreationTime->dwHighDateTime; lpNode->dwFileSize = dwFileSize; lpNode->bHive = bHive; lpNode->dwFileAttribs = (dwFileAttribs & ~FILE_ATTRIBUTE_DIRECTORY); lpNode->pNext = *lpFileInfo; *lpFileInfo = lpNode; return TRUE; } //************************************************************* // // FreeFileInfoList() // // Purpose: Free's a file info link list // // Parameters: lpFileInfo - List to be freed // // Return: TRUE if successful // FALSE if an error occurs // // Comments: // // History: Date Author Comment // 9/28/95 ericflo Created // //************************************************************* BOOL FreeFileInfoList (LPFILEINFO lpFileInfo) { LPFILEINFO lpNext; if (!lpFileInfo) { return TRUE; } lpNext = lpFileInfo->pNext; while (lpFileInfo) { LocalFree (lpFileInfo); lpFileInfo = lpNext; if (lpFileInfo) { lpNext = lpFileInfo->pNext; } } return TRUE; } //************************************************************* // // SyncItems() // // Purpose: Removes unnecessary items from the destination // directory tree // // Parameters: lpSrcItems - Link list of source items // lpDestItems - Link list of dest items // bFile - File or directory list // // Return: TRUE if successful // FALSE if an error occurs // Comments: // // History: Date Author Comment // 9/28/95 ericflo Created // //************************************************************* BOOL SyncItems (LPFILEINFO lpSrcItems, LPFILEINFO lpDestItems, BOOL bFile, LPFILETIME ftDelRefTime) { LPFILEINFO lpTempSrc, lpTempDest; // // Check for NULL pointers // #ifdef DBG if (ftDelRefTime) { SYSTEMTIME SystemTime; FileTimeToSystemTime(ftDelRefTime, &SystemTime); DebugMsg((DM_VERBOSE, TEXT("SyncItems: DelRefTime. Year: %d, Month %d, Day %d, Hour %d, Minute %d"), SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute)); } #endif if (!lpSrcItems || !lpDestItems) { return TRUE; } // // Loop through everyitem in lpDestItems to see if it // is in lpSrcItems. If not, delete it. // lpTempDest = lpDestItems; while (lpTempDest) { lpTempSrc = lpSrcItems; while (lpTempSrc) { if (lstrcmpi(lpTempDest->szSrc, lpTempSrc->szDest) == 0) { break; } lpTempSrc = lpTempSrc->pNext; } // // If lpTempSrc is NULL, then this file / directory is a candidate // for being deleted // if (!lpTempSrc) { BOOL bDelete = TRUE; // // If a delete reference time was offered, compare the // source time with the ref time and only delete files // which have a source time older than the ref time // if (ftDelRefTime) { if (CompareFileTime (&lpTempDest->ftLastWrite, ftDelRefTime) == 1) { bDelete = FALSE; } else if (CompareFileTime (&lpTempDest->ftCreationTime, ftDelRefTime) == 1) { bDelete = FALSE; } } if (bDelete) { // // Delete the file / directory // DebugMsg((DM_VERBOSE, TEXT("SyncItems: removing <%s>"), lpTempDest->szSrc)); if (bFile) { SetFileAttributes(lpTempDest->szSrc, FILE_ATTRIBUTE_NORMAL); if (!DeleteFile (lpTempDest->szSrc)) { DebugMsg((DM_WARNING, TEXT("SyncItems: Failed to delete <%s>. Error = %d."), lpTempDest->szSrc, GetLastError())); } } else { SetFileAttributes(lpTempDest->szSrc, FILE_ATTRIBUTE_NORMAL); if (!RemoveDirectory (lpTempDest->szSrc)) { DebugMsg((DM_WARNING, TEXT("SyncItems: Failed to remove <%s>. Error = %d"), lpTempDest->szSrc, GetLastError())); } } } else { DebugMsg((DM_VERBOSE, TEXT("SyncItems: New file or directory <%s> in destination since this profile was loaded. This will NOT be deleted."), lpTempDest->szSrc)); #ifdef DBG { SYSTEMTIME SystemTime; FileTimeToSystemTime(&lpTempDest->ftLastWrite, &SystemTime); DebugMsg((DM_VERBOSE, TEXT("SyncItems: File WriteTime. Year: %d, Month %d, Day %d, Hour %d, Minute %d"), SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute)); FileTimeToSystemTime(&lpTempDest->ftCreationTime, &SystemTime); DebugMsg((DM_VERBOSE, TEXT("SyncItems: File CreationTime. Year: %d, Month %d, Day %d, Hour %d, Minute %d"), SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute)); } #endif } } lpTempDest = lpTempDest->pNext; } return TRUE; } //************************************************************* // // CopyFileFunc() // // Purpose: Copies files // // Parameters: lpThreadInfo - Thread information // // Return: void // // Comments: // // History: Date Author Comment // 2/23/96 ericflo Created // //************************************************************* void CopyFileFunc (LPTHREADINFO lpThreadInfo) { HANDLE hInstDll; LPFILEINFO lpSrcFile; BOOL bRetVal = TRUE; DWORD dwError; TCHAR szErr[MAX_PATH]; hInstDll = LoadLibrary (TEXT("userenv.dll")); SetThreadDesktop (lpThreadInfo->hDesktop); while (TRUE) { if (lpThreadInfo->dwError) { break; } // // Query for the next file to copy.. // ignore the hive file since it is already copied.. // WaitForSingleObject(lpThreadInfo->hCopyEvent, INFINITE); do { lpSrcFile = lpThreadInfo->lpSrcFiles; if (lpSrcFile) lpThreadInfo->lpSrcFiles = lpThreadInfo->lpSrcFiles->pNext; } while (lpSrcFile && (lpSrcFile->bHive)); SetEvent(lpThreadInfo->hCopyEvent); // // If NULL, then we're finished. // if (!lpSrcFile || lpThreadInfo->dwError) { break; } // // Copy the file // if (!ReconcileFile (lpSrcFile->szSrc, lpSrcFile->szDest, lpThreadInfo->dwFlags, &lpSrcFile->ftLastWrite, lpSrcFile->dwFileSize, FALSE)) { if (!(lpThreadInfo->dwFlags & CPD_IGNORECOPYERRORS)) { WaitForSingleObject(lpThreadInfo->hCopyEvent, INFINITE); if (!(lpThreadInfo->dwError)) { dwError = GetLastError(); ReportError(lpThreadInfo->hTokenUser, ((lpThreadInfo->dwFlags & CPD_NOERRORUI) ? PI_NOUI:0), 3, EVENT_COPYERROR, lpSrcFile->szSrc, lpSrcFile->szDest, GetErrString(dwError, szErr)); lpThreadInfo->dwError = dwError; bRetVal = FALSE; } SetEvent(lpThreadInfo->hCopyEvent); break; } else { dwError = GetLastError(); ReportError(lpThreadInfo->hTokenUser, PI_NOUI | EVENT_WARNING_TYPE, 3, EVENT_COPYERROR, lpSrcFile->szSrc, lpSrcFile->szDest, GetErrString(dwError, szErr)); } } } // // Clean up // if (hInstDll) { FreeLibraryAndExitThread(hInstDll, bRetVal); } else { ExitThread (bRetVal); } } //************************************************************* // // ConvertExclusionList() // // Purpose: Converts the semi-colon profile relative exclusion // list to fully qualified null terminated exclusion // list // // Parameters: lpSourceDir - Profile root directory // lpExclusionList - List of directories to exclude // // Return: List if successful // NULL if an error occurs // //************************************************************* LPTSTR ConvertExclusionList (LPCTSTR lpSourceDir, LPCTSTR lpExclusionList) { LPTSTR lpExcludeList = NULL, lpInsert, lpEnd, lpTempList; LPCTSTR lpTemp, lpDir; TCHAR szTemp[MAX_PATH]; DWORD dwSize = 2; // double null terminator DWORD dwStrLen; HRESULT hr = E_FAIL; DWORD cchEnd; // // Setup a temp buffer to work with // hr = StringCchCopy (szTemp, ARRAYSIZE(szTemp), lpSourceDir); if (FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to copy src dir."))); goto Exit; } lpEnd = CheckSlashEx (szTemp, ARRAYSIZE(szTemp), &cchEnd); if (!lpEnd) { hr = E_FAIL; DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to append slash."))); goto Exit; } // // Loop through the list // lpTemp = lpDir = lpExclusionList; while (*lpTemp) { // // Look for the semicolon separator // while (*lpTemp && ((*lpTemp) != TEXT(';'))) { lpTemp++; } // // Remove any leading spaces // while (*lpDir && ((*lpDir) == TEXT(' '))) { lpDir++; } // // Skip empty entry // if (lpTemp != lpDir) { // // Note: // Empty Spaces will not make the whole profile dir excluded // in RecurseDirectory. // // // Put the directory name on the temp buffer, not include ';' // *lpEnd = TEXT('\0'); hr = StringCchCatN (lpEnd, cchEnd, lpDir, (int)(lpTemp - lpDir)); if (FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to append new list item."))); goto Exit; } DebugMsg((DM_VERBOSE, TEXT("ConvertExclusionList: Adding %s to ExclusionList"), szTemp)); // // Add the string to the exclusion list // if (lpExcludeList) { dwStrLen = lstrlen (szTemp) + 1; dwSize += dwStrLen; lpTempList = LocalReAlloc (lpExcludeList, dwSize * sizeof(TCHAR), LMEM_MOVEABLE | LMEM_ZEROINIT); if (!lpTempList) { hr = E_FAIL; DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to realloc memory with %d"), GetLastError())); goto Exit; } lpExcludeList = lpTempList; lpInsert = lpExcludeList + dwSize - dwStrLen - 1; StringCchCopy (lpInsert, dwStrLen, szTemp); } else { dwSize += lstrlen (szTemp); lpExcludeList = LocalAlloc (LPTR, dwSize * sizeof(TCHAR)); if (!lpExcludeList) { hr = E_FAIL; DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to alloc memory with %d"), GetLastError())); goto Exit; } StringCchCopy (lpExcludeList, dwSize, szTemp); lpExcludeList[dwSize - 1] = TEXT('\0'); // The last null terminator } } // // If we are at the end of the exclusion list, we're done // if (!(*lpTemp)) { break; } // // Prep for the next entry // lpTemp++; lpDir = lpTemp; } hr = S_OK; Exit: if (FAILED(hr)) { if (lpExcludeList) { LocalFree(lpExcludeList); lpExcludeList = NULL; } } return lpExcludeList; } //************************************************************* // // FindDirectorySize() // // Purpose: Takes the Directory Name and the list of files // returned by RecurseDir and gets the total size. // // Parameters: lpDir - '\' terminated Source Directory // lpFiles - List of files to be copied // dwFlags - Flags // pdwLargestHiveFile - optional parameter that // returns the largest hive // file size. // pdwTotalFiles - the size of the directory // // Return: Win32 error code. // //************************************************************* DWORD FindDirectorySize(LPTSTR lpDir, LPFILEINFO lpFiles, DWORD dwFlags, DWORD* pdwLargestHiveFile, DWORD* pdwTotalFiles) { LPFILEINFO lpTemp = NULL; if(pdwLargestHiveFile) { *pdwLargestHiveFile = 0; } if(!pdwTotalFiles) { SetLastError(ERROR_INVALID_PARAMETER); return ERROR_INVALID_PARAMETER; } else { *pdwTotalFiles = 0; } lpTemp = lpFiles; while (lpTemp) { if (lpTemp->bHive) { if (!(dwFlags & CPD_IGNOREHIVE)) { if(pdwLargestHiveFile && (*pdwLargestHiveFile < lpTemp->dwFileSize)) { *pdwLargestHiveFile = lpTemp->dwFileSize; } *pdwTotalFiles += lpTemp->dwFileSize; } } else { *pdwTotalFiles += lpTemp->dwFileSize; } lpTemp = lpTemp->pNext; } return ERROR_SUCCESS; } //************************************************************* // // FindTotalDiskSpaceNeeded() // // Purpose: Calculate the maximum amount of disk space on the // destination drive that is needed to reconcile the // source and the destination directories. The // algorithm is as follows: // max(source size, destination size) + // NUM_COPY_THREADS * size of the largest file in source dir - // destination size // The reason for this algorithm is that the copy // operation is done by NUM_COPY_THREADS threads. // They copy the files to a temp file and then delete // the destination file and rename the temp file. // // Parameters: dwTotalSrcFiles total size of the source files // dwTotalDestFiles total size of the destination files // dwLargestHiveFile largest hive file // lpSrcFiles List of source files // dwFlags - Flags // // Return: Disk space needed. // // History: Created WeiruC 2/10/2000 // //************************************************************* DWORD FindTotalDiskSpaceNeeded(DWORD dwTotalSrcFiles, DWORD dwTotalDestFiles, DWORD dwLargestHiveFile, LPFILEINFO lpSrcFiles, DWORD dwFlags) { DWORD dwNumOfCopyThreads = NUM_COPY_THREADS; DWORD dwDiskSpaceNeeded = 0; LPFILEINFO lpCur = lpSrcFiles; DWORD i, j; // loop counters // // Check for empty file list. // if(!lpSrcFiles) { return dwLargestHiveFile; } // // How many copy threads are there actually? // if(dwFlags & CPD_SLOWCOPY) { dwNumOfCopyThreads = 1; } // // Find the size of the largest file in the source file list. The hive // files are not in this file list, be careful not to forget them. Hive // files have to be treated very carefully because they are always copied // over before we create those copy threads. // dwDiskSpaceNeeded = FindTotalNMaxFileSize(lpSrcFiles, dwNumOfCopyThreads); DebugMsg((DM_VERBOSE, TEXT("FindTotalDiskSpaceNeeded: Largest %d file size is %d"), dwNumOfCopyThreads, dwDiskSpaceNeeded)); // // The actual disk space needed. // if(dwDiskSpaceNeeded < dwLargestHiveFile) { dwDiskSpaceNeeded = dwLargestHiveFile; } if(dwTotalSrcFiles > dwTotalDestFiles) { dwDiskSpaceNeeded += dwTotalSrcFiles - dwTotalDestFiles; } // // It is too much of a pain to actually figure out cluster size impact. // We'll just add an extra 10% of the disk space needed. // dwDiskSpaceNeeded += dwDiskSpaceNeeded / 10; return dwDiskSpaceNeeded; } //************************************************************* // // FindTotalNMaxFileSize() // // Purpose: Calculates the total size for dwNumOfFiles // number of largest files. // // Parameters: lpSrcFiles - List of source files // dwNumOfFiles - Number of files. // dwNumOfFiles <= NUM_COPY_THREADS // // Return: Disk space needed for n largest files. // // History: Created santanuc 10/03/2000 // //************************************************************* DWORD FindTotalNMaxFileSize(LPFILEINFO lpSrcFiles, DWORD dwNumOfFiles) { DWORD pdwNMaxVal[NUM_COPY_THREADS], dwIndex; LPFILEINFO lpCur; DWORD dwTotalSize = 0, dwTmp; if (!lpSrcFiles) return 0; for(dwIndex = 0; dwIndex < dwNumOfFiles; dwIndex++) { pdwNMaxVal[dwIndex] = 0; } for(lpCur = lpSrcFiles; lpCur; lpCur = lpCur->pNext) { if (!lpCur->bHive) { dwIndex = dwNumOfFiles-1; if (lpCur->dwFileSize > pdwNMaxVal[dwIndex]) { pdwNMaxVal[dwIndex] = lpCur->dwFileSize; while (dwIndex > 0 && pdwNMaxVal[dwIndex] > pdwNMaxVal[dwIndex-1]) { dwTmp = pdwNMaxVal[dwIndex-1]; pdwNMaxVal[dwIndex-1] = pdwNMaxVal[dwIndex]; pdwNMaxVal[dwIndex] = dwTmp; dwIndex--; } } } } for(dwIndex = 0; dwIndex < dwNumOfFiles; dwIndex++) { dwTotalSize += pdwNMaxVal[dwIndex]; } return dwTotalSize; }