You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2477 lines
78 KiB
2477 lines
78 KiB
//*************************************************************
|
|
//
|
|
// 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;
|
|
}
|