Leaked source code of windows server 2003
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.

1267 lines
39 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996 - 1996.
  5. //
  6. // File: ftc.cxx
  7. //
  8. // Contents: Fast multi-threaded tree copy program.
  9. //
  10. // History: ?-?-94 IsaacHe Created
  11. // 11-Jun-96 BruceFo Fixed bugs, put this header here.
  12. //
  13. //--------------------------------------------------------------------------
  14. #if defined( UNICODE )
  15. #undef UNICODE
  16. #endif
  17. #include <windows.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <malloc.h>
  22. #include <direct.h>
  23. #include <sys\types.h>
  24. #include <sys\stat.h>
  25. #include <io.h>
  26. #include <conio.h>
  27. #include <errno.h>
  28. #include <process.h>
  29. #include <ctype.h>
  30. #define MAXQUEUE 10000
  31. #define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0]))
  32. /*
  33. * These are the attributes we use to compare for file attribute identity
  34. */
  35. const DWORD FILE_ATTRIBUTE_MASK = FILE_ATTRIBUTE_READONLY |
  36. FILE_ATTRIBUTE_HIDDEN |
  37. FILE_ATTRIBUTE_SYSTEM |
  38. FILE_ATTRIBUTE_ARCHIVE;
  39. class CProtectedLong
  40. {
  41. CRITICAL_SECTION _cs;
  42. LONG _value;
  43. public:
  44. CProtectedLong() { InitializeCriticalSection( &_cs ); _value = 0; }
  45. LONG operator++(int) {
  46. EnterCriticalSection( &_cs );
  47. LONG tmp = _value++;
  48. LeaveCriticalSection( &_cs );
  49. return tmp;
  50. }
  51. LONG operator--( int ) {
  52. EnterCriticalSection( &_cs );
  53. LONG tmp = _value--;
  54. LeaveCriticalSection( &_cs );
  55. return tmp;
  56. }
  57. LONG operator+=( LONG incr ) {
  58. EnterCriticalSection( &_cs );
  59. LONG tmp = (_value += incr );
  60. LeaveCriticalSection( &_cs );
  61. return tmp;
  62. }
  63. LONG operator=( LONG val ) {
  64. EnterCriticalSection( &_cs );
  65. _value = val;
  66. LeaveCriticalSection( &_cs );
  67. return val;
  68. }
  69. operator LONG() { return _value; }
  70. operator int() { return _value; }
  71. };
  72. class CHandle
  73. {
  74. HANDLE _h;
  75. public:
  76. CHandle() : _h(INVALID_HANDLE_VALUE) { }
  77. ~CHandle();
  78. HANDLE operator=( HANDLE h );
  79. BOOL operator==( HANDLE h ) { return (h == _h) ? TRUE : FALSE; }
  80. BOOL operator!=( HANDLE h ) { return (h != _h) ? TRUE : FALSE; }
  81. operator HANDLE() { return _h; }
  82. };
  83. CHandle::~CHandle()
  84. {
  85. if( _h != INVALID_HANDLE_VALUE && _h != NULL )
  86. CloseHandle( _h );
  87. }
  88. HANDLE
  89. CHandle::operator=( HANDLE h )
  90. {
  91. if( _h != INVALID_HANDLE_VALUE && _h != NULL )
  92. CloseHandle( _h );
  93. return _h = h;
  94. }
  95. DWORD dwElapsedTime; // time we've been copying data
  96. CProtectedLong ulTotalBytesCopied; // running total count of bytes
  97. CProtectedLong ulTotalBytesSkipped; // obvious?
  98. CProtectedLong ulTotalBytesScanned;
  99. CProtectedLong nFilesOnQueue; // number of files to copy or examine
  100. CProtectedLong nFcopy; // number of files copied
  101. CProtectedLong nSkipped; // number of files skipped over
  102. CProtectedLong nMappedCopy; // number of files copied using MapFile...
  103. CProtectedLong nCopyFile; // number of files copied using CopyFile()...
  104. CProtectedLong nInProgress; // number of copies currently in progress
  105. CProtectedLong MaxThreads; // Max number of threads for copying
  106. DWORD ExitCode = 0; // each thread's exit code
  107. BOOL bThreadStop = FALSE; // are we trying to exit?
  108. BOOL bWorkListComplete = FALSE; // have we scanned all the directories yet?
  109. BOOL tFlag = FALSE; // only copy if newer
  110. BOOL iFlag = FALSE; // skip seemingly identical files
  111. BOOL rFlag = FALSE; // replace read-only files
  112. BOOL vFlag = FALSE; // verbose
  113. BOOL AFlag = FALSE; // keep going even if there are errors
  114. BOOL FFlag = FALSE; // just produce file list. No copies
  115. BOOL oFlag = TRUE; // should we overwrite files already at dest?
  116. BOOL wFlag = FALSE; // should we wait for the source to show up?
  117. BOOL qFlag = FALSE; // quiet mode?
  118. BOOL yFlag = FALSE; // no recurse on target?
  119. BOOL zFlag = FALSE; // no recurse on source?
  120. BOOL pFlag = FALSE; // pattern?
  121. CHAR szPattern[100]; // pattern string, if pFlag is TRUE
  122. struct WorkList // copy file at 'src' to 'dest'
  123. {
  124. struct WorkList *next;
  125. char *src; // Pathname relative to the source
  126. WIN32_FIND_DATA srcfind;
  127. char *dest;
  128. } *WorkList = NULL;
  129. struct WorkList* WorkListTail = NULL; // always add files to copy to the *tail*
  130. // of the work list. This is to make
  131. // sure we always do work in order, instead
  132. // of starting on a directory but finishing
  133. // it much much much later, because we've
  134. // pushed all the work to the deep tail of
  135. // the list and never returned to it!
  136. CHandle hWorkAvailSem; // signalled whenever there's work on the list
  137. CHandle hMaxWorkQueueSem; // used to control lenght of work queue
  138. CRITICAL_SECTION csMsg; // used to serialize screen output
  139. CRITICAL_SECTION csWorkList; // used when manipulating the linked list
  140. CRITICAL_SECTION csSourceList; // used when manipulating the source list
  141. struct SourceList
  142. {
  143. char *name; // pathname of the source. Ends in '\'
  144. LONG count; // number of files currently being copied
  145. LONG ulTotalFiles; // total for the entire copy
  146. struct {
  147. unsigned valid : 1; // do we know that the source is valid?
  148. } flags;
  149. } SourceList[ 20 ];
  150. int MaxSources = 0;
  151. char *DirectoryExcludeList[ 50 ];
  152. int MaxDirectoryExcludes = 0;
  153. char OldConsoleTitle[ 100 ];
  154. void
  155. __cdecl
  156. errormsg( char const *pszfmt, ... )
  157. {
  158. va_list ArgList;
  159. va_start( ArgList, pszfmt );
  160. if( bThreadStop == FALSE && pszfmt != NULL ) {
  161. EnterCriticalSection( &csMsg );
  162. vprintf( pszfmt, ArgList );
  163. LeaveCriticalSection( &csMsg );
  164. }
  165. va_end( ArgList );
  166. }
  167. DWORD __stdcall
  168. StatusWorker( void *arg )
  169. {
  170. char ostatbuf[ 100 ];
  171. char nstatbuf[ 100 ];
  172. while( bThreadStop == FALSE ) {
  173. ULONG Remaining = (int)ulTotalBytesScanned - (int)ulTotalBytesCopied - (int)ulTotalBytesSkipped;
  174. if (Remaining < 1000) {
  175. sprintf(nstatbuf,
  176. "Remaining Files %d Bytes %d",
  177. (int)nFilesOnQueue,
  178. Remaining);
  179. } else if (Remaining < 1000000) {
  180. sprintf(nstatbuf,
  181. "Remaining Files %d Bytes %d,%03.3d",
  182. (int)nFilesOnQueue,
  183. Remaining / 1000,
  184. Remaining % 1000);
  185. } else if (Remaining < 1000000000) {
  186. sprintf(nstatbuf,
  187. "Remaining Files %d Bytes %d,%03.3d,%03.3d",
  188. (int)nFilesOnQueue,
  189. Remaining / 1000000,
  190. (Remaining / 1000) % 1000,
  191. Remaining % 1000,
  192. Remaining);
  193. } else {
  194. sprintf(nstatbuf,
  195. "Remaining Files %d Bytes %d,%03.3d,%03.3d,%03.3d",
  196. (int)nFilesOnQueue,
  197. Remaining / 1000000000,
  198. (Remaining / 1000000) % 1000,
  199. (Remaining / 1000) % 1000,
  200. Remaining % 1000);
  201. }
  202. if( strcmp( ostatbuf, nstatbuf ) ) {
  203. SetConsoleTitle( nstatbuf );
  204. strcpy( ostatbuf, nstatbuf );
  205. }
  206. Sleep( 1 * 1000 );
  207. }
  208. SetConsoleTitle( OldConsoleTitle );
  209. ExitThread( ExitCode );
  210. arg = arg;
  211. return 0;
  212. }
  213. void
  214. __cdecl
  215. msg( char const *pszfmt, ... )
  216. {
  217. if( qFlag )
  218. return;
  219. va_list ArgList;
  220. va_start( ArgList, pszfmt );
  221. EnterCriticalSection( &csMsg );
  222. vprintf( pszfmt, ArgList );
  223. LeaveCriticalSection( &csMsg );
  224. va_end( ArgList );
  225. }
  226. void
  227. __cdecl
  228. errorexit (char const *pszfmt, ... )
  229. {
  230. if( bThreadStop == FALSE && pszfmt != NULL ) {
  231. va_list ArgList;
  232. va_start( ArgList, pszfmt );
  233. EnterCriticalSection( &csMsg );
  234. vprintf( pszfmt, ArgList );
  235. LeaveCriticalSection( &csMsg );
  236. va_end( ArgList );
  237. }
  238. if( AFlag == FALSE ) {
  239. bThreadStop = TRUE;
  240. EnterCriticalSection( &csWorkList );
  241. WorkList = NULL;
  242. WorkListTail = NULL;
  243. LeaveCriticalSection( &csWorkList );
  244. if( hWorkAvailSem != NULL )
  245. ReleaseSemaphore( hWorkAvailSem, (int)MaxThreads+1, NULL );
  246. if( hMaxWorkQueueSem != NULL )
  247. ReleaseSemaphore( hMaxWorkQueueSem, 1, NULL );
  248. SetConsoleTitle( OldConsoleTitle );
  249. ExitThread (ExitCode = 1);
  250. }
  251. }
  252. DWORD
  253. fcopy( char *src, WIN32_FIND_DATA *srcfind, char *dst, char *errorbuf )
  254. {
  255. CHandle srcfh;
  256. CHandle dstfh;
  257. CHandle hsrc;
  258. DWORD nBytesWritten, totalbytes;
  259. char *result = NULL;
  260. char *psrc;
  261. BOOL ret = TRUE;
  262. char dostatus = 0;
  263. DWORD errcode;
  264. *errorbuf = '\0';
  265. if( srcfind->nFileSizeHigh != 0 ) {
  266. if( CopyFile( src, dst, TRUE ) == FALSE ) {
  267. errcode = GetLastError();
  268. sprintf( errorbuf, "CopyFile failed, error %d", errcode );
  269. return errcode;
  270. }
  271. nCopyFile++;
  272. } else {
  273. srcfh = CreateFile( src, GENERIC_READ, FILE_SHARE_READ, NULL,
  274. OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
  275. if( srcfh == INVALID_HANDLE_VALUE ) {
  276. errcode = GetLastError();
  277. sprintf( errorbuf, "Unable to open source file, error %d", errcode);
  278. return errcode;
  279. }
  280. dstfh = CreateFile( dst, GENERIC_WRITE, FILE_SHARE_WRITE,NULL,
  281. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, srcfh);
  282. if( dstfh == INVALID_HANDLE_VALUE ) {
  283. errcode = GetLastError();
  284. sprintf( errorbuf, "Unable to create dest file, error %d", errcode);
  285. return errcode;
  286. }
  287. if( srcfind->nFileSizeLow != 0 ) {
  288. hsrc = CreateFileMapping( srcfh, NULL, PAGE_READONLY, 0,
  289. srcfind->nFileSizeLow, NULL );
  290. if( hsrc == NULL ) {
  291. dstfh = INVALID_HANDLE_VALUE;
  292. DeleteFile( dst );
  293. if( CopyFile( src, dst, TRUE ) == FALSE ) {
  294. errcode = GetLastError();
  295. sprintf( errorbuf, "Unable to create file mapping, and CopyFile failed, error %d", errcode );
  296. return errcode;
  297. }
  298. nCopyFile++;
  299. ulTotalBytesCopied += srcfind->nFileSizeLow;
  300. goto DoTime;
  301. }
  302. if( (psrc = (char *)MapViewOfFile( hsrc, FILE_MAP_READ, 0, 0, 0 )) == NULL){
  303. dstfh = INVALID_HANDLE_VALUE;
  304. DeleteFile( dst );
  305. if( CopyFile( src, dst, TRUE ) == FALSE ) {
  306. errcode = GetLastError();
  307. sprintf( errorbuf, "Unable to map source file, and CopyFile failed: error %d", errcode );
  308. return errcode;
  309. }
  310. nCopyFile++;
  311. ulTotalBytesCopied += srcfind->nFileSizeLow;
  312. goto DoTime;
  313. }
  314. totalbytes = 0;
  315. while( !bThreadStop && totalbytes < srcfind->nFileSizeLow && ret == TRUE ) {
  316. ret = WriteFile( dstfh,
  317. psrc + totalbytes,
  318. min( 64*1024, srcfind->nFileSizeLow - totalbytes ),
  319. &nBytesWritten, NULL );
  320. totalbytes += nBytesWritten;
  321. ulTotalBytesCopied += nBytesWritten;
  322. }
  323. errcode = GetLastError();
  324. UnmapViewOfFile( psrc );
  325. if( bThreadStop == TRUE ) {
  326. dstfh = INVALID_HANDLE_VALUE;
  327. DeleteFile( dst );
  328. *errorbuf = '\0';
  329. return errcode;
  330. }
  331. if( ret == FALSE ) {
  332. dstfh = INVALID_HANDLE_VALUE;
  333. DeleteFile( dst );
  334. if( CopyFile( src, dst, TRUE ) == FALSE ) {
  335. errcode = GetLastError();
  336. sprintf( errorbuf, "%s: CopyFile failed: error %d", dst, errcode);
  337. return GetLastError();
  338. }
  339. nCopyFile++;
  340. ulTotalBytesCopied += srcfind->nFileSizeLow;
  341. goto DoTime;
  342. }
  343. nMappedCopy++;
  344. }
  345. }
  346. DoTime:
  347. if( dstfh == INVALID_HANDLE_VALUE ) {
  348. dstfh = CreateFile( dst, GENERIC_WRITE,
  349. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
  350. 0, NULL );
  351. }
  352. if( dstfh == INVALID_HANDLE_VALUE ) {
  353. errcode = GetLastError();
  354. DeleteFile( dst );
  355. sprintf( errorbuf, "Unable to open destination file to set time, error %d", errcode );
  356. return errcode;
  357. }
  358. if( !SetFileTime( dstfh, &srcfind->ftCreationTime,
  359. &srcfind->ftLastAccessTime, &srcfind->ftLastWriteTime )) {
  360. errcode = GetLastError();
  361. sprintf( errorbuf, "Unable to set destination file times, error %d\n", errcode );
  362. return errcode;
  363. }
  364. nFcopy++;
  365. return 0;
  366. }
  367. /*
  368. * Pick the source having the fewest outstanding operations at the moment.
  369. */
  370. int
  371. SelectSource()
  372. {
  373. int index = -1;
  374. struct SourceList *psl;
  375. struct SourceList *opsl;
  376. static min;
  377. EnterCriticalSection( &csSourceList );
  378. //
  379. // Find the first valid source
  380. //
  381. for( opsl = SourceList; opsl < &SourceList[ MaxSources ]; opsl++ )
  382. if( opsl->flags.valid == TRUE )
  383. break;
  384. //
  385. // Now locate the source having the fewest pending operations right now
  386. //
  387. for( psl = opsl+1; psl < &SourceList[ MaxSources ]; psl++ ) {
  388. if( psl->flags.valid == TRUE && psl->count < opsl->count )
  389. opsl = psl;
  390. }
  391. if( opsl->flags.valid == TRUE ) {
  392. opsl->count++;
  393. index = (int)(opsl - SourceList);
  394. }
  395. LeaveCriticalSection( &csSourceList );
  396. return index;
  397. }
  398. /*
  399. * We've completed the operation on source 'index'
  400. */
  401. void
  402. SourceCopyComplete( int index, BOOL fFile )
  403. {
  404. EnterCriticalSection( &csSourceList );
  405. SourceList[ index ].count--;
  406. if (fFile) SourceList[ index ].ulTotalFiles++;
  407. LeaveCriticalSection( &csSourceList );
  408. }
  409. void
  410. DisableSource( int index )
  411. {
  412. if( index >= 0 && index < MaxSources ) {
  413. EnterCriticalSection( &csSourceList );
  414. if( SourceList[ index ].flags.valid == TRUE ) {
  415. errormsg( "Disabling %s\n", SourceList[ index ].name );
  416. SourceList[ index ].flags.valid = FALSE;
  417. }
  418. LeaveCriticalSection( &csSourceList );
  419. }
  420. }
  421. BOOL
  422. FileTimesEqual( CONST FILETIME *pt1, CONST FILETIME *pt2 )
  423. {
  424. SYSTEMTIME s1, s2;
  425. if( !FileTimeToSystemTime( pt1, &s1 ) || !FileTimeToSystemTime( pt2, &s2 ) )
  426. return FALSE;
  427. return s1.wHour == s2.wHour &&
  428. s1.wMinute == s2.wMinute &&
  429. s1.wMonth == s2.wMonth &&
  430. s1.wDay == s2.wDay &&
  431. s1.wYear == s2.wYear;
  432. }
  433. void
  434. PrintFileTime( char *str, CONST FILETIME *ft )
  435. {
  436. SYSTEMTIME st;
  437. if( FileTimeToSystemTime( ft, &st ) == FALSE ) {
  438. errormsg( "????\n" );
  439. return;
  440. }
  441. msg( "%s %u:%u.%u.%u %u/%u/%u\n", str,
  442. st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
  443. st.wMonth, st.wDay, st.wYear );
  444. }
  445. DWORD __stdcall
  446. ThreadWorker( void *arg )
  447. {
  448. struct WorkList *pdl = NULL;
  449. HANDLE hdestfind;
  450. WIN32_FIND_DATA destfind;
  451. int index = 0;
  452. char errorbuf[ 100 ];
  453. char pathbuf[ MAX_PATH ];
  454. DWORD errcode;
  455. MaxThreads++;
  456. while( 1 ) {
  457. if( pdl != NULL ) {
  458. free( pdl->src );
  459. free( pdl->dest );
  460. free( pdl );
  461. pdl = NULL;
  462. }
  463. if( bThreadStop == TRUE )
  464. break;
  465. // Poll for new stuff every 2 seconds. If the thread is set to stop,
  466. // then go away.
  467. DWORD dwWait;
  468. while( 1 )
  469. {
  470. dwWait = WaitForSingleObject( hWorkAvailSem, 1000 );
  471. if( dwWait == WAIT_OBJECT_0 ) {
  472. break;
  473. }
  474. if( dwWait == WAIT_TIMEOUT ) {
  475. if( bThreadStop == TRUE )
  476. break;
  477. } else {
  478. errormsg( "Thread %p: Semaphore wait failed\n", arg );
  479. break;
  480. }
  481. }
  482. if ( dwWait != WAIT_OBJECT_0 ) {
  483. break;
  484. }
  485. // pick an item off the head of the work list
  486. EnterCriticalSection( &csWorkList );
  487. pdl = WorkList;
  488. if( pdl != NULL ) {
  489. WorkList = pdl->next;
  490. if (NULL == WorkList) {
  491. // just pulled off the tail entry
  492. WorkListTail = NULL;
  493. }
  494. }
  495. LeaveCriticalSection( &csWorkList );
  496. ReleaseSemaphore( hMaxWorkQueueSem, 1, NULL );
  497. if( pdl == NULL )
  498. break;
  499. nFilesOnQueue--;
  500. if( pdl->srcfind.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
  501. errormsg( "Logic Error: Directory on work list!\n" );
  502. continue;
  503. }
  504. pdl->srcfind.dwFileAttributes &= FILE_ATTRIBUTE_MASK;
  505. hdestfind = FindFirstFile( pdl->dest, &destfind );
  506. if( hdestfind != INVALID_HANDLE_VALUE ) {
  507. FindClose( hdestfind );
  508. destfind.dwFileAttributes &= FILE_ATTRIBUTE_MASK;
  509. /*
  510. * Destination file exists. What should we do?
  511. */
  512. if( oFlag == FALSE ) {
  513. /*
  514. * We should not overwrite the existing file at the dest
  515. */
  516. if( vFlag )
  517. msg( "%s [SKIP: exists]\n", pdl->dest );
  518. ulTotalBytesSkipped += destfind.nFileSizeLow;
  519. nSkipped++;
  520. continue;
  521. }
  522. if( iFlag && vFlag ) {
  523. if( destfind.dwFileAttributes !=pdl->srcfind.dwFileAttributes)
  524. msg( "%s [ ATTRIBUTES differ ]\n", pdl->dest );
  525. if(!FileTimesEqual( &destfind.ftLastWriteTime, &pdl->srcfind.ftLastWriteTime)) {
  526. EnterCriticalSection( &csMsg );
  527. msg( "%s [ TIMES differ ]\n", pdl->dest );
  528. PrintFileTime( "Dest: ", &destfind.ftLastWriteTime );
  529. PrintFileTime( "Src: ", &pdl->srcfind.ftLastWriteTime );
  530. LeaveCriticalSection( &csMsg );
  531. }
  532. if( (destfind.nFileSizeHigh != pdl->srcfind.nFileSizeHigh) ||
  533. (destfind.nFileSizeLow != pdl->srcfind.nFileSizeLow) )
  534. msg( "%s [ SIZES differ ]\n", pdl->dest );
  535. }
  536. if( iFlag &&
  537. (destfind.dwFileAttributes == pdl->srcfind.dwFileAttributes) &&
  538. FileTimesEqual( &destfind.ftLastWriteTime, &pdl->srcfind.ftLastWriteTime) &&
  539. (destfind.nFileSizeHigh == pdl->srcfind.nFileSizeHigh) &&
  540. (destfind.nFileSizeLow == pdl->srcfind.nFileSizeLow) ) {
  541. if( vFlag )
  542. msg("%s [SKIP: same atts, time, size]\n",pdl->dest);
  543. ulTotalBytesSkipped += destfind.nFileSizeLow;
  544. nSkipped++;
  545. continue;
  546. }
  547. if( tFlag &&
  548. CompareFileTime( &destfind.ftLastWriteTime, &pdl->srcfind.ftLastWriteTime) >= 0 ) {
  549. if( vFlag )
  550. msg("%s [SKIP: same or newer time]\n", pdl->dest );
  551. ulTotalBytesSkipped += destfind.nFileSizeLow;
  552. nSkipped++;
  553. continue;
  554. }
  555. if( destfind.dwFileAttributes & FILE_ATTRIBUTE_READONLY )
  556. if( rFlag == FALSE && bThreadStop == FALSE ) {
  557. if( vFlag )
  558. msg( "%s [SKIP: readonly]\n", pdl->dest );
  559. ulTotalBytesSkipped += destfind.nFileSizeLow;
  560. nSkipped++;
  561. continue;
  562. }
  563. /*
  564. * Delete the destination file
  565. */
  566. if( destfind.dwFileAttributes & FILE_ATTRIBUTE_READONLY ) {
  567. destfind.dwFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
  568. SetFileAttributes( pdl->dest, destfind.dwFileAttributes );
  569. }
  570. if( FFlag == FALSE )
  571. (void)DeleteFile( pdl->dest );
  572. }
  573. if( FFlag == FALSE ) {
  574. while( bThreadStop == FALSE && (index = SelectSource()) >= 0 ) {
  575. strcpy( pathbuf, SourceList[index].name );
  576. strcat( pathbuf, pdl->src );
  577. nInProgress++;
  578. errcode = fcopy( pathbuf,&pdl->srcfind,pdl->dest,errorbuf);
  579. nInProgress--;
  580. if( errcode == 0 ) {
  581. SourceCopyComplete( index, TRUE );
  582. msg("%s -> %s [OK]\n", pathbuf, pdl->dest );
  583. SetFileAttributes(pdl->dest,pdl->srcfind.dwFileAttributes);
  584. break;
  585. }
  586. if( errcode == ERROR_SWAPERROR ) {
  587. errormsg( "%s [ SWAP ERROR, will try again... ]\n",pathbuf);
  588. Sleep( 5 * 1000 * 60 );
  589. continue;
  590. }
  591. if( bThreadStop == FALSE )
  592. errormsg( "%s [FAILED: %s ]\n", pathbuf, errorbuf );
  593. if( AFlag == TRUE )
  594. break;
  595. DisableSource( index );
  596. }
  597. } else {
  598. msg( "%s\n", pdl->dest );
  599. }
  600. if( AFlag == FALSE && index < 0 )
  601. errorexit( "%s [FAILED completely]\n", pdl->dest );
  602. }
  603. if( pdl != NULL ) {
  604. free( pdl->src );
  605. free( pdl->dest );
  606. free( pdl );
  607. }
  608. if( MaxThreads-- == 1 ) {
  609. if( ExitCode == 0 ) {
  610. dwElapsedTime = GetTickCount() - dwElapsedTime;
  611. dwElapsedTime /= 1000;
  612. BOOL oldqFlag = qFlag;
  613. qFlag = FALSE;
  614. msg( "%u files copied (%u memory mappped, %u CopyFile )\n",
  615. (int)nFcopy, (int)nMappedCopy, (int)nCopyFile );
  616. msg( "%u files skipped\n", (int)nSkipped);
  617. msg( "%lu bytes in %u seconds: %lu bits/sec\n",
  618. (int)ulTotalBytesCopied, dwElapsedTime,
  619. dwElapsedTime ? (LONG)(((LONG)ulTotalBytesCopied*8L)/dwElapsedTime) : 0L );
  620. qFlag = oldqFlag;
  621. EnterCriticalSection( &csSourceList );
  622. for (int i = 0; i < MaxSources; i++)
  623. {
  624. if (TRUE == SourceList[i].flags.valid)
  625. {
  626. msg( "%s %5lu files\n", SourceList[i].name, SourceList[i].ulTotalFiles);
  627. }
  628. }
  629. LeaveCriticalSection( &csSourceList );
  630. }
  631. SetConsoleTitle( OldConsoleTitle );
  632. ExitProcess( ExitCode );
  633. }
  634. ExitThread( ExitCode );
  635. return 0;
  636. }
  637. void
  638. AddToWorkList( char *src, char *dest, WIN32_FIND_DATA *pfind )
  639. {
  640. struct WorkList *pdl;
  641. if( WaitForSingleObject( hMaxWorkQueueSem, INFINITE ) != WAIT_OBJECT_0 ) {
  642. errormsg( "Semaphore wait failed, can't add to work list\n" );
  643. return;
  644. }
  645. if( bThreadStop == TRUE )
  646. return;
  647. if( (pdl = (struct WorkList *)malloc( sizeof( struct WorkList ) ) ) == NULL ){
  648. errorexit( "Out of Memory!\n" );
  649. return;
  650. }
  651. if( (pdl->dest = _strdup( dest )) == NULL ) {
  652. errorexit( "Out of memory!\n" );
  653. free( pdl );
  654. return;
  655. }
  656. if( (pdl->src = _strdup( src )) == NULL ) {
  657. errorexit( "Out of memory!\n" );
  658. free( pdl->dest );
  659. free( pdl );
  660. return;
  661. }
  662. pdl->srcfind = *pfind;
  663. pdl->next = NULL;
  664. EnterCriticalSection( &csWorkList );
  665. if (NULL == WorkList) {
  666. WorkListTail = WorkList = pdl;
  667. } else {
  668. WorkListTail->next = pdl; // point the tail to the new entry
  669. WorkListTail = pdl; // the new entry becomes the tail
  670. }
  671. LeaveCriticalSection( &csWorkList );
  672. nFilesOnQueue++;
  673. ReleaseSemaphore( hWorkAvailSem, 1, NULL );
  674. }
  675. void
  676. ScanDirectory(
  677. char *relpath,
  678. char *dest
  679. );
  680. void
  681. ScanDirectoryHelp(
  682. char *relpath, // path relative to the source
  683. char *dest, // resulting destination directory
  684. BOOL fOnlyDirectories, // TRUE if we only want to look for dirs
  685. BOOL fOnlyFiles // TRUE if we only want to look for files
  686. )
  687. {
  688. int index;
  689. int destlen = strlen( dest );
  690. int rellen = strlen( relpath );
  691. WIN32_FIND_DATA fbuf;
  692. HANDLE hfind = INVALID_HANDLE_VALUE;
  693. char SourceName[ MAX_PATH ];
  694. while( 1 ) {
  695. if( (index = SelectSource()) < 0 )
  696. return;
  697. /*
  698. * FindFirst/Next is such low overhead on the server that we shouldn't
  699. * really count it as a load on the server...
  700. */
  701. SourceCopyComplete( index, FALSE );
  702. strcpy( SourceName, SourceList[ index ].name );
  703. if( rellen ) {
  704. strcat( SourceName, relpath );
  705. strcat( SourceName, "\\" );
  706. }
  707. strcat( SourceName,
  708. fOnlyDirectories
  709. ? "*.*"
  710. : (pFlag ? szPattern : "*.*" ) );
  711. hfind = FindFirstFile( SourceName, &fbuf );
  712. if( hfind != INVALID_HANDLE_VALUE )
  713. break;
  714. if (pFlag && ERROR_FILE_NOT_FOUND == GetLastError()) {
  715. // simply no files that match the pattern in the directory
  716. return;
  717. }
  718. errormsg( "Dir scan of %s failed, error %d [DISABLING]\n", SourceName, GetLastError() );
  719. DisableSource( index );
  720. }
  721. do {
  722. if( !strcmp( fbuf.cFileName, "." ) || !strcmp( fbuf.cFileName, ".." ) )
  723. continue;
  724. sprintf( &dest[ destlen ], "%s%s",
  725. dest[destlen-1] == '\\' ? "" : "\\", fbuf.cFileName );
  726. if((fbuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 ) {
  727. //
  728. // Not a directory
  729. //
  730. if (fOnlyDirectories) {
  731. continue;
  732. }
  733. /*
  734. * Queue this file into the work queue!
  735. */
  736. if( rellen ) {
  737. strcpy( SourceName, relpath );
  738. strcat( SourceName, "\\" );
  739. } else
  740. SourceName[0] = '\0';
  741. strcat( SourceName, fbuf.cFileName );
  742. ulTotalBytesScanned += fbuf.nFileSizeLow;
  743. AddToWorkList( SourceName, dest, &fbuf );
  744. continue;
  745. }
  746. /*
  747. * We've found a directory. Descend into it and scan (if we're
  748. * not excluding it)
  749. */
  750. if (fOnlyFiles) {
  751. continue;
  752. }
  753. if (zFlag) { // no recurse on source: ignore it!
  754. continue;
  755. }
  756. for( int i=0; i < MaxDirectoryExcludes; i++ )
  757. if( !_stricmp( DirectoryExcludeList[i], fbuf.cFileName ) )
  758. break;
  759. if( i != MaxDirectoryExcludes ) {
  760. if( vFlag )
  761. msg( "Directory: %s [EXCLUDED]\n", dest );
  762. nSkipped++;
  763. continue;
  764. }
  765. sprintf( &relpath[ rellen ], "%s%s", rellen ? "\\":"", fbuf.cFileName );
  766. if (yFlag) { // no recurse on target: nuke end of dest (the new dir)
  767. dest[ destlen ] = '\0';
  768. }
  769. ScanDirectory( relpath, dest );
  770. relpath[ rellen ] = '\0';
  771. } while( !bThreadStop && FindNextFile( hfind, &fbuf ) == TRUE );
  772. dest[ destlen ] = '\0';
  773. FindClose( hfind );
  774. }
  775. void
  776. ScanDirectory(
  777. char *relpath, // path relative to the source
  778. char *dest // resulting destination directory
  779. )
  780. {
  781. DWORD dwattrs;
  782. if( (dwattrs = GetFileAttributes( dest )) == 0xFFFFFFFF ) {
  783. msg( "Creating Directory: %s\n", dest );
  784. if( FFlag == FALSE && CreateDirectory( dest, NULL ) == FALSE ) {
  785. errorexit( "Can not create directory: %s\n", dest );
  786. return;
  787. }
  788. } else if( !(dwattrs & FILE_ATTRIBUTE_DIRECTORY) ) {
  789. errorexit( "Not a directory: %s\n", dest );
  790. return;
  791. }
  792. if (pFlag) {
  793. // two passes: one looking for files, one looking for directories
  794. ScanDirectoryHelp(relpath, dest, FALSE, TRUE);
  795. ScanDirectoryHelp(relpath, dest, TRUE, FALSE);
  796. } else {
  797. ScanDirectoryHelp(relpath, dest, FALSE, FALSE);
  798. }
  799. }
  800. static void
  801. appendslash( char *p )
  802. {
  803. if( p[ strlen(p) - 1 ] != '\\' )
  804. strcat( p, "\\" );
  805. }
  806. BOOL
  807. rootpath( char *src, char *dst )
  808. {
  809. char* FilePart;
  810. char *p;
  811. if( src == NULL || *src == '\0' )
  812. return FALSE;
  813. if( GetFullPathName( src, MAX_PATH, dst, &FilePart ) == 0 )
  814. return FALSE;
  815. p = src + strlen(src) - 1;
  816. if( *p == '.' )
  817. if( p > src ) {
  818. p--;
  819. if( *p != '.' && *p != ':' && (*p == '\\' || *p == '/') )
  820. strcat( dst, "." );
  821. }
  822. return TRUE;
  823. }
  824. static void
  825. Usage( char *s )
  826. {
  827. errormsg( "Usage: %s [flags] [-p pattern] [src ...] dest\n", s );
  828. errormsg( "Flags:\n" );
  829. errormsg( "\t-i Skip seemingly identical files (time, attrs, size agree)\n" );
  830. errormsg( "\t-l Execute at lower priority\n" );
  831. errormsg( "\t-o Do not overwrite any files that are already at dest\n" );
  832. errormsg( "\t-p pattern Only files matching the pattern are copied\n" );
  833. errormsg( "\t-q Quiet mode\n" );
  834. errormsg( "\t-r Overwrite read-only files at dest\n" );
  835. errormsg( "\t-t Copy only newer files to dest\n" );
  836. errormsg( "\t-v Verbose\n" );
  837. errormsg( "\t-y Don't recurse on target\n" );
  838. errormsg( "\t-z Don't recurse on source\n" );
  839. errormsg( "\t-A Keep going even if there are errors\n" );
  840. errormsg( "\t-F Don't actually copy files or create directories\n" );
  841. errormsg( "\t~dir Skip any directory named 'dir'\n" );
  842. errormsg( "\nIf environment variable FTC_PARANOID is set, then the meaning of -o is\n" );
  843. errormsg( "reversed: no -o means don't overwrite, -o means go ahead and overwrite.\n" );
  844. errormsg( "\nExamples:\n" );
  845. errormsg( " Copy from two sources, no 'obj' dir: ftc ~obj \\\\foo\\dir \\\\bar\\dir dest\n" );
  846. ExitProcess( 1 );
  847. }
  848. BOOL __stdcall
  849. ControlHandlerRoutine( DWORD dwCtrlType )
  850. {
  851. msg( "Interrupted!\n" );
  852. bThreadStop = TRUE;
  853. ExitCode = 1;
  854. return TRUE;
  855. }
  856. int
  857. __cdecl
  858. main(int argc, char *argv[])
  859. {
  860. char *p;
  861. SECURITY_ATTRIBUTES sa;
  862. int i, argno;
  863. DWORD IDThread;
  864. char dest[ MAX_PATH ];
  865. char relpath[ MAX_PATH ];
  866. BOOL lFlag = FALSE; // low priority?
  867. BOOL fParanoid = FALSE; // is FTC_PARANOID set in the environment?
  868. SYSTEM_INFO si;
  869. CHandle CThread;
  870. InitializeCriticalSection( &csMsg );
  871. InitializeCriticalSection( &csWorkList );
  872. InitializeCriticalSection( &csSourceList );
  873. ZeroMemory(&si, sizeof( si ));
  874. GetConsoleTitle( OldConsoleTitle, sizeof( OldConsoleTitle ) );
  875. TCHAR szParanoid[100];
  876. DWORD len = GetEnvironmentVariable(TEXT("FTC_PARANOID"), szParanoid, ARRAYLEN(szParanoid));
  877. if (len > 0)
  878. {
  879. fParanoid = TRUE;
  880. }
  881. if (fParanoid)
  882. {
  883. oFlag = FALSE;
  884. }
  885. else
  886. {
  887. oFlag = TRUE;
  888. }
  889. for( argno = 1;
  890. argno < argc && (argv[argno][0] == '-' || argv[argno][0] == '/' || argv[argno][0] == '~') ;
  891. argno++ )
  892. {
  893. if( argv[argno][0] == '~' )
  894. {
  895. DirectoryExcludeList[ MaxDirectoryExcludes++ ] = &argv[argno][1];
  896. }
  897. else for( int j=1; argv[argno][j]; j++ )
  898. {
  899. switch( argv[argno][j] ) {
  900. case 'l':
  901. lFlag = TRUE;
  902. break;
  903. case 'q':
  904. qFlag = TRUE;
  905. break;
  906. case 'y':
  907. yFlag = TRUE;
  908. break;
  909. case 'z':
  910. zFlag = TRUE;
  911. break;
  912. case 'w':
  913. wFlag = TRUE;
  914. break;
  915. case 'o':
  916. if (fParanoid)
  917. {
  918. oFlag = TRUE;
  919. }
  920. else
  921. {
  922. oFlag = FALSE;
  923. }
  924. break;
  925. case 'F':
  926. FFlag = TRUE;
  927. break;
  928. case 'A':
  929. AFlag = TRUE;
  930. break;
  931. case 'p':
  932. if( j == 1 ) {
  933. if ( strcmp( &argv[argno][j], "p" ) == 0 ) {
  934. if (argno + 1 < argc) {
  935. pFlag = TRUE;
  936. strcpy( szPattern, argv[++argno] );
  937. goto nextarg; // go to next argument
  938. } else Usage( argv[0] );
  939. } else Usage( argv[0] );
  940. } else Usage( argv[0] );
  941. break;
  942. case 'v':
  943. vFlag = TRUE;
  944. break;
  945. case 'r':
  946. rFlag = TRUE;
  947. break;
  948. case 'i':
  949. iFlag = TRUE;
  950. break;
  951. case 't':
  952. tFlag = TRUE;
  953. break;
  954. default:
  955. case '?':
  956. Usage( argv[0] );
  957. break;
  958. }
  959. }
  960. nextarg:
  961. ;
  962. }
  963. for( ; argno < argc-1; argno++ ) {
  964. if( rootpath( argv[argno], dest ) == FALSE ) {
  965. errorexit( "invalid source\n" );
  966. ExitProcess(1);
  967. }
  968. for( p = dest; *p; p++ )
  969. if( *p == '/' )
  970. *p = '\\';
  971. if( (SourceList[ MaxSources ].name = (char *)malloc( strlen( dest ) + 2 )) == NULL ) {
  972. errorexit( "Out of memory!\n" );
  973. ExitProcess(1);
  974. }
  975. strcpy( SourceList[ MaxSources++ ].name, dest );
  976. }
  977. if( MaxSources == 0 )
  978. Usage( argv[0] );
  979. for( i=0; i < MaxDirectoryExcludes; i++ )
  980. msg( "Exclude Directory: %s\n", DirectoryExcludeList[i] );
  981. while( 1 ) {
  982. char statusbuffer[ MAX_PATH ];
  983. for( i=0; i < MaxSources; i++ ) {
  984. appendslash( SourceList[i].name );
  985. if( vFlag == TRUE || wFlag == FALSE )
  986. msg( "Validating %s....", SourceList[i].name );
  987. sprintf( statusbuffer, "Validating %s", SourceList[i].name );
  988. SetConsoleTitle( statusbuffer );
  989. if( GetFileAttributes( SourceList[i].name ) == 0xFFFFFFFF ) {
  990. if( vFlag == TRUE || wFlag == FALSE )
  991. msg( "[DISABLING %s]\n", SourceList[i].name );
  992. SourceList[i].flags.valid = FALSE;
  993. } else {
  994. SourceList[i].flags.valid = TRUE;
  995. if( vFlag == TRUE || wFlag == FALSE )
  996. msg( "[OK]\n" );
  997. }
  998. }
  999. for( i=0; i < MaxSources; i++ )
  1000. if( SourceList[i].flags.valid == TRUE )
  1001. break;
  1002. if( i != MaxSources )
  1003. break;
  1004. if( wFlag == TRUE ) {
  1005. SetConsoleTitle( "Sleeping awhile..." );
  1006. Sleep( 3 * 1000 * 60 );
  1007. } else {
  1008. SetConsoleTitle( OldConsoleTitle );
  1009. ExitProcess(1);
  1010. }
  1011. }
  1012. SetConsoleTitle( "Sources Present" );
  1013. LONG cThreads = (MaxSources * 3) + 1;
  1014. /*
  1015. * hack for ftc -w -678 to exit when the release shares are available
  1016. */
  1017. if( argno == argc && wFlag )
  1018. ExitProcess( 0 );
  1019. if ( argno != argc - 1 ) {
  1020. Usage( argv[0] );
  1021. } else if (rootpath (argv[argno], dest) == FALSE ) {
  1022. errorexit( "Invalid destination\n" );
  1023. ExitProcess(1);
  1024. }
  1025. for( p = dest; *p; p++ )
  1026. if( *p == '/' )
  1027. *p = '\\';
  1028. for( i=0; i < MaxSources; i++ )
  1029. if (!strcmp(SourceList[i].name, dest)) {
  1030. errorexit("Source == dest == %s", SourceList[i].name );
  1031. ExitThread(1);
  1032. }
  1033. /*
  1034. * Create the semaphores for the work lists
  1035. */
  1036. sa.nLength = sizeof( sa );
  1037. sa.lpSecurityDescriptor = NULL;
  1038. sa.bInheritHandle = TRUE;
  1039. if( (hWorkAvailSem = CreateSemaphore( &sa, 0, 100000, NULL)) == NULL ) {
  1040. errorexit( "Unable to create semaphore (err %u)!\n", GetLastError() );
  1041. ExitProcess(1);
  1042. }
  1043. hMaxWorkQueueSem = CreateSemaphore( &sa, MAXQUEUE, MAXQUEUE, NULL );
  1044. if( hMaxWorkQueueSem == NULL ) {
  1045. errorexit( "Unable to create queue length semaphore (err %u)!\n", GetLastError() );
  1046. ExitProcess( 1 );
  1047. }
  1048. /*
  1049. * Create the thread pool to do the copies
  1050. */
  1051. for( i=0; i < cThreads - 1; i++ ) {
  1052. CThread = CreateThread( (LPSECURITY_ATTRIBUTES)NULL, 0,
  1053. ThreadWorker, (LPVOID *)IntToPtr(i), 0, &IDThread );
  1054. if( CThread == NULL || CThread == INVALID_HANDLE_VALUE )
  1055. break;
  1056. SetThreadPriority( CThread, THREAD_PRIORITY_NORMAL );
  1057. CThread = INVALID_HANDLE_VALUE;
  1058. }
  1059. /*
  1060. * Create the 'update status' thread
  1061. */
  1062. CThread = CreateThread( (LPSECURITY_ATTRIBUTES)NULL, 0,
  1063. StatusWorker,(LPVOID *)0,0,&IDThread );
  1064. if( CThread != NULL && CThread != INVALID_HANDLE_VALUE ) {
  1065. // SetThreadPriority( CThread, THREAD_PRIORITY_BELOW_NORMAL );
  1066. CThread = INVALID_HANDLE_VALUE;
  1067. }
  1068. SetConsoleCtrlHandler( ControlHandlerRoutine, TRUE );
  1069. /*
  1070. * Produce the directory list
  1071. */
  1072. relpath[0] = '\0';
  1073. dwElapsedTime = GetTickCount();
  1074. SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL );
  1075. ScanDirectory( relpath, dest );
  1076. if( lFlag )
  1077. SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS );
  1078. SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL );
  1079. // OK, now hWorkAvailSem has a count for every item to copy. But when they
  1080. // finish copying, each thread waits on this semaphore again. At the end,
  1081. // everyone will still be waiting! So, add the number of threads to the
  1082. // count, so each thread notices, one by one, that everything's done.
  1083. ReleaseSemaphore( hWorkAvailSem, (int)cThreads+1, NULL );
  1084. if( bThreadStop == FALSE )
  1085. ThreadWorker( 0 );
  1086. return ExitCode;
  1087. }