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