Source code of Windows XP (NT5)
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.

666 lines
17 KiB

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <time.h>
  5. #include <malloc.h>
  6. // #include <imagehlp.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <malloc.h>
  10. // Generic routine to write a blob out.
  11. void
  12. SaveTemp(
  13. PVOID pFile,
  14. PCHAR szFile,
  15. DWORD FileSize
  16. )
  17. {
  18. DWORD dwBytesWritten;
  19. HANDLE hFile;
  20. hFile = CreateFile(
  21. szFile,
  22. GENERIC_WRITE,
  23. (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE),
  24. NULL,
  25. CREATE_ALWAYS,
  26. 0,
  27. NULL
  28. );
  29. if ( hFile == INVALID_HANDLE_VALUE ) {
  30. printf("Unable to open %s\n", szFile);
  31. return;
  32. }
  33. if (!WriteFile(hFile, pFile, FileSize, &dwBytesWritten, FALSE)) {
  34. printf("Unable to write date to %s\n", szFile);
  35. }
  36. CloseHandle(hFile);
  37. return;
  38. }
  39. // Zero out the timestamps in a PE library.
  40. BOOL
  41. ZeroLibTimeStamps(
  42. PCHAR pFile,
  43. DWORD dwSize
  44. )
  45. {
  46. PIMAGE_ARCHIVE_MEMBER_HEADER pHeader;
  47. DWORD dwOffset;
  48. CHAR MemberSize[sizeof(pHeader->Size) + 1];
  49. PIMAGE_FILE_HEADER pObjHeader;
  50. ZeroMemory(MemberSize, sizeof(MemberSize));
  51. __try {
  52. dwOffset = IMAGE_ARCHIVE_START_SIZE;
  53. while (dwOffset < dwSize) {
  54. pHeader = (PIMAGE_ARCHIVE_MEMBER_HEADER)(pFile+dwOffset);
  55. ZeroMemory(pHeader->Date, sizeof(pHeader->Date));
  56. dwOffset += IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR;
  57. memcpy(MemberSize, pHeader->Size, sizeof(pHeader->Size));
  58. // If it's not one of the special members, it must be an object/file, zero it's timestamp also.
  59. if (memcmp(pHeader->Name, IMAGE_ARCHIVE_LINKER_MEMBER, sizeof(pHeader->Name)) &&
  60. memcmp(pHeader->Name, IMAGE_ARCHIVE_LONGNAMES_MEMBER, sizeof(pHeader->Name)))
  61. {
  62. IMAGE_FILE_HEADER UNALIGNED *pFileHeader = (PIMAGE_FILE_HEADER)(pFile+dwOffset);
  63. if ((pFileHeader->Machine == IMAGE_FILE_MACHINE_UNKNOWN) &&
  64. (pFileHeader->NumberOfSections == IMPORT_OBJECT_HDR_SIG2))
  65. {
  66. // VC6 import descriptor OR ANON object header. Eitherway,
  67. // casting to IMPORT_OBJECT_HEADER will do the trick.
  68. ((IMPORT_OBJECT_HEADER UNALIGNED *)pFileHeader)->TimeDateStamp = 0;
  69. } else {
  70. pFileHeader->TimeDateStamp = 0;
  71. }
  72. }
  73. dwOffset += strtoul(MemberSize, NULL, 10);
  74. dwOffset = (dwOffset + 1) & ~1; // align to word
  75. }
  76. } __except(EXCEPTION_EXECUTE_HANDLER) {
  77. }
  78. return TRUE;
  79. }
  80. //
  81. // Compare two libraries ignoring info that isn't relevant
  82. // (timestamps for now, debug info later).
  83. //
  84. int
  85. libcomp(
  86. void *pFile1,
  87. DWORD dwSize1,
  88. void *pFile2,
  89. DWORD dwSize2
  90. )
  91. {
  92. if (dwSize1 != dwSize2) {
  93. return 1;
  94. }
  95. // Normalize the two files and compare the results.
  96. ZeroLibTimeStamps(pFile1, dwSize1);
  97. ZeroLibTimeStamps(pFile2, dwSize2);
  98. return memcmp(pFile1, pFile2, dwSize1);
  99. }
  100. //
  101. // Compare two headers. For now, just use memcmp. Later, we'll need to
  102. // handle MIDL generated timestamp differences and check for comment only changes.
  103. //
  104. int
  105. hdrcomp(
  106. void *pFile1,
  107. DWORD dwSize1,
  108. void *pFile2,
  109. DWORD dwSize2
  110. )
  111. {
  112. if (dwSize1 != dwSize2) {
  113. return 1;
  114. }
  115. return memcmp(pFile1, pFile2, dwSize1);
  116. }
  117. //
  118. // Compare two typelibs. Initially just memcmp. Use DougF's typelib code later.
  119. //
  120. int
  121. tlbcomp(
  122. void *pFile1,
  123. DWORD dwSize1,
  124. void *pFile2,
  125. DWORD dwSize2
  126. )
  127. {
  128. if (dwSize1 != dwSize2) {
  129. return 1;
  130. }
  131. return memcmp(pFile1, pFile2, dwSize1);
  132. }
  133. int
  134. objcomp(
  135. void *pFile1,
  136. DWORD dwSize1,
  137. void *pFile2,
  138. DWORD dwSize2
  139. )
  140. {
  141. PIMAGE_FILE_HEADER pFileHeader1 = (PIMAGE_FILE_HEADER)(pFile1);
  142. PIMAGE_FILE_HEADER pFileHeader2 = (PIMAGE_FILE_HEADER)(pFile2);
  143. if ((dwSize1 != dwSize2) || (pFileHeader1->Machine != pFileHeader2->Machine))
  144. {
  145. return 1;
  146. }
  147. pFileHeader1->TimeDateStamp = 0;
  148. pFileHeader2->TimeDateStamp = 0;
  149. return memcmp(pFile1, pFile2, dwSize1);
  150. }
  151. int
  152. IsValidMachineType(USHORT usMachine)
  153. {
  154. if ((usMachine == IMAGE_FILE_MACHINE_I386) ||
  155. (usMachine == IMAGE_FILE_MACHINE_IA64) ||
  156. (usMachine == IMAGE_FILE_MACHINE_ALPHA64) ||
  157. (usMachine == IMAGE_FILE_MACHINE_ALPHA))
  158. {
  159. return TRUE;
  160. } else {
  161. return FALSE;
  162. }
  163. }
  164. #define FILETYPE_ARCHIVE 0x01
  165. #define FILETYPE_TYPELIB 0x02
  166. #define FILETYPE_HEADER 0x03
  167. #define FILETYPE_PE_OBJECT 0x04
  168. #define FILETYPE_UNKNOWN 0xff
  169. //
  170. // Given a file, attempt to determine what it is. Initial pass will just use file
  171. // extensions except for libs that we can search for the <arch>\n start.
  172. //
  173. int
  174. DetermineFileType(
  175. void *pFile,
  176. DWORD dwSize,
  177. CHAR *szFileName
  178. )
  179. {
  180. char szExt[_MAX_EXT];
  181. // Let's see if it's a library first:
  182. if ((dwSize >= IMAGE_ARCHIVE_START_SIZE) &&
  183. !memcmp(pFile, IMAGE_ARCHIVE_START, IMAGE_ARCHIVE_START_SIZE))
  184. {
  185. return FILETYPE_ARCHIVE;
  186. }
  187. // For now, guess about the headers/tlb based on the extension.
  188. _splitpath(szFileName, NULL, NULL, NULL, szExt);
  189. if (!_stricmp(szExt, ".h") ||
  190. !_stricmp(szExt, ".hxx") ||
  191. !_stricmp(szExt, ".hpp") ||
  192. !_stricmp(szExt, ".w") ||
  193. !_stricmp(szExt, ".inc"))
  194. {
  195. return FILETYPE_HEADER;
  196. }
  197. if (!_stricmp(szExt, ".tlb"))
  198. {
  199. return FILETYPE_TYPELIB;
  200. }
  201. if (!_stricmp(szExt, ".obj") && IsValidMachineType(((PIMAGE_FILE_HEADER)pFile)->Machine))
  202. {
  203. return FILETYPE_PE_OBJECT;
  204. }
  205. return FILETYPE_UNKNOWN;
  206. }
  207. //
  208. // Determine if two files are materially different.
  209. //
  210. BOOL
  211. CheckIfCopyNecessary(
  212. char *szSourceFile,
  213. char *szDestFile,
  214. BOOL *fTimeStampsDiffer
  215. )
  216. {
  217. PVOID pFile1 = NULL, pFile2 = NULL;
  218. DWORD File1Size, File2Size, dwBytesRead, dwErrorCode = ERROR_SUCCESS;
  219. HANDLE hFile1 = INVALID_HANDLE_VALUE;
  220. HANDLE hFile2 = INVALID_HANDLE_VALUE;
  221. BOOL fCopy = FALSE;
  222. int File1Type, File2Type;
  223. FILETIME FileTime1, FileTime2;
  224. hFile1 = CreateFile(
  225. szDestFile,
  226. GENERIC_READ,
  227. (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE),
  228. NULL,
  229. OPEN_EXISTING,
  230. 0,
  231. NULL
  232. );
  233. if ( hFile1 == INVALID_HANDLE_VALUE ) {
  234. fCopy = TRUE; // Dest file doesn't exist. Always do the copy.
  235. goto Exit;
  236. }
  237. // Now get the second file.
  238. hFile2 = CreateFile(
  239. szSourceFile,
  240. GENERIC_READ,
  241. (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE),
  242. NULL,
  243. OPEN_EXISTING,
  244. 0,
  245. NULL
  246. );
  247. if ( hFile2 == INVALID_HANDLE_VALUE ) {
  248. // If the source is missing, always skip the copy (don't want to delete the dest file).
  249. dwErrorCode = ERROR_FILE_NOT_FOUND;
  250. goto Exit;
  251. }
  252. // Get the file time's. If they match, assume the files match.
  253. if (!GetFileTime(hFile1, NULL, NULL, &FileTime1)) {
  254. dwErrorCode = GetLastError();
  255. goto Exit;
  256. }
  257. if (!GetFileTime(hFile2, NULL, NULL, &FileTime2)) {
  258. dwErrorCode = GetLastError();
  259. goto Exit;
  260. }
  261. if (!memcmp(&FileTime1, &FileTime2, sizeof(FILETIME))) {
  262. *fTimeStampsDiffer = FALSE;
  263. goto Exit;
  264. }
  265. *fTimeStampsDiffer = TRUE;
  266. // Read file 1 in.
  267. File1Size = GetFileSize(hFile1, NULL);
  268. pFile1 = malloc(File1Size);
  269. if (!pFile1) {
  270. dwErrorCode = ERROR_OUTOFMEMORY;
  271. goto Exit; // Can't compare - don't copy.
  272. }
  273. SetFilePointer(hFile1, 0, 0, FILE_BEGIN);
  274. if (!ReadFile(hFile1, pFile1, File1Size, &dwBytesRead, FALSE)) {
  275. dwErrorCode = GetLastError();
  276. goto Exit; // Can't compare - don't copy
  277. }
  278. CloseHandle(hFile1);
  279. hFile1 = INVALID_HANDLE_VALUE;
  280. // Read file 2 in.
  281. File2Size = GetFileSize(hFile2, NULL);
  282. pFile2 = malloc(File2Size);
  283. if (!pFile2) {
  284. dwErrorCode = ERROR_OUTOFMEMORY;
  285. goto Exit; // Can't compare - don't copy.
  286. }
  287. SetFilePointer(hFile2, 0, 0, FILE_BEGIN);
  288. if (!ReadFile(hFile2, pFile2, File2Size, &dwBytesRead, FALSE)) {
  289. dwErrorCode = GetLastError();
  290. goto Exit; // Can't compare - don't copy
  291. }
  292. CloseHandle(hFile2);
  293. hFile2 = INVALID_HANDLE_VALUE;
  294. // Let's see what we've got.
  295. File1Type = DetermineFileType(pFile1, File1Size, szSourceFile);
  296. File2Type = DetermineFileType(pFile2, File2Size, szDestFile);
  297. if (File1Type == File2Type) {
  298. switch (File1Type) {
  299. case FILETYPE_ARCHIVE:
  300. fCopy = libcomp(pFile1, File1Size, pFile2, File2Size);
  301. break;
  302. case FILETYPE_HEADER:
  303. fCopy = hdrcomp(pFile1, File1Size, pFile2, File2Size);
  304. break;
  305. case FILETYPE_TYPELIB:
  306. fCopy = tlbcomp(pFile1, File1Size, pFile2, File2Size);
  307. break;
  308. case FILETYPE_PE_OBJECT:
  309. fCopy = objcomp(pFile1, File1Size, pFile2, File2Size);
  310. break;
  311. case FILETYPE_UNKNOWN:
  312. default:
  313. if (File1Size == File2Size) {
  314. fCopy = memcmp(pFile1, pFile2, File1Size);
  315. } else {
  316. fCopy = TRUE;
  317. }
  318. }
  319. } else {
  320. // They don't match according to file extensions - just memcmp them.
  321. if (File1Size == File2Size) {
  322. fCopy = memcmp(pFile1, pFile2, File1Size);
  323. } else {
  324. fCopy = TRUE;
  325. }
  326. }
  327. Exit:
  328. if (pFile1)
  329. free(pFile1);
  330. if (pFile2)
  331. free(pFile2);
  332. if (hFile1 != INVALID_HANDLE_VALUE)
  333. CloseHandle(hFile1);
  334. if (hFile2 != INVALID_HANDLE_VALUE)
  335. CloseHandle(hFile2);
  336. SetLastError(dwErrorCode);
  337. return fCopy;
  338. }
  339. BOOL
  340. UpdateDestTimeStamp(
  341. char *szSourceFile,
  342. char *szDestFile
  343. )
  344. {
  345. HANDLE hFile;
  346. FILETIME LastWriteTime;
  347. DWORD dwAttributes;
  348. BOOL fTweakAttributes;
  349. hFile = CreateFile(
  350. szSourceFile,
  351. GENERIC_READ,
  352. (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE),
  353. NULL,
  354. OPEN_EXISTING,
  355. 0,
  356. NULL
  357. );
  358. if ( hFile == INVALID_HANDLE_VALUE ) {
  359. return FALSE;
  360. }
  361. if (!GetFileTime(hFile, NULL, NULL, &LastWriteTime)) {
  362. return FALSE;
  363. }
  364. CloseHandle(hFile);
  365. dwAttributes = GetFileAttributes(szDestFile);
  366. if ((dwAttributes != (DWORD) -1) && (dwAttributes & FILE_ATTRIBUTE_READONLY))
  367. {
  368. // Make sure it's not readonly
  369. SetFileAttributes(szDestFile, dwAttributes & ~FILE_ATTRIBUTE_READONLY);
  370. fTweakAttributes = TRUE;
  371. } else {
  372. fTweakAttributes = FALSE;
  373. }
  374. hFile = CreateFile(
  375. szDestFile,
  376. GENERIC_WRITE,
  377. (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE),
  378. NULL,
  379. OPEN_EXISTING,
  380. 0,
  381. NULL
  382. );
  383. if ( hFile == INVALID_HANDLE_VALUE ) {
  384. return FALSE;
  385. }
  386. SetFileTime(hFile, NULL, NULL, &LastWriteTime);
  387. CloseHandle(hFile);
  388. if (fTweakAttributes) {
  389. // Put the readonly attribute back on.
  390. if (!SetFileAttributes(szDestFile, dwAttributes)) {
  391. printf("PCOPY: SetFileAttributes(%s, %X) failed - error code: %d\n", szDestFile, dwAttributes, GetLastError());
  392. }
  393. }
  394. return TRUE;
  395. }
  396. //
  397. // Log routine to find out what files were actually copied and why.
  398. //
  399. void
  400. LogCopyFile(
  401. char * szSource,
  402. char * szDest,
  403. BOOL fCopy,
  404. DWORD dwReturnCode
  405. )
  406. {
  407. if (getenv("LOG_PCOPY")) {
  408. FILE *FileHandle = fopen("\\pcopy.log", "a");
  409. if (FileHandle) {
  410. time_t Time;
  411. UCHAR const *szTime = "";
  412. Time = time(NULL);
  413. szTime = ctime(&Time);
  414. fprintf(FileHandle, "%s: %.*s, %s, %s, %d\n", fCopy ? (dwReturnCode ? "ERROR" : "DONE") : "SKIP", strlen(szTime)-1, szTime, szSource, szDest, dwReturnCode);
  415. fclose(FileHandle);
  416. }
  417. }
  418. }
  419. BOOL
  420. MyMakeSureDirectoryPathExists(
  421. char * DirPath
  422. )
  423. {
  424. LPSTR p;
  425. DWORD dw;
  426. char szDir[_MAX_DIR];
  427. char szMakeDir[_MAX_DIR];
  428. _splitpath(DirPath, szMakeDir, szDir, NULL, NULL);
  429. strcat(szMakeDir, szDir);
  430. p = szMakeDir;
  431. dw = GetFileAttributes(szMakeDir);
  432. if ( (dw != (DWORD) -1) && (dw & FILE_ATTRIBUTE_DIRECTORY) ) {
  433. // Directory already exists.
  434. return TRUE;
  435. }
  436. // If the second character in the path is "\", then this is a UNC
  437. // path, and we should skip forward until we reach the 2nd \ in the path.
  438. if ((*p == '\\') && (*(p+1) == '\\')) {
  439. p++; // Skip over the first \ in the name.
  440. p++; // Skip over the second \ in the name.
  441. // Skip until we hit the first "\" (\\Server\).
  442. while (*p && *p != '\\') {
  443. p = p++;
  444. }
  445. // Advance over it.
  446. if (*p) {
  447. p++;
  448. }
  449. // Skip until we hit the second "\" (\\Server\Share\).
  450. while (*p && *p != '\\') {
  451. p = p++;
  452. }
  453. // Advance over it also.
  454. if (*p) {
  455. p++;
  456. }
  457. } else
  458. // Not a UNC. See if it's <drive>:
  459. if (*(p+1) == ':' ) {
  460. p++;
  461. p++;
  462. // If it exists, skip over the root specifier
  463. if (*p && (*p == '\\')) {
  464. p++;
  465. }
  466. }
  467. while( *p ) {
  468. if ( *p == '\\' ) {
  469. *p = '\0';
  470. dw = GetFileAttributes(szMakeDir);
  471. // Nothing exists with this name. Try to make the directory name and error if unable to.
  472. if ( dw == 0xffffffff ) {
  473. if ( !CreateDirectory(szMakeDir,NULL) ) {
  474. if( GetLastError() != ERROR_ALREADY_EXISTS ) {
  475. return FALSE;
  476. }
  477. }
  478. } else {
  479. if ( (dw & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY ) {
  480. // Something exists with this name, but it's not a directory... Error
  481. return FALSE;
  482. }
  483. }
  484. *p = '\\';
  485. }
  486. p = p++;
  487. }
  488. return TRUE;
  489. }
  490. int
  491. __cdecl
  492. main(
  493. int argc,
  494. char *argv[]
  495. )
  496. {
  497. char *szSourceFile, *szDestFile;
  498. BOOL fCopyFile = 0, fDoCopy, fTimeStampsDiffer;
  499. int CopyErrorCode;
  500. if (argc < 3) {
  501. puts("pcopy <source file> <dest file>\n"
  502. "Returns: -1 if no copy necessary (no material change to the files)\n"
  503. " 0 if a successful copy was made\n"
  504. " otherwise the error code for why the copy was unsuccessful\n");
  505. return ((int)ERROR_INVALID_COMMAND_LINE);
  506. }
  507. szSourceFile = argv[1];
  508. szDestFile = argv[2];
  509. fDoCopy = CheckIfCopyNecessary(szSourceFile, szDestFile, &fTimeStampsDiffer);
  510. if (fDoCopy) {
  511. DWORD dwAttributes = GetFileAttributes(szDestFile);
  512. if (dwAttributes != (DWORD) -1) {
  513. // Make sure it's not readonly
  514. SetFileAttributes(szDestFile, dwAttributes & ~FILE_ATTRIBUTE_READONLY);
  515. }
  516. // Make sure destination directory exists.
  517. MyMakeSureDirectoryPathExists(szDestFile);
  518. fCopyFile = CopyFileA(szSourceFile, szDestFile, FALSE);
  519. if (!fCopyFile) {
  520. CopyErrorCode = (int) GetLastError();
  521. } else {
  522. dwAttributes = GetFileAttributes(szDestFile);
  523. if (dwAttributes != (DWORD) -1) {
  524. // Make sure the dest is read/write
  525. SetFileAttributes(szDestFile, dwAttributes & ~FILE_ATTRIBUTE_READONLY);
  526. }
  527. CopyErrorCode = 0;
  528. }
  529. } else {
  530. CopyErrorCode = GetLastError();
  531. if (!CopyErrorCode && fTimeStampsDiffer) {
  532. // No copy necessary. Touch the timestamp on the dest to match the source.
  533. UpdateDestTimeStamp(szSourceFile, szDestFile);
  534. }
  535. }
  536. LogCopyFile(szSourceFile, szDestFile, fDoCopy, CopyErrorCode);
  537. if (fDoCopy) {
  538. return CopyErrorCode;
  539. } else {
  540. return CopyErrorCode ? CopyErrorCode : -1; // No copy necessary.
  541. }
  542. }