windows-nt/Source/XPSP1/NT/ds/security/gina/userenv/profile/copydir.c

2352 lines
71 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//*************************************************************
//
// Functions to copy the profile directory
//
// Microsoft Confidential
// Copyright (c) Microsoft Corporation 1995
// All rights reserved
//
//*************************************************************
#include "uenv.h"
//
// Local function proto-types
//
BOOL RecurseDirectory (HANDLE hTokenUser, LPTSTR lpSrcDir, LPTSTR lpDestDir, 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;
dwErr = GetLastError();
//
// Verbose output
//
DebugMsg((DM_VERBOSE, TEXT("CopyProfileDirectoryEx: Entering, lpSourceDir = <%s>, lpDestinationDir = <%s>, dwFlags = 0x%x"),
lpSourceDir, lpDestinationDir, dwFlags));
//
// Validate parameters
//
if (!lpSourceDir || !lpDestinationDir) {
DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: received NULL pointer")));
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// 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")));
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
//
lpSrcDir = LocalAlloc(LPTR, (2 * MAX_PATH) * sizeof(TCHAR));
lpDestDir = LocalAlloc(LPTR, (2 * MAX_PATH) * sizeof(TCHAR));
if (!lpSrcDir || !lpDestDir) {
DebugMsg((DM_WARNING, TEXT("CopyProfileDirectoryEx: Failed to allocate memory for working directories")));
dwErr = GetLastError();
goto Exit;
}
lstrcpy (lpSrcDir, lpSourceDir);
lstrcpy (lpDestDir, lpDestinationDir);
//
// Setup ending pointers
//
lpSrcEnd = CheckSlash (lpSrcDir);
lpDestEnd = CheckSlash (lpDestDir);
//
// Recurse through the folders gathering info
//
if (!(dwFlags & CPD_COPYHIVEONLY)) {
//
// Recurse the source directory
//
if (!RecurseDirectory(hTokenUser, lpSrcDir, lpDestDir, 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, lpSrcDir, 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)) {
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) {
lstrcpy (lpSrcEnd, c_szUserStar);
} else {
lstrcpy (lpSrcEnd, 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.
//
lstrcpy(lpSrcEnd, c_szNTUserTmp);
}
else {
lstrcpy (lpSrcEnd, fd.cFileName);
}
lstrcpy (lpDestEnd, 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])) {
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 hFile;
SetFileAttributes (lpTemp->szDest, FILE_ATTRIBUTE_NORMAL);
hFile = CreateFile(lpTemp->szDest, GENERIC_WRITE,
FILE_SHARE_DELETE | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
if (!SetFileTime(hFile, 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(hFile);
}
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, LPTSTR lpDestDir, DWORD dwFlags,
LPFILEINFO *llSrcDirs, LPFILEINFO *llSrcFiles,
BOOL bMarkNtUser, LPTSTR lpExcludeList, BOOL bRecurseDest)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA fd;
LPTSTR lpSrcEnd, lpDestEnd, lpTemp;
BOOL bResult = TRUE;
BOOL bSkip;
DWORD dwErr, dwErr1 = 0;
TCHAR szErr[MAX_PATH];
BOOL bHive;
dwErr = GetLastError();
//
// Setup the ending pointers
//
lpSrcEnd = CheckSlash (lpSrcDir);
lpDestEnd = CheckSlash (lpDestDir);
//
// Append *.* to the source directory
//
lstrcpy(lpSrcEnd, c_szStarDotStar);
//
// Search through the source directory
//
hFile = FindFirstFile(lpSrcDir, &fd);
if (hFile == INVALID_HANDLE_VALUE) {
if ( (GetLastError() == ERROR_FILE_NOT_FOUND) ||
(GetLastError() == ERROR_PATH_NOT_FOUND) ) {
//
// bResult is already initialized to TRUE, so
// just fall through.
//
} 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;
}
}
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(fd.cFileName) >= MAX_PATH) ||
(lstrlen(lpDestDir)+lstrlen(fd.cFileName) >= MAX_PATH)) {
LPTSTR lpErrSrc=NULL, lpErrDest=NULL;
BOOL bRet;
DebugMsg((DM_WARNING, TEXT("RecurseDirectory: %s is too long. src = %s, dest = %s"), fd.cFileName, lpSrcDir, lpDestDir));
if (dwFlags & CPD_IGNORELONGFILENAMES)
continue;
//
// Allocate a buffer to show the file names
//
lpErrSrc = LocalAlloc(LPTR, (lstrlen(lpSrcDir)+lstrlen(fd.cFileName)+1)*sizeof(TCHAR));
lpErrDest = LocalAlloc(LPTR, (lstrlen(lpDestDir)+lstrlen(fd.cFileName)+1)*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 {
lstrcpy(lpErrSrc, lpSrcDir);
lstrcpy(lpErrSrc+lstrlen(lpErrSrc), fd.cFileName);
lstrcpy(lpErrDest, lpDestDir);
lstrcpy(lpErrDest+lstrlen(lpErrDest), fd.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
//
lstrcpy (lpSrcEnd, fd.cFileName);
lstrcpy (lpDestEnd, fd.cFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
//
// Check for "." and ".."
//
if (!lstrcmpi(fd.cFileName, c_szDot)) {
continue;
}
if (!lstrcmpi(fd.cFileName, c_szDotDot)) {
continue;
}
//
// Check for reparse point
//
if (fd.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 (!(fd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) {
DWORD dwNewFlags = dwFlags;
//
// Add to the list of directories only if marked as system, o/w just recurse through
//
if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
if (!AddFileInfoNode (llSrcDirs, lpSrcDir, lpDestDir, &fd.ftLastWriteTime,
&fd.ftCreationTime, 0, fd.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, lpDestDir, 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 (fd.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 (fd.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, &fd.ftLastWriteTime,
&fd.ftCreationTime, 0, fd.dwFileAttributes, bHive)) {
DebugMsg((DM_WARNING, TEXT("RecurseDirectory: AddFileInfoNode failed")));
dwErr = GetLastError();
goto RecurseDir_Exit;
}
//
// Recurse the subdirectory
//
if (!RecurseDirectory(hTokenUser, lpSrcDir, lpDestDir, 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(fd.cFileName) >= 6) {
if (CompareString (LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
fd.cFileName, 6,
TEXT("ntuser"), 6) == CSTR_EQUAL) {
bHive = TRUE;
}
}
//
// Check if this file should be excluded
//
if (dwFlags & CPD_SYSTEMFILES) {
if (!(fd.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 (fd.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 (fd.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,
&fd.ftLastWriteTime, &fd.ftCreationTime,
fd.nFileSizeLow, fd.dwFileAttributes, bHive)) {
DebugMsg((DM_WARNING, TEXT("RecurseDirectory: AddFileInfoNode failed")));
dwErr = GetLastError();
goto RecurseDir_Exit;
}
}
//
// Find the next entry
//
} while (FindNextFile(hFile, &fd));
RecurseDir_Exit:
//
// Remove the file / directory name appended above
//
*lpSrcEnd = TEXT('\0');
*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 + 1];
TCHAR szTempDir[MAX_PATH + 1];
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
//
lstrcpy (szTempDir, lpDestFile);
lpTemp = szTempDir + lstrlen (szTempDir);
while ((lpTemp > szTempDir) && (*lpTemp != TEXT('\\'))) {
lpTemp--;
}
if (lpTemp == szTempDir) {
lstrcpy (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;
}
lstrcpy (lpNode->szSrc, lpSrcFile);
lstrcpy (lpNode->szDest, 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;
//
// Setup a temp buffer to work with
//
lstrcpy (szTemp, lpSourceDir);
lpEnd = CheckSlash (szTemp);
//
// 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 == TEXT(' ')) {
lpDir++;
}
//
// Note:
// Empty Spaces will not make the whole profile dir excluded
// in RecurseDirectory.
//
//
// Put the directory name on the temp buffer
//
lstrcpyn (lpEnd, lpDir, (int)(lpTemp - lpDir + 1));
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) {
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to realloc memory with %d"), GetLastError()));
LocalFree (lpExcludeList);
lpExcludeList = NULL;
goto Exit;
}
lpExcludeList = lpTempList;
lpInsert = lpExcludeList + dwSize - dwStrLen - 1;
lstrcpy (lpInsert, szTemp);
} else {
dwSize += lstrlen (szTemp);
lpExcludeList = LocalAlloc (LPTR, dwSize * sizeof(TCHAR));
if (!lpExcludeList) {
DebugMsg((DM_WARNING, TEXT("ConvertExclusionList: Failed to alloc memory with %d"), GetLastError()));
goto Exit;
}
lstrcpy (lpExcludeList, szTemp);
}
//
// If we are at the end of the exclusion list, we're done
//
if (!(*lpTemp)) {
goto Exit;
}
//
// Prep for the next entry
//
lpTemp++;
lpDir = lpTemp;
}
Exit:
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;
}