/*++ Copyright (c) 1995 Microsoft Corporation Module Name: multisrc.c Abstract: Routines to handle multisource copies. Author: Ted Miller (tedm) 30-March-1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include "msg.h" // // Define structure that corresponds to a single file // that is to be copied. // typedef struct _COPY_ENTRY { struct _COPY_ENTRY *Next; PTSTR DestinationRoot; PTSTR RelativeDirectory; PTSTR SourceFilename; PTSTR TargetFilename; UINT ThreadBitmap; } COPY_ENTRY, *PCOPY_ENTRY; // // Define structure that corresponds to a list of files // to be copied. // typedef struct _COPY_LIST { PCOPY_ENTRY CopyEntryList; CRITICAL_SECTION CopyListCritSect; DWORD ThreadId; } COPY_LIST, *PCOPY_LIST; // // Define structure that is passed to sach copy worker thread. // typedef struct _MULTICOPY_THREAD_PARAMS { HWND hdlg; PTSTR SourcePath; UINT ThreadNumber; } MULTICOPY_THREAD_PARAMS, *PMULTICOPY_THREAD_PARAMS; HANDLE StopCopyingEvent; PCOPY_LIST CopyList; CRITICAL_SECTION GaugeCritSect; TCHAR CurrentFile[MAX_SOURCES][MAX_PATH]; HANDLE ListReadyEvent[MAX_SOURCES]; DWORD CreateMultiLevelDirectory( IN PCWSTR Directory ); BOOL BuildOptionalDirsFileLists( IN HWND hdlg ); VOID UpdateGaugeText( IN HWND hdlg, IN UINT ThreadNumber, IN PTSTR Filename ) { PTSTR p; TCHAR GaugeText[(MAX_SOURCES * MAX_PATH) + MAX_SOURCES]; UINT u; BOOL First; // // Remember the current file for this thread. // if(p = StringRevChar(Filename,TEXT('\\'))) { p++; } else { p = Filename; } _lstrcpyn(CurrentFile[ThreadNumber],p,MAX_PATH); // // Build up the gauge text. // GaugeText[0] = 0; First = TRUE; EnterCriticalSection(&GaugeCritSect); for(u=0; uThreadNumber; Events[0] = ListReadyEvent[MultiCopy->ThreadNumber]; Events[1] = StopCopyingEvent; while(1) { // // Wait for user to cancel or the file list to become ready/non-empty // if(WaitForMultipleObjects(2,Events,FALSE,INFINITE) != WAIT_OBJECT_0) { // // User cancelled or we're done copying, or some other error occurred. // break; } EnterCriticalSection(&CopyList->CopyListCritSect); // // Locate the next file that this thread has not yet // tried to copy, if any. If the list is completely // empty then reset the list ready event. // for(Previous=NULL, CopyEntry=CopyList->CopyEntryList; CopyEntry && (CopyEntry->ThreadBitmap & ThreadBit); Previous=CopyEntry, CopyEntry=CopyEntry->Next) ; // // If we found an entry unlink it from the list. // if(CopyEntry) { if(Previous) { Previous->Next = CopyEntry->Next; } else { CopyList->CopyEntryList = CopyEntry->Next; } } else { // // No entry for this thread. Enter a state where we're waiting // for an entry to be requeued or for copying to be finished. // ResetEvent(Events[0]); } LeaveCriticalSection(&CopyList->CopyListCritSect); // // Make sure we're not supposed to terminate. // We don't bother checking the state of StopCopyingEvent because the only // times this event ever gets signalled is when bCancelled is also being // set to TRUE. If that changes then we need to check the event here also. // if(bCancelled) { break; } // // If we got a file entry, go ahead and try to copy the file. // if(CopyEntry) { _lstrcpyn(SourceFilename,MultiCopy->SourcePath,MAX_PATH); DnConcatenatePaths(SourceFilename,CopyEntry->RelativeDirectory,MAX_PATH); DnConcatenatePaths(SourceFilename,CopyEntry->SourceFilename,MAX_PATH); _lstrcpyn(TargetFilename,CopyEntry->DestinationRoot,MAX_PATH); DnConcatenatePaths(TargetFilename,CopyEntry->RelativeDirectory,MAX_PATH); DnConcatenatePaths(TargetFilename,CopyEntry->TargetFilename,MAX_PATH); UpdateGaugeText(MultiCopy->hdlg,MultiCopy->ThreadNumber,SourceFilename); BytesCopied = DnCopyOneFile(MultiCopy->hdlg,SourceFilename,TargetFilename,&d); if(bCancelled) { break; } Requeue = FALSE; if(BytesCopied == (DWORD)(-1)) { // // Error. If this is the last thread to try to copy the file, // then we want to ask the user what to do. Otherwise requeue // the file so other copy threads can try to copy it. // if((CopyEntry->ThreadBitmap | ThreadBit) == (UINT)((1 << SourceCount)-1)) { switch(DnFileCopyError(MultiCopy->hdlg,SourceFilename,TargetFilename,d)) { case COPYERR_EXIT: PostMessage(MultiCopy->hdlg, WM_COMMAND, IDCANCEL, 0); break; case COPYERR_SKIP: // // We copied no bytes but still want to inform the main // thread that we've dispensed with another file. // BytesCopied = 0; break; case COPYERR_RETRY: // // Wipe the list of threads that have tried to copy the file // so all will take another crack at it. // CopyEntry->ThreadBitmap = 0; Requeue = TRUE; break; } } else { // // Tell ourselves that we've already tried to copy this file // and requeue it at the head of the list. // CopyEntry->ThreadBitmap |= ThreadBit; Requeue = TRUE; } } if(Requeue) { EnterCriticalSection(&CopyList->CopyListCritSect); CopyEntry->Next = CopyList->CopyEntryList; CopyList->CopyEntryList = CopyEntry; // // Want to set the event for every thread that might be called on // to copy this file. // for(d=0; dThreadBitmap & (1 << d))) { SetEvent(ListReadyEvent[d]); } } LeaveCriticalSection(&CopyList->CopyListCritSect); } else { FREE(CopyEntry); PostThreadMessage(CopyList->ThreadId,WMX_MULTICOPY,0,BytesCopied); } } else { // // This thread is not copying any file. Other threads may be copying files // that will get errors, and this thread therefore needs to be around // to service that file. So we don't exit here. The right thing happens // at the top of the thread's loop. // UpdateGaugeText(MultiCopy->hdlg,MultiCopy->ThreadNumber,TEXT("")); } } return(0); } BOOL InitializeMultiSourcedCopy( IN HWND hdlg ) { UINT Source; PMULTICOPY_THREAD_PARAMS ThreadParams; HANDLE hThread; DWORD ThreadId; InitializeCriticalSection(&GaugeCritSect); // // Create a copy list structure. // CopyList = MALLOC(sizeof(COPY_LIST)); ZeroMemory(CopyList,sizeof(COPY_LIST)); InitializeCriticalSection(&CopyList->CopyListCritSect); // // Create a manual reset event that will be used to tell the // worker threads to terminate. // StopCopyingEvent = CreateEvent(NULL,TRUE,FALSE,NULL); if(!StopCopyingEvent) { OutOfMemory(); } if(!BuildOptionalDirsFileLists(hdlg)) { return(FALSE); } // // Create one thread for each source. // for(Source=0; Sourcehdlg = hdlg; ThreadParams->SourcePath = DupString(Sources[Source]); ThreadParams->ThreadNumber = Source; hThread = CreateThread( NULL, 0, CopyWorkerThread, ThreadParams, 0, &ThreadId ); if(hThread) { CloseHandle(hThread); } else { OutOfMemory(); } } return(TRUE); } VOID EnqueueFileForCopy( IN PTSTR DestinationRoot, IN PTSTR RelativeDirectory, IN PTSTR SourceFilename, IN PTSTR TargetFilename ) { PCOPY_ENTRY CopyEntry; CopyEntry = MALLOC(sizeof(COPY_ENTRY)); ZeroMemory(CopyEntry,sizeof(COPY_ENTRY)); CopyEntry->DestinationRoot = DestinationRoot; CopyEntry->RelativeDirectory = RelativeDirectory; CopyEntry->SourceFilename = SourceFilename; CopyEntry->TargetFilename = TargetFilename; // // Put at head of list. // EnterCriticalSection(&CopyList->CopyListCritSect); CopyEntry->Next = CopyList->CopyEntryList; CopyList->CopyEntryList = CopyEntry; LeaveCriticalSection(&CopyList->CopyListCritSect); } VOID StartMultiSourcedCopy( VOID ) { UINT u; CopyList->ThreadId = GetCurrentThreadId(); for(u=0; uUsePlatformSpecificDir ? LocalSourceSubPath : LocalSourcePath ); DnConcatenatePaths(FindDir,Params->Subdir,MAX_PATH); CreateMultiLevelDirectory(FindDir); ListHead = NULL; FileCount = 0; lstrcpy(FindDir,Params->Source); DnConcatenatePaths(FindDir,Params->Subdir,MAX_PATH); // // If this is the $OEM$ directory, check if it exists in the source // If it does not exist, assume that it exists, but it is empty // if( Params->OptionalDirFlags & OPTDIR_OEMSYS ) { FindHandle = FindFirstFile(FindDir,&FindData); if(FindHandle == INVALID_HANDLE_VALUE) { return( 0 ); } FindClose(FindHandle); Params->OptionalDirFlags = 0; // &= ~OPTDIR_OEMSYS } DnConcatenatePaths(FindDir,TEXT("*"),MAX_PATH); FindHandle = FindFirstFile(FindDir,&FindData); if(FindHandle == INVALID_HANDLE_VALUE) { b = FALSE; } else { do { if(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // // It's a directory. Ignore if current or parent dir spec (. or ..). // c1 = FindData.cFileName[0]; c2 = FindData.cFileName[1]; c3 = FindData.cFileName[2]; if(!(((c1 == TEXT('.')) && !c2) || ((c1 == TEXT('.')) && (c2 == TEXT('.')) && !c3))) { SubParams.Source = Params->Source; lstrcpy(FindDir,Params->Subdir); DnConcatenatePaths(FindDir,FindData.cFileName,MAX_PATH); SubParams.Subdir = DupString(FindDir); SubParams.UsePlatformSpecificDir = Params->UsePlatformSpecificDir; d = BuildFileListThread(&SubParams); if(d == (DWORD)(-1)) { FREE(SubParams.Subdir); b = FALSE; } else { FileCount += d; } } } else { // // Not a directory. Create a file copy list node for the file. // CopyEntry = MALLOC(sizeof(COPY_ENTRY)); CopyEntry->DestinationRoot = Params->UsePlatformSpecificDir ? LocalSourceSubPath : LocalSourcePath; CopyEntry->RelativeDirectory = Params->Subdir; CopyEntry->ThreadBitmap = 0; CopyEntry->SourceFilename = DupString(FindData.cFileName); CopyEntry->TargetFilename = CopyEntry->SourceFilename; CopyEntry->Next = ListHead; ListHead = CopyEntry; FileCount++; } } while(b && FindNextFile(FindHandle,&FindData)); if(b) { b = (GetLastError() == ERROR_NO_MORE_FILES); } FindClose(FindHandle); } if(b) { // // Append the entire list to the main copy list. // EnterCriticalSection(&CopyList->CopyListCritSect); if(CopyList->CopyEntryList) { for(TempEntry=CopyList->CopyEntryList; TempEntry->Next; TempEntry=TempEntry->Next) ; TempEntry->Next = ListHead; } else { CopyList->CopyEntryList = ListHead; } LeaveCriticalSection(&CopyList->CopyListCritSect); } else { // // Free the partial list we built up. // for(CopyEntry=ListHead; CopyEntry; CopyEntry=TempEntry) { TempEntry = CopyEntry->Next; FREE(CopyEntry->SourceFilename); FREE(CopyEntry); } } return(b ? FileCount : (DWORD)(-1)); } BOOL BuildOptionalDirsFileLists( IN HWND hdlg ) { unsigned i,u,ThreadCount; DWORD ThreadId; HANDLE ThreadHandles[MAX_SOURCES]; PFLT_PARAMS ThreadParams[MAX_SOURCES]; BOOL b; DWORD ThreadFileCount; if(!CreateLocalSource) { OptionalDirCount = 0; } // // Create a thread for each optional directory. // But we don't want to create more threads than there are sources. // ThreadCount = 0; b = TRUE; for(i=0; iSource = Sources[ThreadCount]; ThreadParams[ThreadCount]->Subdir = OptionalDirs[i]; ThreadParams[ThreadCount]->UsePlatformSpecificDir = (OptionalDirFlags[i] & OPTDIR_TEMPONLY) ? TRUE : FALSE; ThreadParams[ThreadCount]->OptionalDirFlags = OptionalDirFlags[i]; ThreadHandles[ThreadCount] = CreateThread( NULL, 0, BuildFileListThread, ThreadParams[ThreadCount], 0, &ThreadId ); ThreadCount++; if(ThreadCount == SourceCount) { WaitForMultipleObjects(ThreadCount,ThreadHandles,TRUE,INFINITE); for(u=0; u