diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/sdktools/ftc | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to '')
-rw-r--r-- | private/sdktools/ftc/app.rc | 10 | ||||
-rw-r--r-- | private/sdktools/ftc/ftc.cxx | 1324 | ||||
-rw-r--r-- | private/sdktools/ftc/makefile | 6 | ||||
-rw-r--r-- | private/sdktools/ftc/sources | 18 |
4 files changed, 1358 insertions, 0 deletions
diff --git a/private/sdktools/ftc/app.rc b/private/sdktools/ftc/app.rc new file mode 100644 index 000000000..f5958c45e --- /dev/null +++ b/private/sdktools/ftc/app.rc @@ -0,0 +1,10 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Fast tree copy" +#define VER_INTERNALNAME_STR "ftc\0" +#define VER_ORIGINALFILENAME_STR "ftc.exe" + +#include <common.ver> diff --git a/private/sdktools/ftc/ftc.cxx b/private/sdktools/ftc/ftc.cxx new file mode 100644 index 000000000..50f844dcf --- /dev/null +++ b/private/sdktools/ftc/ftc.cxx @@ -0,0 +1,1324 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1996 - 1996. +// +// File: ftc.cxx +// +// Contents: Fast multi-threaded tree copy program. +// +// History: ?-?-94 IsaacHe Created +// 11-Jun-96 BruceFo Fixed bugs, put this header here. +// +//-------------------------------------------------------------------------- + +#if defined( UNICODE ) +#undef UNICODE +#endif + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <malloc.h> +#include <direct.h> +#include <sys\types.h> +#include <sys\stat.h> +#include <io.h> +#include <conio.h> +#include <errno.h> +#include <process.h> +#include <ctype.h> + +#define MAXQUEUE 10000 +#define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0])) + +/* + * These are the attributes we use to compare for file attribute identity + */ +const DWORD FILE_ATTRIBUTE_MASK = FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_ARCHIVE; + +class CProtectedLong +{ + CRITICAL_SECTION _cs; + LONG _value; + +public: + CProtectedLong() { InitializeCriticalSection( &_cs ); _value = 0; } + LONG operator++(int) { + EnterCriticalSection( &_cs ); + LONG tmp = _value++; + LeaveCriticalSection( &_cs ); + return tmp; + } + LONG operator--( int ) { + EnterCriticalSection( &_cs ); + LONG tmp = _value--; + LeaveCriticalSection( &_cs ); + return tmp; + } + LONG operator+=( LONG incr ) { + EnterCriticalSection( &_cs ); + LONG tmp = (_value += incr ); + LeaveCriticalSection( &_cs ); + return tmp; + } + LONG operator=( LONG val ) { + EnterCriticalSection( &_cs ); + _value = val; + LeaveCriticalSection( &_cs ); + return val; + } + operator LONG() { return _value; } + operator int() { return _value; } +}; + +class CHandle +{ + HANDLE _h; + +public: + CHandle() : _h(INVALID_HANDLE_VALUE) { } + ~CHandle(); + HANDLE operator=( HANDLE h ); + BOOL operator==( HANDLE h ) { return (h == _h) ? TRUE : FALSE; } + BOOL operator!=( HANDLE h ) { return (h != _h) ? TRUE : FALSE; } + operator HANDLE() { return _h; } +}; + +CHandle::~CHandle() +{ + if( _h != INVALID_HANDLE_VALUE && _h != NULL ) + CloseHandle( _h ); +} + +HANDLE +CHandle::operator=( HANDLE h ) +{ + if( _h != INVALID_HANDLE_VALUE && _h != NULL ) + CloseHandle( _h ); + return _h = h; +} + +DWORD dwElapsedTime; // time we've been copying data +CProtectedLong ulTotalBytesCopied; // running total count of bytes +CProtectedLong ulTotalBytesSkipped; // obvious? +CProtectedLong ulTotalBytesScanned; +CProtectedLong nFilesOnQueue; // number of files to copy or examine +CProtectedLong nFcopy; // number of files copied +CProtectedLong nSkipped; // number of files skipped over +CProtectedLong nMappedCopy; // number of files copied using MapFile... +CProtectedLong nCopyFile; // number of files copied using CopyFile()... +CProtectedLong nInProgress; // number of copies currently in progress +CProtectedLong MaxThreads; // Max number of threads for copying +DWORD ExitCode = 0; // each thread's exit code + +BOOL bThreadStop = FALSE; // are we trying to exit? +BOOL bWorkListComplete = FALSE; // have we scanned all the directories yet? + +BOOL tFlag = FALSE; // only copy if newer +BOOL iFlag = FALSE; // skip seemingly identical files +BOOL rFlag = FALSE; // replace read-only files +BOOL vFlag = FALSE; // verbose +BOOL AFlag = FALSE; // keep going even if there are errors +BOOL FFlag = FALSE; // just produce file list. No copies +BOOL oFlag = TRUE; // should we overwrite files already at dest? +BOOL wFlag = FALSE; // should we wait for the source to show up? +BOOL qFlag = FALSE; // quiet mode? +BOOL yFlag = FALSE; // no recurse on target? +BOOL zFlag = FALSE; // no recurse on source? + +BOOL pFlag = FALSE; // pattern? +CHAR szPattern[100]; // pattern string, if pFlag is TRUE + +struct WorkList // copy file at 'src' to 'dest' +{ + struct WorkList *next; + char *src; // Pathname relative to the source + WIN32_FIND_DATA srcfind; + char *dest; +} *WorkList = NULL; +struct WorkList* WorkListTail = NULL; // always add files to copy to the *tail* + // of the work list. This is to make + // sure we always do work in order, instead + // of starting on a directory but finishing + // it much much much later, because we've + // pushed all the work to the deep tail of + // the list and never returned to it! + +CHandle hWorkAvailSem; // signalled whenever there's work on the list +CHandle hMaxWorkQueueSem; // used to control lenght of work queue + +CRITICAL_SECTION csMsg; // used to serialize screen output +CRITICAL_SECTION csWorkList; // used when manipulating the linked list +CRITICAL_SECTION csSourceList; // used when manipulating the source list + +struct SourceList +{ + char *name; // pathname of the source. Ends in '\' + LONG count; // number of files currently being copied + LONG ulTotalFiles; // total for the entire copy + struct { + unsigned valid : 1; // do we know that the source is valid? + } flags; +} SourceList[ 20 ]; +int MaxSources = 0; + +char *DirectoryExcludeList[ 50 ]; +int MaxDirectoryExcludes = 0; + +char OldConsoleTitle[ 100 ]; + +void +errormsg( char const *pszfmt, ... ) +{ + va_list ArgList; + va_start( ArgList, pszfmt ); + + if( bThreadStop == FALSE && pszfmt != NULL ) { + EnterCriticalSection( &csMsg ); + vprintf( pszfmt, ArgList ); + LeaveCriticalSection( &csMsg ); + } + + va_end( ArgList ); +} + +DWORD __stdcall +StatusWorker( void *arg ) +{ + char ostatbuf[ 100 ]; + char nstatbuf[ 100 ]; + + while( bThreadStop == FALSE ) { + sprintf(nstatbuf, + "Remaining Files %d Bytes %d", + (int)nFilesOnQueue, + (int)ulTotalBytesScanned - + (int)ulTotalBytesCopied - (int)ulTotalBytesSkipped ); + + if( strcmp( ostatbuf, nstatbuf ) ) { + SetConsoleTitle( nstatbuf ); + strcpy( ostatbuf, nstatbuf ); + } + Sleep( 1 * 1000 ); + } + + SetConsoleTitle( OldConsoleTitle ); + ExitThread( ExitCode ); + arg = arg; + return 0; +} + +void +msg( char const *pszfmt, ... ) +{ + if( qFlag ) + return; + + va_list ArgList; + va_start( ArgList, pszfmt ); + + EnterCriticalSection( &csMsg ); + vprintf( pszfmt, ArgList ); + LeaveCriticalSection( &csMsg ); + va_end( ArgList ); +} + +void +errorexit (char const *pszfmt, ... ) +{ + + if( bThreadStop == FALSE && pszfmt != NULL ) { + va_list ArgList; + va_start( ArgList, pszfmt ); + + EnterCriticalSection( &csMsg ); + vprintf( pszfmt, ArgList ); + LeaveCriticalSection( &csMsg ); + + va_end( ArgList ); + } + + if( AFlag == FALSE ) { + bThreadStop = TRUE; + + EnterCriticalSection( &csWorkList ); + WorkList = NULL; + WorkListTail = NULL; + LeaveCriticalSection( &csWorkList ); + + if( hWorkAvailSem != NULL ) + ReleaseSemaphore( hWorkAvailSem, (int)MaxThreads+1, NULL ); + + if( hMaxWorkQueueSem != NULL ) + ReleaseSemaphore( hMaxWorkQueueSem, 1, NULL ); + + SetConsoleTitle( OldConsoleTitle ); + ExitThread (ExitCode = 1); + } +} + +DWORD +fcopy( char *src, WIN32_FIND_DATA *srcfind, char *dst, char *errorbuf ) +{ + CHandle srcfh; + CHandle dstfh; + CHandle hsrc; + DWORD nBytesWritten, totalbytes; + char *result = NULL; + char *psrc; + BOOL ret = TRUE; + char dostatus = 0; + DWORD errcode; + + *errorbuf = '\0'; + + if( srcfind->nFileSizeHigh != 0 ) { + if( CopyFile( src, dst, TRUE ) == FALSE ) { + errcode = GetLastError(); + sprintf( errorbuf, "CopyFile failed, error %d", errcode ); + return errcode; + } + nCopyFile++; + + } else { + + srcfh = CreateFile( src, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL ); + if( srcfh == INVALID_HANDLE_VALUE ) { + errcode = GetLastError(); + sprintf( errorbuf, "Unable to open source file, error %d", errcode); + return errcode; + } + + dstfh = CreateFile( dst, GENERIC_WRITE, FILE_SHARE_WRITE,NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, srcfh); + + if( dstfh == INVALID_HANDLE_VALUE ) { + errcode = GetLastError(); + sprintf( errorbuf, "Unable to create dest file, error %d", errcode); + return errcode; + } + + if( srcfind->nFileSizeLow != 0 ) { + hsrc = CreateFileMapping( srcfh, NULL, PAGE_READONLY, 0, + srcfind->nFileSizeLow, NULL ); + if( hsrc == NULL ) { + dstfh = INVALID_HANDLE_VALUE; + DeleteFile( dst ); + if( CopyFile( src, dst, TRUE ) == FALSE ) { + errcode = GetLastError(); + sprintf( errorbuf, "Unable to create file mapping, and CopyFile failed, error %d", errcode ); + return errcode; + } + nCopyFile++; + ulTotalBytesCopied += srcfind->nFileSizeLow; + goto DoTime; + } + if( (psrc = (char *)MapViewOfFile( hsrc, FILE_MAP_READ, 0, 0, 0 )) == NULL){ + dstfh = INVALID_HANDLE_VALUE; + DeleteFile( dst ); + if( CopyFile( src, dst, TRUE ) == FALSE ) { + errcode = GetLastError(); + sprintf( errorbuf, "Unable to map source file, and CopyFile failed: error %d", errcode ); + return errcode; + } + nCopyFile++; + ulTotalBytesCopied += srcfind->nFileSizeLow; + goto DoTime; + } + totalbytes = 0; + while( !bThreadStop && totalbytes < srcfind->nFileSizeLow && ret == TRUE ) { + ret = WriteFile( dstfh, + psrc + totalbytes, + min( 64*1024, srcfind->nFileSizeLow - totalbytes ), + &nBytesWritten, NULL ); + totalbytes += nBytesWritten; + ulTotalBytesCopied += nBytesWritten; + } + errcode = GetLastError(); + UnmapViewOfFile( psrc ); + + if( bThreadStop == TRUE ) { + dstfh = INVALID_HANDLE_VALUE; + DeleteFile( dst ); + *errorbuf = '\0'; + return errcode; + } + + if( ret == FALSE ) { + dstfh = INVALID_HANDLE_VALUE; + DeleteFile( dst ); + if( CopyFile( src, dst, TRUE ) == FALSE ) { + errcode = GetLastError(); + sprintf( errorbuf, "%s: CopyFile failed: error %d", dst, errcode); + return GetLastError(); + } + nCopyFile++; + ulTotalBytesCopied += srcfind->nFileSizeLow; + goto DoTime; + } + + nMappedCopy++; + } + } + +DoTime: + if( dstfh == INVALID_HANDLE_VALUE ) { + dstfh = CreateFile( dst, GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + 0, NULL ); + } + + if( dstfh == INVALID_HANDLE_VALUE ) { + errcode = GetLastError(); + DeleteFile( dst ); + sprintf( errorbuf, "Unable to open destination file to set time, error %d", errcode ); + return errcode; + } + + if( !SetFileTime( dstfh, &srcfind->ftCreationTime, + &srcfind->ftLastAccessTime, &srcfind->ftLastWriteTime )) { + errcode = GetLastError(); + sprintf( errorbuf, "Unable to set destination file times, error %d\n", errcode ); + return errcode; + } + + nFcopy++; + return 0; +} + +/* + * Pick the source having the fewest outstanding operations at the moment. + */ +int +SelectSource() +{ + int index = -1; + struct SourceList *psl; + struct SourceList *opsl; + static min; + + EnterCriticalSection( &csSourceList ); + + // + // Find the first valid source + // + for( opsl = SourceList; opsl < &SourceList[ MaxSources ]; opsl++ ) + if( opsl->flags.valid == TRUE ) + break; + + // + // Now locate the source having the fewest pending operations right now + // + for( psl = opsl+1; psl < &SourceList[ MaxSources ]; psl++ ) { + if( psl->flags.valid == TRUE && psl->count < opsl->count ) + opsl = psl; + } + + if( opsl->flags.valid == TRUE ) { + opsl->count++; + index = opsl - SourceList; + } + + LeaveCriticalSection( &csSourceList ); + + return index; +} + +/* + * We've completed the operation on source 'index' + */ +void +SourceCopyComplete( int index, BOOL fFile ) +{ + EnterCriticalSection( &csSourceList ); + SourceList[ index ].count--; + if (fFile) SourceList[ index ].ulTotalFiles++; + LeaveCriticalSection( &csSourceList ); +} + +void +DisableSource( int index ) +{ + if( index >= 0 && index < MaxSources ) { + EnterCriticalSection( &csSourceList ); + if( SourceList[ index ].flags.valid == TRUE ) { + errormsg( "Disabling %s\n", SourceList[ index ].name ); + SourceList[ index ].flags.valid = FALSE; + } + LeaveCriticalSection( &csSourceList ); + } +} +BOOL +FileTimesEqual( CONST FILETIME *pt1, CONST FILETIME *pt2 ) +{ + SYSTEMTIME s1, s2; + + if( !FileTimeToSystemTime( pt1, &s1 ) || !FileTimeToSystemTime( pt2, &s2 ) ) + return FALSE; + + return s1.wHour == s2.wHour && + s1.wMinute == s2.wMinute && + s1.wMonth == s2.wMonth && + s1.wDay == s2.wDay && + s1.wYear == s2.wYear; +} +void +PrintFileTime( char *str, CONST FILETIME *ft ) +{ + SYSTEMTIME st; + + if( FileTimeToSystemTime( ft, &st ) == FALSE ) { + errormsg( "????\n" ); + return; + } + + msg( "%s %u:%u.%u.%u %u/%u/%u\n", str, + st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, + st.wMonth, st.wDay, st.wYear ); +} + +DWORD __stdcall +ThreadWorker( void *arg ) +{ + struct WorkList *pdl = NULL; + int id = (int)arg; + HANDLE hdestfind; + WIN32_FIND_DATA destfind; + int index = 0; + char errorbuf[ 100 ]; + char pathbuf[ MAX_PATH ]; + DWORD errcode; + + MaxThreads++; + + while( 1 ) { + if( pdl != NULL ) { + free( pdl->src ); + free( pdl->dest ); + free( pdl ); + pdl = NULL; + } + + if( bThreadStop == TRUE ) + break; + + // Poll for new stuff every 2 seconds. If the thread is set to stop, + // then go away. + DWORD dwWait; + while( 1 ) + { + dwWait = WaitForSingleObject( hWorkAvailSem, 1000 ); + if( dwWait == WAIT_OBJECT_0 ) { + break; + } + + if( dwWait == WAIT_TIMEOUT ) { + if( bThreadStop == TRUE ) + break; + } else { + errormsg( "Thread %d: Semaphore wait failed\n", id ); + break; + } + } + if ( dwWait != WAIT_OBJECT_0 ) { + break; + } + + // pick an item off the head of the work list + EnterCriticalSection( &csWorkList ); + pdl = WorkList; + if( pdl != NULL ) { + WorkList = pdl->next; + if (NULL == WorkList) { + // just pulled off the tail entry + WorkListTail = NULL; + } + } + LeaveCriticalSection( &csWorkList ); + ReleaseSemaphore( hMaxWorkQueueSem, 1, NULL ); + + if( pdl == NULL ) + break; + + nFilesOnQueue--; + + if( pdl->srcfind.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { + errormsg( "Logic Error: Directory on work list!\n" ); + continue; + } + + pdl->srcfind.dwFileAttributes &= FILE_ATTRIBUTE_MASK; + + hdestfind = FindFirstFile( pdl->dest, &destfind ); + if( hdestfind != INVALID_HANDLE_VALUE ) { + FindClose( hdestfind ); + destfind.dwFileAttributes &= FILE_ATTRIBUTE_MASK; + + /* + * Destination file exists. What should we do? + */ + + if( oFlag == FALSE ) { + /* + * We should not overwrite the existing file at the dest + */ + if( vFlag ) + msg( "%s [SKIP: exists]\n", pdl->dest ); + ulTotalBytesSkipped += destfind.nFileSizeLow; + nSkipped++; + continue; + } + + if( iFlag && vFlag ) { + + if( destfind.dwFileAttributes !=pdl->srcfind.dwFileAttributes) + msg( "%s [ ATTRIBUTES differ ]\n", pdl->dest ); + if(!FileTimesEqual( &destfind.ftLastWriteTime, &pdl->srcfind.ftLastWriteTime)) { + EnterCriticalSection( &csMsg ); + msg( "%s [ TIMES differ ]\n", pdl->dest ); + PrintFileTime( "Dest: ", &destfind.ftLastWriteTime ); + PrintFileTime( "Src: ", &pdl->srcfind.ftLastWriteTime ); + LeaveCriticalSection( &csMsg ); + } + if( (destfind.nFileSizeHigh != pdl->srcfind.nFileSizeHigh) || + (destfind.nFileSizeLow != pdl->srcfind.nFileSizeLow) ) + msg( "%s [ SIZES differ ]\n", pdl->dest ); + } + + if( iFlag && + (destfind.dwFileAttributes == pdl->srcfind.dwFileAttributes) && + FileTimesEqual( &destfind.ftLastWriteTime, &pdl->srcfind.ftLastWriteTime) && + (destfind.nFileSizeHigh == pdl->srcfind.nFileSizeHigh) && + (destfind.nFileSizeLow == pdl->srcfind.nFileSizeLow) ) { + if( vFlag ) + msg("%s [SKIP: same atts, time, size]\n",pdl->dest); + ulTotalBytesSkipped += destfind.nFileSizeLow; + nSkipped++; + continue; + } + + if( tFlag && + CompareFileTime( &destfind.ftLastWriteTime, &pdl->srcfind.ftLastWriteTime) >= 0 ) { + if( vFlag ) + msg("%s [SKIP: same or newer time]\n", pdl->dest ); + ulTotalBytesSkipped += destfind.nFileSizeLow; + nSkipped++; + continue; + } + + if( destfind.dwFileAttributes & FILE_ATTRIBUTE_READONLY ) + if( rFlag == FALSE && bThreadStop == FALSE ) { + if( vFlag ) + msg( "%s [SKIP: readonly]\n", pdl->dest ); + ulTotalBytesSkipped += destfind.nFileSizeLow; + nSkipped++; + continue; + } + + /* + * Delete the destination file + */ + if( destfind.dwFileAttributes & FILE_ATTRIBUTE_READONLY ) { + destfind.dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY; + SetFileAttributes( pdl->dest, destfind.dwFileAttributes ); + } + + if( FFlag == FALSE ) + (void)DeleteFile( pdl->dest ); + + } + + if( FFlag == FALSE ) { + while( bThreadStop == FALSE && (index = SelectSource()) >= 0 ) { + strcpy( pathbuf, SourceList[index].name ); + strcat( pathbuf, pdl->src ); + + nInProgress++; + errcode = fcopy( pathbuf,&pdl->srcfind,pdl->dest,errorbuf); + nInProgress--; + + if( errcode == 0 ) { + SourceCopyComplete( index, TRUE ); + msg("%s -> %s [OK]\n", pathbuf, pdl->dest ); + SetFileAttributes(pdl->dest,pdl->srcfind.dwFileAttributes); + break; + } + + if( errcode == ERROR_SWAPERROR ) { + errormsg( "%s [ SWAP ERROR, will try again... ]\n",pathbuf); + Sleep( 5 * 1000 * 60 ); + continue; + } + if( bThreadStop == FALSE ) + errormsg( "%s [FAILED: %s ]\n", pathbuf, errorbuf ); + if( AFlag == TRUE ) + break; + DisableSource( index ); + } + } else { + msg( "%s\n", pdl->dest ); + } + + if( AFlag == FALSE && index < 0 ) + errorexit( "%s [FAILED completely]\n", pdl->dest ); + } + + if( pdl != NULL ) { + free( pdl->src ); + free( pdl->dest ); + free( pdl ); + } + + if( MaxThreads-- == 1 ) { + if( ExitCode == 0 ) { + dwElapsedTime = GetTickCount() - dwElapsedTime; + dwElapsedTime /= 1000; + BOOL oldqFlag = qFlag; + qFlag = FALSE; + msg( "%u files copied (%u memory mappped, %u CopyFile )\n", + (int)nFcopy, (int)nMappedCopy, (int)nCopyFile ); + msg( "%u files skipped\n", (int)nSkipped); + msg( "%lu bytes in %u seconds: %lu bits/sec\n", + (int)ulTotalBytesCopied, dwElapsedTime, + dwElapsedTime ? (LONG)(((LONG)ulTotalBytesCopied*8L)/dwElapsedTime) : 0L ); + + qFlag = oldqFlag; + EnterCriticalSection( &csSourceList ); + for (int i = 0; i < MaxSources; i++) + { + if (TRUE == SourceList[i].flags.valid) + { + msg( "%s %5lu files\n", SourceList[i].name, SourceList[i].ulTotalFiles); + } + } + LeaveCriticalSection( &csSourceList ); + } + SetConsoleTitle( OldConsoleTitle ); + ExitProcess( ExitCode ); + } + + ExitThread( ExitCode ); + return 0; +} + +void +AddToWorkList( char *src, char *dest, WIN32_FIND_DATA *pfind ) +{ + struct WorkList *pdl; + + if( WaitForSingleObject( hMaxWorkQueueSem, INFINITE ) != WAIT_OBJECT_0 ) { + errormsg( "Semaphore wait failed, can't add to work list\n" ); + return; + } + + if( bThreadStop == TRUE ) + return; + + if( (pdl = (struct WorkList *)malloc( sizeof( struct WorkList ) ) ) == NULL ){ + errorexit( "Out of Memory!\n" ); + return; + } + + if( (pdl->dest = _strdup( dest )) == NULL ) { + errorexit( "Out of memory!\n" ); + free( pdl ); + return; + } + + if( (pdl->src = _strdup( src )) == NULL ) { + errorexit( "Out of memory!\n" ); + free( pdl->dest ); + free( pdl ); + return; + } + + pdl->srcfind = *pfind; + pdl->next = NULL; + + EnterCriticalSection( &csWorkList ); + if (NULL == WorkList) { + WorkListTail = WorkList = pdl; + } else { + WorkListTail->next = pdl; // point the tail to the new entry + WorkListTail = pdl; // the new entry becomes the tail + } + LeaveCriticalSection( &csWorkList ); + + nFilesOnQueue++; + ReleaseSemaphore( hWorkAvailSem, 1, NULL ); +} + +void +ScanDirectory( + char *relpath, + char *dest + ); + +void +ScanDirectoryHelp( + char *relpath, // path relative to the source + char *dest, // resulting destination directory + BOOL fOnlyDirectories, // TRUE if we only want to look for dirs + BOOL fOnlyFiles // TRUE if we only want to look for files + ) +{ + int index; + int destlen = strlen( dest ); + int rellen = strlen( relpath ); + WIN32_FIND_DATA fbuf; + HANDLE hfind = INVALID_HANDLE_VALUE; + char SourceName[ MAX_PATH ]; + + while( 1 ) { + if( (index = SelectSource()) < 0 ) + return; + /* + * FindFirst/Next is such low overhead on the server that we shouldn't + * really count it as a load on the server... + */ + SourceCopyComplete( index, FALSE ); + + strcpy( SourceName, SourceList[ index ].name ); + if( rellen ) { + strcat( SourceName, relpath ); + strcat( SourceName, "\\" ); + } + + strcat( SourceName, + fOnlyDirectories + ? "*.*" + : (pFlag ? szPattern : "*.*" ) ); + + hfind = FindFirstFile( SourceName, &fbuf ); + if( hfind != INVALID_HANDLE_VALUE ) + break; + + if (pFlag && ERROR_FILE_NOT_FOUND == GetLastError()) { + // simply no files that match the pattern in the directory + return; + } + + errormsg( "Dir scan of %s failed, error %d [DISABLING]\n", SourceName, GetLastError() ); + DisableSource( index ); + } + + do { + if( !strcmp( fbuf.cFileName, "." ) || !strcmp( fbuf.cFileName, ".." ) ) + continue; + + sprintf( &dest[ destlen ], "%s%s", + dest[destlen-1] == '\\' ? "" : "\\", fbuf.cFileName ); + + if((fbuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 ) { + // + // Not a directory + // + + if (fOnlyDirectories) { + continue; + } + + /* + * Queue this file into the work queue! + */ + if( rellen ) { + strcpy( SourceName, relpath ); + strcat( SourceName, "\\" ); + } else + SourceName[0] = '\0'; + + strcat( SourceName, fbuf.cFileName ); + ulTotalBytesScanned += fbuf.nFileSizeLow; + AddToWorkList( SourceName, dest, &fbuf ); + continue; + } + + /* + * We've found a directory. Descend into it and scan (if we're + * not excluding it) + */ + + if (fOnlyFiles) { + continue; + } + + if (zFlag) { // no recurse on source: ignore it! + continue; + } + + for( int i=0; i < MaxDirectoryExcludes; i++ ) + if( !_stricmp( DirectoryExcludeList[i], fbuf.cFileName ) ) + break; + + if( i != MaxDirectoryExcludes ) { + if( vFlag ) + msg( "Directory: %s [EXCLUDED]\n", dest ); + nSkipped++; + continue; + } + + sprintf( &relpath[ rellen ], "%s%s", rellen ? "\\":"", fbuf.cFileName ); + if (yFlag) { // no recurse on target: nuke end of dest (the new dir) + dest[ destlen ] = '\0'; + } + ScanDirectory( relpath, dest ); + relpath[ rellen ] = '\0'; + + } while( !bThreadStop && FindNextFile( hfind, &fbuf ) == TRUE ); + + dest[ destlen ] = '\0'; + + FindClose( hfind ); +} + +void +ScanDirectory( + char *relpath, // path relative to the source + char *dest // resulting destination directory + ) +{ + DWORD dwattrs; + + if( (dwattrs = GetFileAttributes( dest )) == 0xFFFFFFFF ) { + msg( "Creating Directory: %s\n", dest ); + if( FFlag == FALSE && CreateDirectory( dest, NULL ) == FALSE ) { + errorexit( "Can not create directory: %s\n", dest ); + return; + } + + } else if( !(dwattrs & FILE_ATTRIBUTE_DIRECTORY) ) { + errorexit( "Not a directory: %s\n", dest ); + return; + } + + if (pFlag) { + // two passes: one looking for files, one looking for directories + + ScanDirectoryHelp(relpath, dest, FALSE, TRUE); + ScanDirectoryHelp(relpath, dest, TRUE, FALSE); + } else { + ScanDirectoryHelp(relpath, dest, FALSE, FALSE); + } +} + +static void +appendslash( char *p ) +{ + if( p[ strlen(p) - 1 ] != '\\' ) + strcat( p, "\\" ); +} + +BOOL +rootpath( char *src, char *dst ) +{ + char* FilePart; + char *p; + + if( src == NULL || *src == '\0' ) + return FALSE; + + if( GetFullPathName( src, MAX_PATH, dst, &FilePart ) == 0 ) + return FALSE; + + p = src + strlen(src) - 1; + if( *p == '.' ) + if( p > src ) { + p--; + if( *p != '.' && *p != ':' && (*p == '\\' || *p == '/') ) + strcat( dst, "." ); + } + + return TRUE; +} + +static void +Usage( char *s ) +{ + errormsg( "Usage: %s [flags] [-p pattern] [src ...] dest\n", s ); + errormsg( "Flags:\n" ); + errormsg( "\t-c|f Get a checked (or free) public tree. Use before -# option\n" ); + errormsg( "\t-[x86|mips|alpha|ppc] Get release for specific processor family\n" ); + errormsg( "\t-w Wait until at least 1 'src' is present\n" ); + errormsg( "\t-# Get NT public release #\n\n" ); + + errormsg( "\t-i Skip seemingly identical files (time, attrs, size agree)\n" ); + errormsg( "\t-l Execute at lower priority\n" ); + errormsg( "\t-o Do not overwrite any files that are already at dest\n" ); + errormsg( "\t-p pattern Only files matching the pattern are copied\n" ); + errormsg( "\t-q Quiet mode\n" ); + errormsg( "\t-r Overwrite read-only files at dest\n" ); + errormsg( "\t-t Copy only newer files to dest\n" ); + errormsg( "\t-v Verbose\n" ); + errormsg( "\t-y Don't recurse on target\n" ); + errormsg( "\t-z Don't recurse on source\n" ); + errormsg( "\t-A Keep going even if there are errors\n" ); + errormsg( "\t-F Don't actually copy files or create directories\n" ); + errormsg( "\t~dir Skip any directory named 'dir'\n" ); + errormsg( "\nIf environment variable FTC_PARANOID is set, then the meaning of -o is\n" ); + errormsg( "reversed: no -o means don't overwrite, -o means go ahead and overwrite.\n" ); + + errormsg( "\nExamples:\n" ); + errormsg( " Get NT 1338 publics for mips: ftc -mips -1338 dest\n" ); + errormsg( " Get NT 1338 publics, skip 'alpha' dirs: ftc ~alpha -1338 dest\n" ); + errormsg( " Wait until release 1338 shares are up: ftc -w -1338 dest\n" ); + errormsg( " Copy from two sources, no 'obj' dir: ftc ~obj \\\\foo\\dir \\\\bar\\dir dest\n" ); + ExitProcess( 1 ); +} + +BOOL __stdcall +ControlHandlerRoutine( DWORD dwCtrlType ) +{ + msg( "Interrupted!\n" ); + bThreadStop = TRUE; + ExitCode = 1; + return TRUE; +} + + +int +__cdecl +main(int argc, char *argv[]) +{ + char *p; + SECURITY_ATTRIBUTES sa; + int i, argno; + DWORD IDThread; + char dest[ MAX_PATH ]; + char relpath[ MAX_PATH ]; + BOOL cFlag = TRUE; // get a checked NT release? otherwise free + BOOL lFlag = FALSE; // low priority? + BOOL fParanoid = FALSE; // is FTC_PARANOID set in the environment? + SYSTEM_INFO si; + CHandle CThread; + + InitializeCriticalSection( &csMsg ); + InitializeCriticalSection( &csWorkList ); + InitializeCriticalSection( &csSourceList ); + ZeroMemory(&si, sizeof( si )); + GetConsoleTitle( OldConsoleTitle, sizeof( OldConsoleTitle ) ); + + TCHAR szParanoid[100]; + DWORD len = GetEnvironmentVariable(TEXT("FTC_PARANOID"), szParanoid, ARRAYLEN(szParanoid)); + if (len > 0) + { + fParanoid = TRUE; + } + + if (fParanoid) + { + oFlag = FALSE; + } + else + { + oFlag = TRUE; + } + + for( argno = 1; + argno < argc && (argv[argno][0] == '-' || argv[argno][0] == '/' || argv[argno][0] == '~') ; + argno++ ) + { + if( argv[argno][0] == '~' ) + { + DirectoryExcludeList[ MaxDirectoryExcludes++ ] = &argv[argno][1]; + + } + else for( int j=1; argv[argno][j]; j++ ) + { + switch( argv[argno][j] ) { + case 'l': + lFlag = TRUE; + break; + case 'q': + qFlag = TRUE; + break; + case 'y': + yFlag = TRUE; + break; + case 'z': + zFlag = TRUE; + break; + case 'w': + wFlag = TRUE; + break; + case 'o': + if (fParanoid) + { + oFlag = TRUE; + } + else + { + oFlag = FALSE; + } + break; + case 'F': + FFlag = TRUE; + break; + case 'A': + AFlag = TRUE; + break; + case 'f': + cFlag = FALSE; + break; + case 'c': + cFlag = TRUE; + break; + case 'p': + if( j == 1 ) { + if ( strcmp( &argv[argno][j], "ppc" ) == 0 ) { + si.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_PPC; + j += 2; + } else if ( strcmp( &argv[argno][j], "p" ) == 0 ) { + if (argno + 1 < argc) { + pFlag = TRUE; + strcpy( szPattern, argv[++argno] ); + goto nextarg; // go to next argument + } else Usage( argv[0] ); + } else Usage( argv[0] ); + } else Usage( argv[0] ); + break; + case 'v': + vFlag = TRUE; + break; + case 'r': + rFlag = TRUE; + break; + case 'i': + iFlag = TRUE; + break; + case 't': + tFlag = TRUE; + break; + case 'x': + if( j == 1 && strcmp( &argv[argno][j], "x86" ) != 0 ) + Usage( argv[0] ); + si.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL; + j += 2; + break; + case 'm': + case 'j': + if( j == 1 && strcmp( &argv[argno][1], "jazz" ) != 0 && strcmp(&argv[argno][1], "mips" ) != 0 ) + Usage( argv[0] ); + si.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_MIPS; + j += 3; + break; + case 'a': + if( j == 1 && strcmp( &argv[argno][j], "alpha" ) != 0 ) + Usage( argv[0] ); + si.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_ALPHA; + j += 4; + break; + default: + if( isdigit( argv[argno][j] )) { + int NTRelNo = atoi( &argv[argno][j] ); + int MaxNTServers; + j += strlen( &argv[argno][j] ) - 1; + + if( si.wProcessorArchitecture == 0 ) + GetSystemInfo( &si ); + + switch( si.wProcessorArchitecture ) { + case PROCESSOR_ARCHITECTURE_INTEL: + p = "x86"; + MaxNTServers = 5; + break; + case PROCESSOR_ARCHITECTURE_MIPS: + p = "jazz"; + MaxNTServers = 2; + break; + case PROCESSOR_ARCHITECTURE_ALPHA: + p = "alpha"; + MaxNTServers = 2; + break; + case PROCESSOR_ARCHITECTURE_PPC: + p = "ppc"; + MaxNTServers = 2; + break; + default: + errorexit( "Sorry, don't know server name for your processor type\n" ); + ExitProcess(1); + break; + } + msg( "Fetch %s public release %03u from %d \\\\nt%s servers...\n", + cFlag ? "checked" : "free", + NTRelNo, MaxNTServers, p ); + for( int svr = 1; svr <= MaxNTServers ; svr++ ) { + sprintf( dest, "\\\\nt%s%d\\%spub.%03u", + p, svr, + cFlag ? "chk" : "free", + NTRelNo ); + SourceList[ MaxSources ].name = (char *)malloc(strlen(dest)+2); + if( SourceList[ MaxSources ].name == NULL ) { + errorexit( "Out of memory!\n" ); + ExitProcess(1); + } + strcpy( SourceList[ MaxSources++ ].name, dest ); + } + break; + } + case '?': + Usage( argv[0] ); + break; + + } + } + +nextarg: + ; + + } + + for( ; argno < argc-1; argno++ ) { + if( rootpath( argv[argno], dest ) == FALSE ) { + errorexit( "invalid source\n" ); + ExitProcess(1); + } + for( p = dest; *p; p++ ) + if( *p == '/' ) + *p = '\\'; + if( (SourceList[ MaxSources ].name = (char *)malloc( strlen( dest ) + 2 )) == NULL ) { + errorexit( "Out of memory!\n" ); + ExitProcess(1); + } + strcpy( SourceList[ MaxSources++ ].name, dest ); + } + + if( MaxSources == 0 ) + Usage( argv[0] ); + + for( i=0; i < MaxDirectoryExcludes; i++ ) + msg( "Exclude Directory: %s\n", DirectoryExcludeList[i] ); + + while( 1 ) { + char statusbuffer[ MAX_PATH ]; + + for( i=0; i < MaxSources; i++ ) { + appendslash( SourceList[i].name ); + if( vFlag == TRUE || wFlag == FALSE ) + msg( "Validating %s....", SourceList[i].name ); + sprintf( statusbuffer, "Validating %s", SourceList[i].name ); + SetConsoleTitle( statusbuffer ); + if( GetFileAttributes( SourceList[i].name ) == 0xFFFFFFFF ) { + if( vFlag == TRUE || wFlag == FALSE ) + msg( "[DISABLING %s]\n", SourceList[i].name ); + SourceList[i].flags.valid = FALSE; + } else { + SourceList[i].flags.valid = TRUE; + if( vFlag == TRUE || wFlag == FALSE ) + msg( "[OK]\n" ); + } + } + + for( i=0; i < MaxSources; i++ ) + if( SourceList[i].flags.valid == TRUE ) + break; + + if( i != MaxSources ) + break; + + if( wFlag == TRUE ) { + SetConsoleTitle( "Sleeping awhile..." ); + Sleep( 3 * 1000 * 60 ); + } else { + SetConsoleTitle( OldConsoleTitle ); + ExitProcess(1); + } + } + + SetConsoleTitle( "Sources Present" ); + LONG cThreads = (MaxSources * 3) + 1; + + /* + * hack for ftc -w -678 to exit when the release shares are available + */ + if( argno == argc && wFlag ) + ExitProcess( 0 ); + + if ( argno != argc - 1 ) { + Usage( argv[0] ); + } else if (rootpath (argv[argno], dest) == FALSE ) { + errorexit( "Invalid destination\n" ); + ExitProcess(1); + } + + for( p = dest; *p; p++ ) + if( *p == '/' ) + *p = '\\'; + + for( i=0; i < MaxSources; i++ ) + if (!strcmp(SourceList[i].name, dest)) { + errorexit("Source == dest == %s", SourceList[i].name ); + ExitThread(1); + } + + /* + * Create the semaphores for the work lists + */ + sa.nLength = sizeof( sa ); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + if( (hWorkAvailSem = CreateSemaphore( &sa, 0, 100000, NULL)) == NULL ) { + errorexit( "Unable to create semaphore (err %u)!\n", GetLastError() ); + ExitProcess(1); + } + + hMaxWorkQueueSem = CreateSemaphore( &sa, MAXQUEUE, MAXQUEUE, NULL ); + if( hMaxWorkQueueSem == NULL ) { + errorexit( "Unable to create queue length semaphore (err %u)!\n", GetLastError() ); + ExitProcess( 1 ); + } + + /* + * Create the thread pool to do the copies + */ + for( i=0; i < cThreads - 1; i++ ) { + CThread = CreateThread( (LPSECURITY_ATTRIBUTES)NULL, 0, + ThreadWorker, (LPVOID *)i, 0, &IDThread ); + if( CThread == NULL || CThread == INVALID_HANDLE_VALUE ) + break; + SetThreadPriority( CThread, THREAD_PRIORITY_NORMAL ); + CThread = INVALID_HANDLE_VALUE; + } + + /* + * Create the 'update status' thread + */ + CThread = CreateThread( (LPSECURITY_ATTRIBUTES)NULL, 0, + StatusWorker,(LPVOID *)0,0,&IDThread ); + if( CThread != NULL && CThread != INVALID_HANDLE_VALUE ) { +// SetThreadPriority( CThread, THREAD_PRIORITY_BELOW_NORMAL ); + CThread = INVALID_HANDLE_VALUE; + } + + SetConsoleCtrlHandler( ControlHandlerRoutine, TRUE ); + + /* + * Produce the directory list + */ + relpath[0] = '\0'; + dwElapsedTime = GetTickCount(); + + SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL ); + ScanDirectory( relpath, dest ); + + if( lFlag ) + SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS ); + + SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL ); + + // OK, now hWorkAvailSem has a count for every item to copy. But when they + // finish copying, each thread waits on this semaphore again. At the end, + // everyone will still be waiting! So, add the number of threads to the + // count, so each thread notices, one by one, that everything's done. + ReleaseSemaphore( hWorkAvailSem, (int)cThreads+1, NULL ); + + if( bThreadStop == FALSE ) + ThreadWorker( 0 ); + + return ExitCode; +} diff --git a/private/sdktools/ftc/makefile b/private/sdktools/ftc/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/sdktools/ftc/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/sdktools/ftc/sources b/private/sdktools/ftc/sources new file mode 100644 index 000000000..7fadaf70f --- /dev/null +++ b/private/sdktools/ftc/sources @@ -0,0 +1,18 @@ +MAJORCOMP= sdktools +MINORCOMP= ftc + +TARGETNAME= ftc +TARGETTYPE= PROGRAM +TARGETPATH= obj + +UMTYPE= console +UMENTRY= main + +USE_LIBCMT= 1 + +SOURCES= ftc.cxx app.rc + +!IFNDEF MSC_WARNING_LEVEL +MSC_WARNING_LEVEL=/W3 +!ENDIF +MSC_WARNING_LEVEL=$(MSC_WARNING_LEVEL) /WX |