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.

4662 lines
121 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. utils.c
  5. Abstract:
  6. Utilitaries for winnt32.
  7. Author:
  8. Revision History:
  9. Ovidiu Temereanca (ovidiut) 24-Jul-2000
  10. --*/
  11. #include "precomp.h"
  12. #include <mbstring.h>
  13. #pragma hdrstop
  14. VOID
  15. MyWinHelp(
  16. IN HWND Window,
  17. IN UINT Command,
  18. IN ULONG_PTR Data
  19. )
  20. {
  21. TCHAR Buffer[MAX_PATH];
  22. LPTSTR p;
  23. HANDLE FindHandle;
  24. BOOL b;
  25. WIN32_FIND_DATA FindData;
  26. LPCTSTR HelpFileName = TEXT("winnt32.hlp");
  27. //
  28. // The likely scenario is that a user invokes winnt32 from
  29. // a network share. We'll expect the help file to be there too.
  30. //
  31. b = FALSE;
  32. if(MyGetModuleFileName(NULL,Buffer,ARRAYSIZE(Buffer))
  33. && (p = _tcsrchr(Buffer,TEXT('\\'))))
  34. {
  35. //
  36. // skip the slash (one TCHAR)
  37. //
  38. p++;
  39. if (SUCCEEDED (StringCchCopy (p, Buffer + ARRAYSIZE(Buffer) - p, HelpFileName))) {
  40. //
  41. // See whether the help file is there. If so, use it.
  42. //
  43. FindHandle = FindFirstFile(Buffer,&FindData);
  44. if(FindHandle != INVALID_HANDLE_VALUE) {
  45. FindClose(FindHandle);
  46. b = WinHelp(Window,Buffer,Command,Data);
  47. }
  48. }
  49. }
  50. if(!b) {
  51. //
  52. // Try just the base help file name.
  53. //
  54. b = WinHelp(Window,HelpFileName,Command,Data);
  55. }
  56. if(!b) {
  57. //
  58. // Tell user.
  59. //
  60. MessageBoxFromMessage(
  61. Window,
  62. MSG_CANT_OPEN_HELP_FILE,
  63. FALSE,
  64. AppTitleStringId,
  65. MB_OK | MB_ICONINFORMATION,
  66. HelpFileName
  67. );
  68. }
  69. }
  70. BOOL
  71. ConcatenatePaths(
  72. IN OUT PTSTR Path1,
  73. IN LPCTSTR Path2,
  74. IN DWORD BufferSizeChars
  75. )
  76. /*++
  77. Routine Description:
  78. Concatenate two path strings together, supplying a path separator
  79. character (\) if necessary between the 2 parts.
  80. Arguments:
  81. Path1 - supplies prefix part of path. Path2 is concatenated to Path1.
  82. Path2 - supplies the suffix part of path. If Path1 does not end with a
  83. path separator and Path2 does not start with one, then a path sep
  84. is appended to Path1 before appending Path2.
  85. BufferSizeChars - supplies the size in chars (Unicode version) or
  86. bytes (Ansi version) of the buffer pointed to by Path1. The string
  87. will be truncated as necessary to not overflow that size.
  88. Return Value:
  89. TRUE if the 2 paths were successfully concatenated, without any truncation
  90. FALSE otherwise
  91. --*/
  92. {
  93. BOOL NeedBackslash = TRUE;
  94. PTSTR p;
  95. BOOL b = FALSE;
  96. if (!Path1 || BufferSizeChars < 1) {
  97. MYASSERT (FALSE);
  98. SetLastError (ERROR_INVALID_PARAMETER);
  99. return FALSE;
  100. }
  101. //
  102. // Determine whether we need to stick a backslash
  103. // between the components.
  104. //
  105. p = _tcsrchr (Path1, TEXT('\\'));
  106. if(p && *(++p) == 0) {
  107. if (p >= Path1 + BufferSizeChars) {
  108. //
  109. // buffer already overflowed
  110. //
  111. MYASSERT (FALSE);
  112. SetLastError (ERROR_INSUFFICIENT_BUFFER);
  113. return FALSE;
  114. }
  115. NeedBackslash = FALSE;
  116. } else {
  117. p = _tcschr (Path1, 0);
  118. }
  119. if(Path2 && *Path2 == TEXT('\\')) {
  120. if(NeedBackslash) {
  121. NeedBackslash = FALSE;
  122. } else {
  123. //
  124. // Not only do we not need a backslash, but we
  125. // need to eliminate one before concatenating.
  126. //
  127. Path2++;
  128. }
  129. }
  130. //
  131. // Append backslash if necessary.
  132. // We verified above that we have enough space for this
  133. //
  134. if(NeedBackslash) {
  135. *p++ = TEXT('\\');
  136. *p = 0;
  137. }
  138. //
  139. // Append second part of string to first part if it fits.
  140. //
  141. if (Path2) {
  142. MYASSERT (Path1 + BufferSizeChars >= p);
  143. if (FAILED (StringCchCopy (p, Path1 + BufferSizeChars - p, Path2))) {
  144. //
  145. // why is the buffer so small?
  146. //
  147. MYASSERT (FALSE);
  148. *p = 0;
  149. return FALSE;
  150. }
  151. } else {
  152. *p = 0;
  153. }
  154. return TRUE;
  155. }
  156. LPTSTR
  157. DupString(
  158. IN LPCTSTR String
  159. )
  160. /*++
  161. Routine Description:
  162. Make a duplicate of a nul-terminated string.
  163. Arguments:
  164. String - supplies pointer to nul-terminated string to copy.
  165. Return Value:
  166. Copy of string or NULL if OOM. Caller can free with FREE().
  167. --*/
  168. {
  169. LPTSTR p;
  170. if(p = MALLOC((lstrlen(String)+1)*sizeof(TCHAR))) {
  171. lstrcpy(p,String);
  172. }
  173. return(p);
  174. }
  175. PTSTR
  176. DupMultiSz (
  177. IN PCTSTR MultiSz
  178. )
  179. /*++
  180. Routine Description:
  181. Make a duplicate of a MultiSz.
  182. Arguments:
  183. MultiSz - supplies pointer to the multi-string to duplicate.
  184. Return Value:
  185. Copy of string or NULL if OOM. Caller can free with FREE().
  186. --*/
  187. {
  188. PCTSTR p;
  189. PTSTR q;
  190. DWORD size = sizeof (TCHAR);
  191. for (p = MultiSz; *p; p = _tcschr (p, 0) + 1) {
  192. size += (lstrlen (p) + 1) * sizeof(TCHAR);
  193. }
  194. if (q = MALLOC (size)) {
  195. CopyMemory (q, MultiSz, size);
  196. }
  197. return q;
  198. }
  199. PTSTR
  200. CreatePrintableString (
  201. IN PCTSTR MultiSz
  202. )
  203. /*++
  204. Routine Description:
  205. Creates a string of the form (str1, str2, ..., strN) from a MultiSz
  206. Arguments:
  207. MultiSz - supplies pointer to the MultiSz string to represent.
  208. Return Value:
  209. Pointer to the new string string or NULL if OOM. Caller can free with FREE().
  210. --*/
  211. {
  212. PCTSTR p;
  213. PTSTR q, r;
  214. DWORD size = 3 * sizeof (TCHAR);
  215. for (p = MultiSz; *p; p = _tcschr (p, 0) + 1) {
  216. size += (lstrlen (p) + 1) * sizeof(TCHAR);
  217. }
  218. if (r = MALLOC (size)) {
  219. q = r;
  220. *q++ = TEXT('(');
  221. for (p = MultiSz; *p; p = _tcschr (p, 0) + 1) {
  222. if (q - r > 1) {
  223. *q++ = TEXT(',');
  224. }
  225. q += wsprintf (q, TEXT("%s"), p);
  226. }
  227. *q++ = TEXT(')');
  228. *q = 0;
  229. }
  230. return r;
  231. }
  232. PSTR
  233. UnicodeToAnsi (
  234. IN PCWSTR Unicode
  235. )
  236. /*++
  237. Routine Description:
  238. Makes an ANSI duplicate of a UNICODE string.
  239. Arguments:
  240. Unicode - supplies pointer to the UNICODE string to duplicate.
  241. Return Value:
  242. Copy of string or NULL if OOM. Caller can free with FREE().
  243. --*/
  244. {
  245. PSTR p;
  246. DWORD size;
  247. if (!Unicode) {
  248. return NULL;
  249. }
  250. size = (lstrlenW (Unicode) + 1) * sizeof(WCHAR);
  251. if (p = MALLOC (size)) {
  252. if (!WideCharToMultiByte (
  253. CP_ACP,
  254. 0,
  255. Unicode,
  256. -1,
  257. p,
  258. size,
  259. NULL,
  260. NULL
  261. )) {
  262. FREE (p);
  263. p = NULL;
  264. }
  265. }
  266. return p;
  267. }
  268. PWSTR
  269. AnsiToUnicode (
  270. IN PCSTR SzAnsi
  271. )
  272. /*++
  273. Routine Description:
  274. Makes a UNICODE duplicate of an ANSI string.
  275. Arguments:
  276. SzAnsi - supplies pointer to the ANSI string to duplicate.
  277. Return Value:
  278. Copy of string or NULL if OOM. Caller can free with FREE().
  279. --*/
  280. {
  281. PWSTR q;
  282. DWORD size;
  283. if (!SzAnsi) {
  284. return NULL;
  285. }
  286. size = strlen (SzAnsi) + 1;
  287. if (q = MALLOC (size * sizeof(WCHAR))) {
  288. if (!MultiByteToWideChar (
  289. CP_ACP,
  290. 0,
  291. SzAnsi,
  292. size,
  293. q,
  294. size
  295. )) {
  296. FREE (q);
  297. q = NULL;
  298. }
  299. }
  300. return q;
  301. }
  302. PWSTR
  303. MultiSzAnsiToUnicode (
  304. IN PCSTR MultiSzAnsi
  305. )
  306. /*++
  307. Routine Description:
  308. Makes a UNICODE duplicate of a multi-sz ANSI string.
  309. Arguments:
  310. MultiSzAnsi - supplies pointer to the multisz ANSI string to duplicate.
  311. Return Value:
  312. Copy of string or NULL if OOM. Caller can free with FREE().
  313. --*/
  314. {
  315. PCSTR p;
  316. PWSTR q;
  317. DWORD size = 1;
  318. if (!MultiSzAnsi) {
  319. return NULL;
  320. }
  321. for (p = MultiSzAnsi; *p; p = _mbschr (p, 0) + 1) {
  322. size += strlen (p) + 1;
  323. }
  324. if (q = MALLOC (size * sizeof(WCHAR))) {
  325. if (!MultiByteToWideChar (
  326. CP_ACP,
  327. 0,
  328. MultiSzAnsi,
  329. size,
  330. q,
  331. size
  332. )) {
  333. FREE (q);
  334. q = NULL;
  335. }
  336. }
  337. return q;
  338. }
  339. UINT
  340. MyGetDriveType(
  341. IN TCHAR Drive
  342. )
  343. /*++
  344. Routine Description:
  345. Same as GetDriveType() Win32 API except on NT returns
  346. DRIVE_FIXED for removeable hard drives.
  347. Arguments:
  348. Drive - supplies drive letter whose type is desired.
  349. Return Value:
  350. Same as GetDriveType().
  351. --*/
  352. {
  353. TCHAR DriveNameNt[] = TEXT("\\\\.\\?:");
  354. TCHAR DriveName[] = TEXT("?:\\");
  355. HANDLE hDisk;
  356. BOOL b;
  357. UINT rc;
  358. DWORD DataSize;
  359. DISK_GEOMETRY MediaInfo;
  360. //
  361. // First, get the win32 drive type. If it tells us DRIVE_REMOVABLE,
  362. // then we need to see whether it's a floppy or hard disk. Otherwise
  363. // just believe the api.
  364. //
  365. //
  366. MYASSERT (Drive);
  367. DriveName[0] = Drive;
  368. rc = GetDriveType(DriveName);
  369. #ifdef _X86_ //NEC98
  370. //
  371. // NT5 for NEC98 can not access AT formated HD during setup.
  372. // We need except these type.
  373. if (IsNEC98() && ISNT() && (rc == DRIVE_FIXED) && BuildNumber <= NT40) {
  374. //
  375. // Check ATA Card?
  376. //
  377. {
  378. HANDLE hDisk;
  379. DriveNameNt[4] = Drive;
  380. hDisk = CreateFile( DriveNameNt,
  381. GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
  382. NULL,
  383. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  384. if(hDisk == INVALID_HANDLE_VALUE) {
  385. return(DRIVE_UNKNOWN);
  386. }
  387. if (CheckATACardonNT4(hDisk)){
  388. CloseHandle(hDisk);
  389. return(DRIVE_REMOVABLE);
  390. }
  391. CloseHandle(hDisk);
  392. }
  393. if (!IsValidDrive(Drive)){
  394. // HD format is not NEC98 format.
  395. return(DRIVE_UNKNOWN);
  396. }
  397. }
  398. if((rc != DRIVE_REMOVABLE) || !ISNT() || (!IsNEC98() && (Drive < L'C'))) {
  399. return(rc);
  400. }
  401. #else //NEC98
  402. if((rc != DRIVE_REMOVABLE) || !ISNT() || (Drive < L'C')) {
  403. return(rc);
  404. }
  405. #endif
  406. //
  407. // DRIVE_REMOVABLE on NT.
  408. //
  409. //
  410. // Disallow use of removable media (e.g. Jazz, Zip, ...).
  411. //
  412. DriveNameNt[4] = Drive;
  413. hDisk = CreateFile(
  414. DriveNameNt,
  415. FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  416. FILE_SHARE_READ | FILE_SHARE_WRITE,
  417. NULL,
  418. OPEN_EXISTING,
  419. 0,
  420. NULL
  421. );
  422. if(hDisk != INVALID_HANDLE_VALUE) {
  423. b = DeviceIoControl(
  424. hDisk,
  425. IOCTL_DISK_GET_DRIVE_GEOMETRY,
  426. NULL,
  427. 0,
  428. &MediaInfo,
  429. sizeof(MediaInfo),
  430. &DataSize,
  431. NULL
  432. );
  433. //
  434. // It's really a hard disk if the media type is removable.
  435. //
  436. if(b && (MediaInfo.MediaType == RemovableMedia)) {
  437. rc = DRIVE_FIXED;
  438. }
  439. CloseHandle(hDisk);
  440. }
  441. return(rc);
  442. }
  443. #ifdef UNICODE
  444. UINT
  445. MyGetDriveType2 (
  446. IN PCWSTR NtVolumeName
  447. )
  448. /*++
  449. Routine Description:
  450. Same as GetDriveType() Win32 API except on NT returns
  451. DRIVE_FIXED for removeable hard drives.
  452. Arguments:
  453. NtVolumeName - supplies device name whose type is desired.
  454. Return Value:
  455. Same as GetDriveType().
  456. --*/
  457. {
  458. NTSTATUS Status;
  459. OBJECT_ATTRIBUTES Obja;
  460. UNICODE_STRING DeviceName;
  461. HANDLE hDisk;
  462. IO_STATUS_BLOCK IoStatusBlock;
  463. BOOL b;
  464. UINT rc;
  465. DWORD DataSize;
  466. DISK_GEOMETRY MediaInfo;
  467. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  468. //
  469. // First, get the win32 drive type. If it tells us DRIVE_REMOVABLE,
  470. // then we need to see whether it's a floppy or hard disk. Otherwise
  471. // just believe the api.
  472. //
  473. INIT_OBJA (&Obja, &DeviceName, NtVolumeName);
  474. Status = NtOpenFile (
  475. &hDisk,
  476. (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  477. &Obja,
  478. &IoStatusBlock,
  479. FILE_SHARE_READ | FILE_SHARE_WRITE,
  480. FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
  481. );
  482. if (!NT_SUCCESS( Status )) {
  483. return DRIVE_NO_ROOT_DIR;
  484. }
  485. //
  486. // Determine if this is a network or disk file system. If it
  487. // is a disk file system determine if this is removable or not
  488. //
  489. Status = NtQueryVolumeInformationFile(
  490. hDisk,
  491. &IoStatusBlock,
  492. &DeviceInfo,
  493. sizeof(DeviceInfo),
  494. FileFsDeviceInformation
  495. );
  496. if (!NT_SUCCESS (Status)) {
  497. rc = DRIVE_UNKNOWN;
  498. } else if (DeviceInfo.Characteristics & FILE_REMOTE_DEVICE) {
  499. rc = DRIVE_REMOTE;
  500. } else {
  501. switch (DeviceInfo.DeviceType) {
  502. case FILE_DEVICE_NETWORK:
  503. case FILE_DEVICE_NETWORK_FILE_SYSTEM:
  504. rc = DRIVE_REMOTE;
  505. break;
  506. case FILE_DEVICE_CD_ROM:
  507. case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
  508. rc = DRIVE_CDROM;
  509. break;
  510. case FILE_DEVICE_VIRTUAL_DISK:
  511. rc = DRIVE_RAMDISK;
  512. break;
  513. case FILE_DEVICE_DISK:
  514. case FILE_DEVICE_DISK_FILE_SYSTEM:
  515. if ( DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA ) {
  516. rc = DRIVE_REMOVABLE;
  517. } else {
  518. rc = DRIVE_FIXED;
  519. }
  520. break;
  521. default:
  522. rc = DRIVE_UNKNOWN;
  523. break;
  524. }
  525. }
  526. if(rc == DRIVE_REMOVABLE) {
  527. //
  528. // DRIVE_REMOVABLE on NT.
  529. // Disallow use of removable media (e.g. Jazz, Zip, ...).
  530. //
  531. Status = NtDeviceIoControlFile(
  532. hDisk,
  533. 0,
  534. NULL,
  535. NULL,
  536. &IoStatusBlock,
  537. IOCTL_DISK_GET_DRIVE_GEOMETRY,
  538. NULL,
  539. 0,
  540. &MediaInfo,
  541. sizeof(DISK_GEOMETRY)
  542. );
  543. //
  544. // It's really a hard disk if the media type is removable.
  545. //
  546. if(NT_SUCCESS (Status) && (MediaInfo.MediaType == RemovableMedia)) {
  547. rc = DRIVE_FIXED;
  548. }
  549. }
  550. NtClose (hDisk);
  551. return(rc);
  552. }
  553. #endif
  554. BOOL
  555. GetPartitionInfo(
  556. IN TCHAR Drive,
  557. OUT PPARTITION_INFORMATION PartitionInfo
  558. )
  559. /*++
  560. Routine Description:
  561. Fill in a PARTITION_INFORMATION structure with information about
  562. a particular drive.
  563. This routine is meaningful only when run on NT -- it always fails
  564. on Win95.
  565. Arguments:
  566. Drive - supplies drive letter whose partition info is desired.
  567. PartitionInfo - upon success, receives partition info for Drive.
  568. Return Value:
  569. Boolean value indicating whether PartitionInfo has been filled in.
  570. --*/
  571. {
  572. TCHAR DriveName[] = TEXT("\\\\.\\?:");
  573. HANDLE hDisk;
  574. BOOL b;
  575. DWORD DataSize;
  576. if(!ISNT()) {
  577. return(FALSE);
  578. }
  579. DriveName[4] = Drive;
  580. hDisk = CreateFile(
  581. DriveName,
  582. GENERIC_READ,
  583. FILE_SHARE_READ | FILE_SHARE_WRITE,
  584. NULL,
  585. OPEN_EXISTING,
  586. 0,
  587. NULL
  588. );
  589. if(hDisk == INVALID_HANDLE_VALUE) {
  590. return(FALSE);
  591. }
  592. b = DeviceIoControl(
  593. hDisk,
  594. IOCTL_DISK_GET_PARTITION_INFO,
  595. NULL,
  596. 0,
  597. PartitionInfo,
  598. sizeof(PARTITION_INFORMATION),
  599. &DataSize,
  600. NULL
  601. );
  602. CloseHandle(hDisk);
  603. return(b);
  604. }
  605. #ifdef UNICODE
  606. BOOL
  607. GetPartitionInfo2 (
  608. IN PCWSTR NtVolumeName,
  609. OUT PPARTITION_INFORMATION PartitionInfo
  610. )
  611. /*++
  612. Routine Description:
  613. Fill in a PARTITION_INFORMATION structure with information about
  614. a particular drive.
  615. This routine is meaningful only when run on NT -- it always fails
  616. on Win95.
  617. Arguments:
  618. NtVolumeName - supplies NT volume name whose partition info is desired.
  619. PartitionInfo - upon success, receives partition info for Drive.
  620. Return Value:
  621. Boolean value indicating whether PartitionInfo has been filled in.
  622. --*/
  623. {
  624. NTSTATUS Status;
  625. OBJECT_ATTRIBUTES Obja;
  626. UNICODE_STRING DeviceName;
  627. HANDLE hDisk;
  628. IO_STATUS_BLOCK IoStatusBlock;
  629. BOOL b = FALSE;
  630. DWORD DataSize;
  631. //
  632. // Open the file
  633. //
  634. INIT_OBJA (&Obja, &DeviceName, NtVolumeName);
  635. Status = NtOpenFile (
  636. &hDisk,
  637. (ACCESS_MASK)FILE_READ_DATA | SYNCHRONIZE,
  638. &Obja,
  639. &IoStatusBlock,
  640. FILE_SHARE_READ | FILE_SHARE_WRITE,
  641. FILE_SYNCHRONOUS_IO_NONALERT
  642. );
  643. if (NT_SUCCESS (Status)) {
  644. Status = NtDeviceIoControlFile (
  645. hDisk,
  646. 0,
  647. NULL,
  648. NULL,
  649. &IoStatusBlock,
  650. IOCTL_DISK_GET_PARTITION_INFO,
  651. NULL,
  652. 0,
  653. PartitionInfo,
  654. sizeof(PARTITION_INFORMATION)
  655. );
  656. NtClose (hDisk);
  657. b = NT_SUCCESS (Status);
  658. }
  659. return(b);
  660. }
  661. #endif
  662. BOOL
  663. IsDriveNTFT(
  664. IN TCHAR Drive,
  665. IN PCTSTR NtVolumeName
  666. )
  667. /*++
  668. Routine Description:
  669. Determine whether a drive is any kind of NTFT set.
  670. This routine is meaningful only when run on NT -- it always fails
  671. on Win95.
  672. Arguments:
  673. Drive - supplies drive letter to check; optional
  674. NtVolumeName - supplies volume name to check; required if Drive not specified
  675. Return Value:
  676. Boolean value indicating whether the drive is NTFT.
  677. --*/
  678. {
  679. PARTITION_INFORMATION PartitionInfo;
  680. if(!ISNT()) {
  681. return(FALSE);
  682. }
  683. //
  684. // If we can't open the drive, assume not NTFT.
  685. //
  686. if (Drive) {
  687. if(!GetPartitionInfo(Drive,&PartitionInfo)) {
  688. return(FALSE);
  689. }
  690. } else {
  691. #ifdef UNICODE
  692. if(!GetPartitionInfo2 (NtVolumeName, &PartitionInfo)) {
  693. return(FALSE);
  694. }
  695. #else
  696. MYASSERT (FALSE);
  697. return(FALSE);
  698. #endif
  699. }
  700. //
  701. // It's FT if the partition type is marked NTFT (ie, high bit set).
  702. //
  703. if((IsRecognizedPartition(PartitionInfo.PartitionType)) &&
  704. ((PartitionInfo.PartitionType & PARTITION_NTFT) != 0)) {
  705. #if defined(_IA64_)
  706. //
  707. // This check is dependant on the EFI system partition type not being
  708. // a recognized type. It's unlikely that we'd start recognizing it
  709. // before we start requiring GPT partitions on the system disk, but
  710. // just in case we'll assert before returning true for an ESP.
  711. //
  712. ASSERT(PartitionInfo.PartitionType != 0xef);
  713. #endif
  714. return TRUE;
  715. } else {
  716. return FALSE;
  717. }
  718. }
  719. BOOL
  720. IsDriveVeritas(
  721. IN TCHAR Drive,
  722. IN PCTSTR NtVolumeName
  723. )
  724. {
  725. TCHAR name[3];
  726. TCHAR Target[MAX_PATH];
  727. if(ISNT()) {
  728. //
  729. // Check for Veritas volume, which links to \Device\HarddiskDmVolumes...
  730. //
  731. if (Drive) {
  732. name[0] = Drive;
  733. name[1] = TEXT(':');
  734. name[2] = 0;
  735. if(!QueryDosDevice(name,Target,MAX_PATH)) {
  736. return FALSE;
  737. }
  738. } else {
  739. lstrcpy (Target, NtVolumeName);
  740. }
  741. if(!_tcsnicmp(Target,TEXT("\\Device\\HarddiskDm"),ARRAYSIZE("\\Device\\HarddiskDm") - 1)) {
  742. return(TRUE);
  743. }
  744. }
  745. return(FALSE);
  746. }
  747. //
  748. // Get Harddisk BPS
  749. // I970721
  750. //
  751. ULONG
  752. GetHDBps(
  753. HANDLE hDisk
  754. )
  755. {
  756. BOOL b;
  757. UINT rc;
  758. DWORD DataSize;
  759. DISK_GEOMETRY MediaInfo;
  760. b = DeviceIoControl(
  761. hDisk,
  762. IOCTL_DISK_GET_DRIVE_GEOMETRY,
  763. NULL,
  764. 0,
  765. &MediaInfo,
  766. sizeof(MediaInfo),
  767. &DataSize,
  768. NULL
  769. );
  770. if(!b) {
  771. return(0);
  772. } else {
  773. return(MediaInfo.BytesPerSector);
  774. }
  775. }
  776. #ifdef UNICODE
  777. #ifdef _WIN64
  778. //
  779. // define IOCTL_VOLUME_IS_PARTITION since we don't include ntddvol.h
  780. //
  781. #define IOCTL_VOLUME_IS_PARTITION CTL_CODE(IOCTL_VOLUME_BASE, 10, METHOD_BUFFERED, FILE_ANY_ACCESS)
  782. BOOL
  783. IsSoftPartition(
  784. IN TCHAR Drive,
  785. IN PCTSTR NtVolumeName
  786. )
  787. /*++
  788. Routine Description:
  789. Finds out whether the given volume is soft partition
  790. or not (i.e. does it have an underlying partition).
  791. NOTE : We just use the IOCTL_VOLUME_IS_PARTITION.
  792. Arguments:
  793. Drive - supplies drive letter for the volume
  794. NtVolumeName - supplies NT volume name
  795. Return Value:
  796. TRUE if the volume is soft partition otherwise FALSE.
  797. --*/
  798. {
  799. BOOL SoftPartition;
  800. HANDLE VolumeHandle = INVALID_HANDLE_VALUE;
  801. ULONG DataSize;
  802. //
  803. // Assume that the partition is a soft one.
  804. // If we cannot determine whether or not the partition is a soft partition, then assume it is a soft
  805. // partition. This will prevent us from placing $win_nt$.~ls in such a drive.
  806. //
  807. SoftPartition = TRUE;
  808. if (Drive) {
  809. TCHAR Name[] = TEXT("\\\\.\\?:");
  810. BOOL Result;
  811. Name[4] = Drive;
  812. VolumeHandle = CreateFile(Name,
  813. GENERIC_READ,
  814. FILE_SHARE_READ | FILE_SHARE_WRITE,
  815. NULL,
  816. OPEN_EXISTING,
  817. FILE_ATTRIBUTE_NORMAL,
  818. INVALID_HANDLE_VALUE);
  819. if (VolumeHandle != INVALID_HANDLE_VALUE) {
  820. Result = DeviceIoControl(VolumeHandle,
  821. IOCTL_VOLUME_IS_PARTITION,
  822. NULL,
  823. 0,
  824. NULL,
  825. 0,
  826. &DataSize,
  827. NULL);
  828. SoftPartition = !Result;
  829. CloseHandle(VolumeHandle);
  830. }
  831. } else {
  832. NTSTATUS Status;
  833. OBJECT_ATTRIBUTES Obja;
  834. UNICODE_STRING DeviceName;
  835. IO_STATUS_BLOCK IoStatusBlock;
  836. //
  837. // Open the file
  838. //
  839. INIT_OBJA (&Obja, &DeviceName, NtVolumeName);
  840. Status = NtOpenFile (&VolumeHandle,
  841. (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  842. &Obja,
  843. &IoStatusBlock,
  844. FILE_SHARE_READ | FILE_SHARE_WRITE,
  845. FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
  846. if (NT_SUCCESS (Status)) {
  847. Status = NtDeviceIoControlFile(VolumeHandle,
  848. 0,
  849. NULL,
  850. NULL,
  851. &IoStatusBlock,
  852. IOCTL_VOLUME_IS_PARTITION,
  853. NULL,
  854. 0,
  855. NULL,
  856. 0);
  857. if (NT_SUCCESS(Status)) {
  858. SoftPartition = FALSE;
  859. }
  860. NtClose(VolumeHandle);
  861. }
  862. }
  863. return SoftPartition;
  864. }
  865. #else
  866. BOOL
  867. IsSoftPartition(
  868. IN TCHAR Drive,
  869. IN PCTSTR NtVolumeName
  870. )
  871. {
  872. TCHAR name[] = TEXT("\\\\.\\?:");
  873. PARTITION_INFORMATION partInfo;
  874. DWORD bytes;
  875. BOOL SoftPartition = TRUE;
  876. BOOL b;
  877. HANDLE h = INVALID_HANDLE_VALUE;
  878. IO_STATUS_BLOCK IoStatusBlock;
  879. LARGE_INTEGER SoftPartitionStartingOffset;
  880. ULONG bps;
  881. NTSTATUS Status;
  882. OBJECT_ATTRIBUTES Obja;
  883. UNICODE_STRING DeviceName;
  884. DWORD DataSize;
  885. DISK_GEOMETRY MediaInfo;
  886. if( !IsDriveVeritas( Drive, NtVolumeName ) ) {
  887. return( FALSE );
  888. }
  889. //
  890. // Assume that the partition is a soft one.
  891. // If we cannot determine whether or not the partition is a soft partition, then assume it is a soft
  892. // partition. This will prevent us from placing $win_nt$.~ls in such a drive.
  893. //
  894. SoftPartition = TRUE;
  895. if (Drive) {
  896. name[4] = Drive;
  897. h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
  898. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
  899. INVALID_HANDLE_VALUE);
  900. if (h == INVALID_HANDLE_VALUE) {
  901. #if DBG
  902. GetLastError();
  903. #endif
  904. goto Exit;
  905. }
  906. b = DeviceIoControl(
  907. h,
  908. IOCTL_DISK_GET_DRIVE_GEOMETRY,
  909. NULL,
  910. 0,
  911. &MediaInfo,
  912. sizeof(MediaInfo),
  913. &DataSize,
  914. NULL
  915. );
  916. if(!b) {
  917. #if DBG
  918. GetLastError();
  919. #endif
  920. goto CleanUp;
  921. }
  922. b = DeviceIoControl(h, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0,
  923. &partInfo, sizeof(partInfo), &bytes, NULL);
  924. if (!b) {
  925. #if DBG
  926. GetLastError();
  927. #endif
  928. goto CleanUp;
  929. }
  930. } else {
  931. //
  932. // Open the file
  933. //
  934. INIT_OBJA (&Obja, &DeviceName, NtVolumeName);
  935. Status = NtOpenFile (
  936. &h,
  937. (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  938. &Obja,
  939. &IoStatusBlock,
  940. FILE_SHARE_READ | FILE_SHARE_WRITE,
  941. FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
  942. );
  943. if (!NT_SUCCESS (Status)) {
  944. goto Exit;
  945. }
  946. Status = NtDeviceIoControlFile(
  947. h,
  948. 0,
  949. NULL,
  950. NULL,
  951. &IoStatusBlock,
  952. IOCTL_DISK_GET_DRIVE_GEOMETRY,
  953. NULL,
  954. 0,
  955. &MediaInfo,
  956. sizeof(DISK_GEOMETRY)
  957. );
  958. if (!NT_SUCCESS (Status)) {
  959. goto CleanUp;
  960. }
  961. Status = NtDeviceIoControlFile(
  962. h,
  963. 0,
  964. NULL,
  965. NULL,
  966. &IoStatusBlock,
  967. IOCTL_DISK_GET_PARTITION_INFO,
  968. NULL,
  969. 0,
  970. &partInfo,
  971. sizeof(PARTITION_INFORMATION)
  972. );
  973. if (!NT_SUCCESS (Status)) {
  974. goto CleanUp;
  975. }
  976. }
  977. bps = MediaInfo.BytesPerSector;
  978. //
  979. // Find out the number of bytes per sector of the drive
  980. //
  981. //
  982. // A soft partition always starts at sector 29 (0x1d)
  983. //
  984. SoftPartitionStartingOffset.QuadPart = 29*bps;
  985. SoftPartition = ( partInfo.StartingOffset.QuadPart == SoftPartitionStartingOffset.QuadPart );
  986. CleanUp:
  987. if (Drive) {
  988. CloseHandle(h);
  989. } else {
  990. NtClose (h);
  991. }
  992. Exit:
  993. return( SoftPartition );
  994. }
  995. #endif // WIN64
  996. BOOL
  997. MyGetDiskFreeSpace (
  998. IN PCWSTR NtVolumeName,
  999. IN PDWORD SectorsPerCluster,
  1000. IN PDWORD BytesPerSector,
  1001. IN PDWORD NumberOfFreeClusters,
  1002. IN PDWORD TotalNumberOfClusters
  1003. )
  1004. {
  1005. NTSTATUS Status;
  1006. OBJECT_ATTRIBUTES Obja;
  1007. HANDLE Handle;
  1008. UNICODE_STRING VolumeName;
  1009. IO_STATUS_BLOCK IoStatusBlock;
  1010. FILE_FS_SIZE_INFORMATION SizeInfo;
  1011. INIT_OBJA (&Obja, &VolumeName, NtVolumeName);
  1012. //
  1013. // Open the file
  1014. //
  1015. Status = NtOpenFile(
  1016. &Handle,
  1017. (ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE,
  1018. &Obja,
  1019. &IoStatusBlock,
  1020. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1021. FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_FREE_SPACE_QUERY
  1022. );
  1023. if (!NT_SUCCESS (Status)) {
  1024. return FALSE;
  1025. }
  1026. //
  1027. // Determine the size parameters of the volume.
  1028. //
  1029. Status = NtQueryVolumeInformationFile(
  1030. Handle,
  1031. &IoStatusBlock,
  1032. &SizeInfo,
  1033. sizeof(SizeInfo),
  1034. FileFsSizeInformation
  1035. );
  1036. NtClose(Handle);
  1037. if (!NT_SUCCESS(Status)) {
  1038. return FALSE;
  1039. }
  1040. if (SizeInfo.TotalAllocationUnits.HighPart) {
  1041. SizeInfo.TotalAllocationUnits.LowPart = (ULONG)-1;
  1042. }
  1043. if (SizeInfo.AvailableAllocationUnits.HighPart) {
  1044. SizeInfo.AvailableAllocationUnits.LowPart = (ULONG)-1;
  1045. }
  1046. *SectorsPerCluster = SizeInfo.SectorsPerAllocationUnit;
  1047. *BytesPerSector = SizeInfo.BytesPerSector;
  1048. *NumberOfFreeClusters = SizeInfo.AvailableAllocationUnits.LowPart;
  1049. *TotalNumberOfClusters = SizeInfo.TotalAllocationUnits.LowPart;
  1050. return TRUE;
  1051. }
  1052. #endif
  1053. BOOL
  1054. IsDriveNTFS(
  1055. IN TCHAR Drive
  1056. )
  1057. /*++
  1058. Routine Description:
  1059. Determine whether a drive is any kind of NTFT set.
  1060. This routine is meaningful only when run on NT -- it always fails
  1061. on Win95.
  1062. Arguments:
  1063. Drive - supplies drive letter to check.
  1064. Return Value:
  1065. Boolean value indicating whether the drive is NTFT.
  1066. --*/
  1067. {
  1068. TCHAR DriveName[4];
  1069. TCHAR Filesystem[256];
  1070. TCHAR VolumeName[MAX_PATH];
  1071. DWORD SerialNumber;
  1072. DWORD MaxComponent;
  1073. DWORD Flags;
  1074. BOOL b;
  1075. if(!ISNT()) {
  1076. return(FALSE);
  1077. }
  1078. MYASSERT (Drive);
  1079. DriveName[0] = Drive;
  1080. DriveName[1] = TEXT(':');
  1081. DriveName[2] = TEXT('\\');
  1082. DriveName[3] = 0;
  1083. b = GetVolumeInformation(
  1084. DriveName,
  1085. VolumeName,
  1086. ARRAYSIZE(VolumeName),
  1087. &SerialNumber,
  1088. &MaxComponent,
  1089. &Flags,
  1090. Filesystem,
  1091. ARRAYSIZE(Filesystem)
  1092. );
  1093. if(!b || !lstrcmpi(Filesystem,TEXT("NTFS"))) {
  1094. return( TRUE );
  1095. }
  1096. return( FALSE );
  1097. }
  1098. DWORD
  1099. MapFileForRead(
  1100. IN LPCTSTR FileName,
  1101. OUT PDWORD FileSize,
  1102. OUT PHANDLE FileHandle,
  1103. OUT PHANDLE MappingHandle,
  1104. OUT PVOID *BaseAddress
  1105. )
  1106. /*++
  1107. Routine Description:
  1108. Open and map an entire file for read access. The file must
  1109. not be 0-length or the routine fails.
  1110. Arguments:
  1111. FileName - supplies pathname to file to be mapped.
  1112. FileSize - receives the size in bytes of the file.
  1113. FileHandle - receives the win32 file handle for the open file.
  1114. The file will be opened for generic read access.
  1115. MappingHandle - receives the win32 handle for the file mapping
  1116. object. This object will be for read access. This value is
  1117. undefined if the file being opened is 0 length.
  1118. BaseAddress - receives the address where the file is mapped. This
  1119. value is undefined if the file being opened is 0 length.
  1120. Return Value:
  1121. NO_ERROR if the file was opened and mapped successfully.
  1122. The caller must unmap the file with UnmapFile when
  1123. access to the file is no longer desired.
  1124. Win32 error code if the file was not successfully mapped.
  1125. --*/
  1126. {
  1127. DWORD rc;
  1128. //
  1129. // Open the file -- fail if it does not exist.
  1130. //
  1131. *FileHandle = CreateFile(
  1132. FileName,
  1133. GENERIC_READ,
  1134. FILE_SHARE_READ,
  1135. NULL,
  1136. OPEN_EXISTING,
  1137. 0,
  1138. NULL
  1139. );
  1140. if(*FileHandle == INVALID_HANDLE_VALUE) {
  1141. rc = GetLastError();
  1142. } else {
  1143. //
  1144. // Get the size of the file.
  1145. //
  1146. *FileSize = GetFileSize(*FileHandle,NULL);
  1147. if(*FileSize == (DWORD)(-1)) {
  1148. rc = GetLastError();
  1149. } else {
  1150. //
  1151. // Create file mapping for the whole file.
  1152. //
  1153. *MappingHandle = CreateFileMapping(
  1154. *FileHandle,
  1155. NULL,
  1156. PAGE_READONLY,
  1157. 0,
  1158. *FileSize,
  1159. NULL
  1160. );
  1161. if(*MappingHandle) {
  1162. //
  1163. // Map the whole file.
  1164. //
  1165. *BaseAddress = MapViewOfFile(
  1166. *MappingHandle,
  1167. FILE_MAP_READ,
  1168. 0,
  1169. 0,
  1170. *FileSize
  1171. );
  1172. if(*BaseAddress) {
  1173. return(NO_ERROR);
  1174. }
  1175. rc = GetLastError();
  1176. CloseHandle(*MappingHandle);
  1177. } else {
  1178. rc = GetLastError();
  1179. }
  1180. }
  1181. CloseHandle(*FileHandle);
  1182. }
  1183. return(rc);
  1184. }
  1185. DWORD
  1186. UnmapFile(
  1187. IN HANDLE MappingHandle,
  1188. IN PVOID BaseAddress
  1189. )
  1190. /*++
  1191. Routine Description:
  1192. Unmap and close a file.
  1193. Arguments:
  1194. MappingHandle - supplies the win32 handle for the open file mapping
  1195. object.
  1196. BaseAddress - supplies the address where the file is mapped.
  1197. Return Value:
  1198. NO_ERROR if the file was unmapped successfully.
  1199. Win32 error code if the file was not successfully unmapped.
  1200. --*/
  1201. {
  1202. DWORD rc;
  1203. rc = UnmapViewOfFile(BaseAddress) ? NO_ERROR : GetLastError();
  1204. if(!CloseHandle(MappingHandle)) {
  1205. if(rc == NO_ERROR) {
  1206. rc = GetLastError();
  1207. }
  1208. }
  1209. return(rc);
  1210. }
  1211. VOID
  1212. GenerateCompressedName(
  1213. IN LPCTSTR Filename,
  1214. OUT LPTSTR CompressedName
  1215. )
  1216. /*++
  1217. Routine Description:
  1218. Given a filename, generate the compressed form of the name.
  1219. The compressed form is generated as follows:
  1220. Look backwards for a dot. If there is no dot, append "._" to the name.
  1221. If there is a dot followed by 0, 1, or 2 charcaters, append "_".
  1222. Otherwise assume there is a 3-character extension and replace the
  1223. third character after the dot with "_".
  1224. Arguments:
  1225. Filename - supplies filename whose compressed form is desired.
  1226. CompressedName - receives compressed form. This routine assumes
  1227. that this buffer is MAX_PATH TCHARs in size.
  1228. Return Value:
  1229. None.
  1230. --*/
  1231. {
  1232. LPTSTR p,q;
  1233. //
  1234. // Leave room for the worst case, namely where there's no extension
  1235. // (and we thus have to append ._).
  1236. //
  1237. lstrcpyn(CompressedName,Filename,MAX_PATH-2);
  1238. p = _tcsrchr(CompressedName,TEXT('.'));
  1239. q = _tcsrchr(CompressedName,TEXT('\\'));
  1240. if(q < p) {
  1241. //
  1242. // If there are 0, 1, or 2 characters after the dot, just append
  1243. // the underscore. p points to the dot so include that in the length.
  1244. //
  1245. if(lstrlen(p) < 4) {
  1246. lstrcat(CompressedName,TEXT("_"));
  1247. } else {
  1248. //
  1249. // Assume there are 3 characters in the extension and replace
  1250. // the final one with an underscore.
  1251. //
  1252. p[3] = TEXT('_');
  1253. MYASSERT (!p[4]);
  1254. }
  1255. } else {
  1256. //
  1257. // No dot, just add ._.
  1258. //
  1259. lstrcat(CompressedName,TEXT("._"));
  1260. }
  1261. }
  1262. DWORD
  1263. CreateMultiLevelDirectory(
  1264. IN LPCTSTR Directory
  1265. )
  1266. /*++
  1267. Routine Description:
  1268. This routine ensures that a multi-level path exists by creating individual
  1269. levels one at a time. It can handle either paths of form x:... or \\?\Volume{...
  1270. Arguments:
  1271. Directory - supplies fully-qualified Win32 pathspec of directory to create
  1272. Return Value:
  1273. Win32 error code indicating outcome.
  1274. --*/
  1275. {
  1276. TCHAR Buffer[MAX_PATH];
  1277. PTCHAR p,q;
  1278. TCHAR c;
  1279. BOOL Done;
  1280. DWORD d = ERROR_SUCCESS;
  1281. INT Skip=0;
  1282. if (FAILED(StringCchCopy(Buffer,ARRAYSIZE(Buffer),Directory))) {
  1283. return ERROR_INSUFFICIENT_BUFFER;
  1284. }
  1285. //
  1286. // If it already exists do nothing. (We do this before syntax checking
  1287. // to allow for remote paths that already exist. This is needed for
  1288. // remote boot machines.)
  1289. //
  1290. d = GetFileAttributes(Buffer);
  1291. if(d != (DWORD)(-1)) {
  1292. return((d & FILE_ATTRIBUTE_DIRECTORY) ? NO_ERROR : ERROR_DIRECTORY);
  1293. }
  1294. //
  1295. // Check path format
  1296. //
  1297. c = (TCHAR)CharUpper((LPTSTR)Buffer[0]);
  1298. if(((c < TEXT('A')) || (c > TEXT('Z')) || (Buffer[1] != TEXT(':'))) && c != TEXT('\\')) {
  1299. return(ERROR_INVALID_PARAMETER);
  1300. }
  1301. if (c != TEXT('\\')) {
  1302. //
  1303. // Ignore drive roots, which we allow to be either x:\ or x:.
  1304. //
  1305. if(Buffer[2] != TEXT('\\')) {
  1306. return(Buffer[2] ? ERROR_INVALID_PARAMETER : ERROR_SUCCESS);
  1307. }
  1308. q = Buffer + 3;
  1309. if(*q == 0) {
  1310. return(ERROR_SUCCESS);
  1311. }
  1312. } else {
  1313. //
  1314. // support \\server\share[\xxx] format
  1315. //
  1316. q = NULL;
  1317. if (Buffer[1] != TEXT('\\') || Buffer[1] != 0 && Buffer[2] == TEXT('\\')) {
  1318. return(ERROR_INVALID_PARAMETER);
  1319. }
  1320. q = _tcschr (&Buffer[2], TEXT('\\'));
  1321. if (!q) {
  1322. return(ERROR_INVALID_PARAMETER);
  1323. }
  1324. if (q[1] == TEXT('\\')) {
  1325. return(ERROR_INVALID_PARAMETER);
  1326. }
  1327. q = _tcschr (&q[1], TEXT('\\'));
  1328. if (!q) {
  1329. return(ERROR_SUCCESS);
  1330. }
  1331. q++;
  1332. #ifdef UNICODE
  1333. //
  1334. // Hack to make sure the system partition case works on IA64 (arc)
  1335. // We believe this should be the only case where we use a
  1336. // GlobalRoot style name as the other cases deal with OEM partitions etc.
  1337. // which we should never touch. WE skip over by the length of
  1338. // SystemPartitionVolumeGuid. We take care of the \ present at the end.
  1339. //
  1340. if (SystemPartitionVolumeGuid != NULL && _wcsnicmp (Buffer, SystemPartitionVolumeGuid, (wcslen(SystemPartitionVolumeGuid)-1)) == 0 ){
  1341. Skip = wcslen(SystemPartitionVolumeGuid)-1;
  1342. } else if (_wcsnicmp (Buffer, L"\\\\?\\Volume{", LENGTHOF("\\\\?\\Volume{")) == 0 &&
  1343. lstrlenW (Buffer) > 47 &&
  1344. Buffer[47] == L'}') {
  1345. //
  1346. // skip over the VolumeGUID part
  1347. //
  1348. Skip = 48;
  1349. }
  1350. if (Skip > 0) {
  1351. if (Buffer[Skip] == 0) {
  1352. return ERROR_SUCCESS;
  1353. }
  1354. q = Buffer + Skip + 1;
  1355. }
  1356. #endif
  1357. }
  1358. Done = FALSE;
  1359. do {
  1360. //
  1361. // Locate the next path sep char. If there is none then
  1362. // this is the deepest level of the path.
  1363. //
  1364. if(p = _tcschr(q,TEXT('\\'))) {
  1365. *p = 0;
  1366. } else {
  1367. Done = TRUE;
  1368. }
  1369. //
  1370. // Create this portion of the path.
  1371. //
  1372. if(CreateDirectory(Buffer,NULL)) {
  1373. d = ERROR_SUCCESS;
  1374. } else {
  1375. d = GetLastError();
  1376. if(d == ERROR_ALREADY_EXISTS) {
  1377. d = ERROR_SUCCESS;
  1378. }
  1379. }
  1380. if(d == ERROR_SUCCESS) {
  1381. //
  1382. // Put back the path sep and move to the next component.
  1383. //
  1384. if(!Done) {
  1385. *p = TEXT('\\');
  1386. q = p+1;
  1387. }
  1388. } else {
  1389. Done = TRUE;
  1390. }
  1391. } while(!Done);
  1392. return(d);
  1393. }
  1394. BOOL
  1395. ForceFileNoCompress(
  1396. IN LPCTSTR Filename
  1397. )
  1398. /*++
  1399. Routine Description:
  1400. This routine makes sure that a file on a volume that supports per-file
  1401. compression is not compressed. The caller need not ensure that the volume
  1402. actually supports this, since this routine will query the attributes of
  1403. the file before deciding whether any operation is actually necessary,
  1404. and the compressed attribute will not be set on volumes that don't support
  1405. per-file compression.
  1406. It assumed that the file exists. If the file does not exist, this routine
  1407. will fail.
  1408. Arguments:
  1409. Filename - supplies the filename of the file to mke uncompressed.
  1410. Return Value:
  1411. Boolean value indicating outcome. If FALSE, last error is set.
  1412. --*/
  1413. {
  1414. ULONG d;
  1415. HANDLE h;
  1416. BOOL b;
  1417. USHORT u;
  1418. DWORD Attributes;
  1419. Attributes = GetFileAttributes(Filename);
  1420. if(Attributes == (DWORD)(-1)) {
  1421. return(FALSE);
  1422. }
  1423. if(!(Attributes & FILE_ATTRIBUTE_COMPRESSED)) {
  1424. return(TRUE);
  1425. }
  1426. //
  1427. // Temporarily nullify attributes that might prevent opening
  1428. // the file for read-write access.
  1429. //
  1430. // We preserve the 'standard' attributes that the file might have,
  1431. // to be restored later.
  1432. //
  1433. Attributes &= (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE);
  1434. SetFileAttributes(Filename,FILE_ATTRIBUTE_NORMAL);
  1435. h = CreateFile(
  1436. Filename,
  1437. FILE_READ_DATA | FILE_WRITE_DATA,
  1438. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1439. NULL,
  1440. OPEN_EXISTING,
  1441. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
  1442. NULL
  1443. );
  1444. if(h == INVALID_HANDLE_VALUE) {
  1445. SetFileAttributes(Filename,Attributes);
  1446. return(FALSE);
  1447. }
  1448. u = 0;
  1449. b = DeviceIoControl( h,
  1450. FSCTL_SET_COMPRESSION,
  1451. &u,
  1452. sizeof(u),
  1453. NULL,
  1454. 0,
  1455. &d,
  1456. FALSE);
  1457. d = GetLastError();
  1458. CloseHandle(h);
  1459. SetFileAttributes(Filename,Attributes);
  1460. SetLastError(d);
  1461. return(b);
  1462. }
  1463. BOOL
  1464. IsCurrentOsServer(
  1465. void
  1466. )
  1467. {
  1468. LONG l;
  1469. HKEY hKey;
  1470. DWORD d;
  1471. DWORD Size;
  1472. TCHAR Value[100];
  1473. DWORD Type;
  1474. l = RegCreateKeyEx(
  1475. HKEY_LOCAL_MACHINE,
  1476. TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"),
  1477. 0,
  1478. NULL,
  1479. 0,
  1480. KEY_QUERY_VALUE,
  1481. NULL,
  1482. &hKey,
  1483. &d
  1484. );
  1485. if (l != NO_ERROR) {
  1486. return FALSE;
  1487. }
  1488. Size = sizeof(Value);
  1489. l = RegQueryValueEx(hKey,TEXT("ProductType"),NULL,&Type,(LPBYTE)Value,&Size);
  1490. RegCloseKey(hKey);
  1491. if (l != NO_ERROR) {
  1492. return FALSE;
  1493. }
  1494. if (lstrcmpi(Value,TEXT("winnt")) == 0) {
  1495. return FALSE;
  1496. }
  1497. return TRUE;
  1498. }
  1499. BOOL
  1500. IsCurrentAdvancedServer(
  1501. void
  1502. )
  1503. {
  1504. LONG l;
  1505. HKEY hKey;
  1506. DWORD d;
  1507. DWORD Size;
  1508. TCHAR Value[100];
  1509. DWORD Type;
  1510. l = RegCreateKeyEx(
  1511. HKEY_LOCAL_MACHINE,
  1512. TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"),
  1513. 0,
  1514. NULL,
  1515. 0,
  1516. KEY_QUERY_VALUE,
  1517. NULL,
  1518. &hKey,
  1519. &d
  1520. );
  1521. if (l != NO_ERROR) {
  1522. return FALSE;
  1523. }
  1524. Size = sizeof(Value);
  1525. l = RegQueryValueEx(hKey,TEXT("ProductType"),NULL,&Type,(LPBYTE)Value,&Size);
  1526. RegCloseKey(hKey);
  1527. if (l != NO_ERROR) {
  1528. return FALSE;
  1529. }
  1530. if (lstrcmpi(Value,TEXT("lanmannt")) == 0) {
  1531. return TRUE;
  1532. }
  1533. return FALSE;
  1534. }
  1535. BOOL
  1536. ConcatenateFile(
  1537. IN HANDLE OpenFile,
  1538. IN LPTSTR FileName
  1539. )
  1540. /*++
  1541. Routine Description:
  1542. This routine will go load the named file, and concatenate its
  1543. contents into the open file.
  1544. Arguments:
  1545. OpenFile Handle to the open file
  1546. FileName The name of the file we're going to concatenate.
  1547. Return Value:
  1548. TRUE Everything went okay.
  1549. FALSE We failed.
  1550. --*/
  1551. {
  1552. DWORD rc;
  1553. HANDLE hFile, hFileMapping;
  1554. DWORD FileSize, BytesWritten;
  1555. PVOID pFileBase;
  1556. BOOL ReturnValue = FALSE;
  1557. //
  1558. // Open the file...
  1559. //
  1560. rc = MapFileForRead (
  1561. FileName,
  1562. &FileSize,
  1563. &hFile,
  1564. &hFileMapping,
  1565. &pFileBase
  1566. );
  1567. if (rc == NO_ERROR) {
  1568. //
  1569. // Write the file...
  1570. //
  1571. if (!WriteFile( OpenFile, pFileBase, FileSize, &BytesWritten, NULL )) {
  1572. rc = GetLastError ();
  1573. ReturnValue = FALSE;
  1574. }
  1575. UnmapFile (hFileMapping, pFileBase);
  1576. CloseHandle (hFile);
  1577. }
  1578. if (!ReturnValue) {
  1579. SetLastError (rc);
  1580. }
  1581. return( ReturnValue );
  1582. }
  1583. BOOL
  1584. FileExists(
  1585. IN PCTSTR FileName,
  1586. OUT PWIN32_FIND_DATA FindData OPTIONAL
  1587. )
  1588. /*++
  1589. Routine Description:
  1590. Determine if a file exists and is accessible.
  1591. Errormode is set (and then restored) so the user will not see
  1592. any pop-ups.
  1593. Arguments:
  1594. FileName - supplies full path of file to check for existance.
  1595. FindData - if specified, receives find data for the file.
  1596. Return Value:
  1597. TRUE if the file exists and is accessible.
  1598. FALSE if not. GetLastError() returns extended error info.
  1599. --*/
  1600. {
  1601. WIN32_FIND_DATA findData;
  1602. HANDLE FindHandle;
  1603. UINT OldMode;
  1604. DWORD Error;
  1605. FindHandle = FindFirstFile(FileName,&findData);
  1606. if(FindHandle == INVALID_HANDLE_VALUE) {
  1607. Error = GetLastError();
  1608. } else {
  1609. FindClose(FindHandle);
  1610. if(FindData) {
  1611. *FindData = findData;
  1612. }
  1613. Error = NO_ERROR;
  1614. }
  1615. SetLastError(Error);
  1616. return (Error == NO_ERROR);
  1617. }
  1618. BOOL
  1619. DoesDirectoryExist (
  1620. IN PCTSTR DirSpec
  1621. )
  1622. /*++
  1623. Routine Description:
  1624. Determine if a directory exists and is accessible.
  1625. This routine works even with the root of drives or with the root of
  1626. network shares (like \\server\share).
  1627. Arguments:
  1628. DirSpec - supplies full path of dir to check for existance;
  1629. Return Value:
  1630. TRUE if the dir exists and is accessible.
  1631. --*/
  1632. {
  1633. TCHAR pattern[MAX_PATH];
  1634. BOOL b = FALSE;
  1635. if (DirSpec) {
  1636. if (BuildPathEx (pattern, ARRAYSIZE(pattern), DirSpec, TEXT("*"))) {
  1637. WIN32_FIND_DATA fd;
  1638. UINT oldMode;
  1639. HANDLE h;
  1640. oldMode = SetErrorMode (SEM_FAILCRITICALERRORS);
  1641. h = FindFirstFile (pattern, &fd);
  1642. if (h != INVALID_HANDLE_VALUE) {
  1643. FindClose (h);
  1644. b = TRUE;
  1645. }
  1646. SetErrorMode (oldMode);
  1647. }
  1648. }
  1649. return b;
  1650. }
  1651. #if defined(_AMD64_) || defined(_X86_)
  1652. BOOLEAN
  1653. IsValidDrive(
  1654. IN TCHAR Drive
  1655. )
  1656. /*++
  1657. Routine Description:
  1658. This routine check formatted disk type
  1659. NEC98 of NT4 has supported NEC98 format and PC-AT format.
  1660. But BIOS is handling only NEC98 format.
  1661. So We need setup Boot stuff to ONLY NEC98 formated HD.
  1662. Arguments:
  1663. Drive Drive letter.
  1664. Return Value:
  1665. TRUE Dive is NEC98 format.
  1666. FALSE Drive is not NEC98 format.
  1667. --*/
  1668. {
  1669. HANDLE hDisk;
  1670. TCHAR HardDiskName[] = TEXT("\\\\.\\?:");
  1671. PUCHAR pBuffer,pUBuffer;
  1672. WCHAR Buffer[128];
  1673. WCHAR DevicePath[128];
  1674. WCHAR DriveName[3];
  1675. WCHAR DiskNo;
  1676. STORAGE_DEVICE_NUMBER number;
  1677. PWCHAR p;
  1678. ULONG bps;
  1679. NTSTATUS Sts;
  1680. DWORD DataSize,ExtentSize;
  1681. BOOL b;
  1682. PVOLUME_DISK_EXTENTS Extent;
  1683. if (!ISNT())
  1684. return TRUE;
  1685. HardDiskName[4] = Drive;
  1686. DriveName[0] = Drive;
  1687. DriveName[1] = ':';
  1688. DriveName[2] = 0;
  1689. if(QueryDosDeviceW(DriveName, Buffer, ARRAYSIZE(Buffer))) {
  1690. if (BuildNumber <= NT40){ //check NT Version
  1691. //
  1692. // QueryDosDevice in NT3.51 is buggy.
  1693. // This API return "\\Harddisk\...." or
  1694. // "\\harddisk\...."
  1695. // We need work around.
  1696. //
  1697. p = wcsstr(Buffer, L"arddisk");
  1698. if (!p) {
  1699. return FALSE;
  1700. }
  1701. DiskNo = (*(p + LENGTHOF(L"arddisk")) - 0x30);
  1702. } else {
  1703. hDisk = CreateFile(
  1704. HardDiskName,
  1705. 0,
  1706. FILE_SHARE_WRITE, NULL,
  1707. OPEN_EXISTING, 0, NULL
  1708. );
  1709. if(hDisk == INVALID_HANDLE_VALUE) {
  1710. return FALSE;
  1711. }
  1712. b = DeviceIoControl(hDisk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
  1713. &number, sizeof(number), &DataSize, NULL);
  1714. if (b) {
  1715. DiskNo = (TCHAR) number.DeviceNumber;
  1716. } else {
  1717. Extent = malloc(1024);
  1718. ExtentSize = 1024;
  1719. if(!Extent) {
  1720. CloseHandle( hDisk );
  1721. return FALSE;
  1722. }
  1723. b = DeviceIoControl(hDisk, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
  1724. NULL, 0,
  1725. (PVOID)Extent, ExtentSize, &DataSize, NULL);
  1726. if (!b) {
  1727. free(Extent);
  1728. CloseHandle( hDisk );
  1729. return FALSE;
  1730. }
  1731. if (Extent->NumberOfDiskExtents != 1){
  1732. free(Extent);
  1733. CloseHandle( hDisk );
  1734. return FALSE;
  1735. }
  1736. DiskNo = (TCHAR)Extent->Extents->DiskNumber;
  1737. free(Extent);
  1738. }
  1739. CloseHandle(hDisk);
  1740. }
  1741. if (FAILED (StringCchPrintfW (DevicePath, ARRAYSIZE(DevicePath), L"\\\\.\\PHYSICALDRIVE%u", DiskNo))) {
  1742. MYASSERT (FALSE);
  1743. return FALSE;
  1744. }
  1745. hDisk = CreateFileW( DevicePath,
  1746. GENERIC_READ|GENERIC_WRITE,
  1747. FILE_SHARE_READ, NULL,
  1748. OPEN_EXISTING, 0, NULL);
  1749. if(hDisk == INVALID_HANDLE_VALUE) {
  1750. return FALSE;
  1751. }
  1752. if ((bps = GetHDBps(hDisk)) == 0){
  1753. CloseHandle(hDisk);
  1754. return FALSE;
  1755. }
  1756. pUBuffer = MALLOC(bps * 2);
  1757. if (!pUBuffer) {
  1758. CloseHandle(hDisk);
  1759. return FALSE;
  1760. }
  1761. pBuffer = ALIGN(pUBuffer, bps);
  1762. RtlZeroMemory(pBuffer, bps);
  1763. Sts = SpReadWriteDiskSectors(hDisk,0,1,bps,pBuffer, NEC_READSEC);
  1764. if(!NT_SUCCESS(Sts)) {
  1765. FREE(pUBuffer);
  1766. CloseHandle(hDisk);
  1767. return FALSE;
  1768. }
  1769. if (!(pBuffer[4] == 'I'
  1770. && pBuffer[5] == 'P'
  1771. && pBuffer[6] == 'L'
  1772. && pBuffer[7] == '1')){
  1773. FREE(pUBuffer);
  1774. CloseHandle(hDisk);
  1775. return FALSE;
  1776. }
  1777. FREE(pUBuffer);
  1778. CloseHandle(hDisk);
  1779. return TRUE;
  1780. }
  1781. return FALSE;
  1782. }
  1783. #endif
  1784. #ifdef _X86_
  1785. BOOLEAN
  1786. CheckATACardonNT4(
  1787. IN HANDLE hDisk
  1788. )
  1789. {
  1790. //
  1791. // NT4, NT3.51 for NEC98.
  1792. // NEC98 does not handle to boot from PCMCIA ATA card disk.
  1793. // So we need to check ATA Disk.
  1794. //
  1795. // Return
  1796. // TRUE is ATA Card
  1797. // FALSE is Other
  1798. //
  1799. #define IOCTL_DISK_GET_FORMAT_MEDIA CTL_CODE(IOCTL_DISK_BASE, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
  1800. #define FORMAT_MEDIA_98 0 // TYPE NEC98
  1801. #define FORMAT_MEDIA_AT 1 // TYPE PC-AT
  1802. #define FORMAT_MEDIA_OTHER 2 // Unknown
  1803. struct _OutBuffer {
  1804. ULONG CurrentFormatMedia;
  1805. ULONG InitializeFormatMedia;
  1806. } OutBuffer;
  1807. DWORD ReturnedByteCount;
  1808. if (!(DeviceIoControl(hDisk, IOCTL_DISK_GET_FORMAT_MEDIA, NULL,
  1809. 0,
  1810. &OutBuffer,
  1811. sizeof(struct _OutBuffer),
  1812. &ReturnedByteCount,
  1813. NULL
  1814. ) )){
  1815. return FALSE;
  1816. }
  1817. if (OutBuffer.InitializeFormatMedia == FORMAT_MEDIA_AT){
  1818. return TRUE;
  1819. }
  1820. return FALSE;
  1821. }
  1822. #endif
  1823. BOOL
  1824. IsMachineSupported(
  1825. OUT PCOMPATIBILITY_ENTRY CompEntry
  1826. )
  1827. /*++
  1828. Routine Description:
  1829. This function determines whether or not the machine is supported by the version
  1830. of NT to be installed.
  1831. Arguments:
  1832. CompEntry - If the machine is not supported, the Compatability Entry
  1833. is updated to describe why the machine is not supported.
  1834. Return Value:
  1835. Boolean value indicating whether the machine is supported.
  1836. --*/
  1837. {
  1838. TCHAR SetupLogPath[MAX_PATH];
  1839. TCHAR KeyName[MAX_PATH];
  1840. TCHAR HalName[MAX_PATH];
  1841. LPTSTR p;
  1842. LPTSTR szHalDll = TEXT("hal.dll");
  1843. LPTSTR SectionName;
  1844. LPTSTR UnsupportedName;
  1845. BOOL b;
  1846. //
  1847. // Assume that the machine is supported
  1848. //
  1849. b = TRUE;
  1850. #ifdef _X86_
  1851. try {
  1852. ULONG Name0, Name1, Name2, Family, Flags;
  1853. _asm {
  1854. push ebx ;; save ebx
  1855. mov eax, 0 ;; get processor vendor
  1856. _emit 00fh ;; CPUID(0)
  1857. _emit 0a2h ;;
  1858. mov Name0, ebx ;; Name[0-3]
  1859. mov Name1, edx ;; Name[4-7]
  1860. mov Name2, ecx ;; Name[8-11]
  1861. mov eax, 1 ;; get family/model/stepping and features
  1862. _emit 00fh ;; CPUID(1)
  1863. _emit 0a2h
  1864. mov Family, eax ;; save family/model/stepping
  1865. mov Flags, edx ;; save flags returned by CPUID
  1866. pop ebx ;; restore ebx
  1867. }
  1868. //
  1869. // Check the cmpxchg8b flag in the flags returned by CPUID.
  1870. //
  1871. if ((Flags & 0x100) == 0) {
  1872. //
  1873. // This processor doesn't support the CMPXCHG instruction
  1874. // which is required for Whistler.
  1875. //
  1876. // Some processors actually do support it but claim they
  1877. // don't because of a bug in NT 4. See if this processor
  1878. // is one of these.
  1879. //
  1880. if (!(((Name0 == 'uneG') &&
  1881. (Name1 == 'Teni') &&
  1882. (Name2 == '68xM') &&
  1883. (Family >= 0x542)) ||
  1884. (Name0 == 'tneC') &&
  1885. (Name1 == 'Hrua') &&
  1886. (Name2 == 'slua') &&
  1887. (Family >= 0x500))) {
  1888. b = FALSE;
  1889. }
  1890. }
  1891. } except(EXCEPTION_EXECUTE_HANDLER) {
  1892. //
  1893. // If this processor doesn't support CPUID, we don't
  1894. // run on it.
  1895. //
  1896. b = FALSE;
  1897. }
  1898. if (!b) {
  1899. CompEntry->HtmlName = TEXT("cpufeat.htm");
  1900. CompEntry->TextName = TEXT("cpufeat.txt");
  1901. SectionName = TEXT("UnsupportedArchitectures");
  1902. UnsupportedName = TEXT("missprocfeat");
  1903. }
  1904. #endif // _X86_
  1905. if( b && ISNT() ) {
  1906. //
  1907. // Build the path to setup.log
  1908. //
  1909. MyGetWindowsDirectory( SetupLogPath, ARRAYSIZE(SetupLogPath) );
  1910. ConcatenatePaths( SetupLogPath, TEXT("repair\\setup.log"), ARRAYSIZE(SetupLogPath));
  1911. //
  1912. // Find out the actual name of the hal installed
  1913. //
  1914. if (!IsArc()) {
  1915. #if defined(_AMD64_) || defined(_X86_)
  1916. //
  1917. // On BIOS, look for %windir%\system32\hal.dll in the section
  1918. // [Files.WinNt]
  1919. //
  1920. GetSystemDirectory( KeyName, MAX_PATH );
  1921. ConcatenatePaths(KeyName, szHalDll, MAX_PATH );
  1922. SectionName = TEXT("Files.WinNt");
  1923. //
  1924. // While we are at it, see if this is Windows 2000 or higher
  1925. // to see if the hal should be preserved or not
  1926. //
  1927. #ifdef UNICODE
  1928. if (BUILDNUM() >= 2195) {
  1929. PCHAR halName;
  1930. halName = FindRealHalName( KeyName );
  1931. if (halName) {
  1932. WriteAcpiHalValue = TRUE;
  1933. #if defined(_AMD64_)
  1934. if (!strcmp(halName,"hal")) {
  1935. #else
  1936. if (!strcmp(halName,"halacpi") ||
  1937. !strcmp(halName,"halmacpi") ||
  1938. !strcmp(halName,"halaacpi")) {
  1939. #endif
  1940. AcpiHalValue = TRUE;
  1941. }
  1942. }
  1943. }
  1944. #endif // UNICODE
  1945. #endif // defined(_AMD64_) || defined(_X86_)
  1946. } else {
  1947. #ifdef UNICODE // Always true for ARC, never true for Win9x upgrade
  1948. //
  1949. // On ARC, look for hal.dll in the section [Files.SystemPartition]
  1950. //
  1951. lstrcpy( KeyName, szHalDll );
  1952. SectionName = TEXT("Files.SystemPartition");
  1953. #endif // UNICODE
  1954. } // if (!IsArc())
  1955. GetPrivateProfileString( SectionName,
  1956. KeyName,
  1957. TEXT(""),
  1958. HalName,
  1959. sizeof(HalName)/sizeof(TCHAR),
  1960. SetupLogPath );
  1961. //
  1962. // GetPrivateProfileString() will strip the first and last '"' from the logged value,
  1963. // so find the next '"' character and replace it with NUL, and we will end up with
  1964. // the actual hal name.
  1965. //
  1966. if( lstrlen(HalName) &&
  1967. ( p = _tcschr( HalName, TEXT('"') ) )
  1968. ) {
  1969. *p = TEXT('\0');
  1970. //
  1971. // Find out if the hal is listed in [UnsupportedArchitectures] (dosnet.inf)
  1972. //
  1973. SectionName = TEXT("UnsupportedArchitectures");
  1974. b = !InfDoesLineExistInSection( MainInf,
  1975. SectionName,
  1976. HalName );
  1977. UnsupportedName = HalName;
  1978. }
  1979. }
  1980. //
  1981. // If architecture is not supported, look up the description.
  1982. //
  1983. if( !b ) {
  1984. CompEntry->Description = (LPTSTR)InfGetFieldByKey( MainInf,
  1985. SectionName,
  1986. UnsupportedName,
  1987. 0 );
  1988. }
  1989. return( b );
  1990. }
  1991. BOOL
  1992. UnsupportedArchitectureCheck(
  1993. PCOMPAIBILITYCALLBACK CompatibilityCallback,
  1994. LPVOID Context
  1995. )
  1996. /*++
  1997. Routine Description:
  1998. Check if the machine is no longer supported by Windows NT.
  1999. This routine is meaningful only when run on NT -- it always succeeds
  2000. on Win95.
  2001. Arguments:
  2002. CompatibilityCallback - pointer to call back function
  2003. Context - context pointer
  2004. Return Value:
  2005. Returns always TRUE.
  2006. --*/
  2007. {
  2008. COMPATIBILITY_ENTRY CompEntry;
  2009. CompEntry.Description = TEXT("MCA");//BUGBUG: must be changed
  2010. #ifdef _X86_
  2011. CompEntry.HtmlName = TEXT("mca.htm");
  2012. CompEntry.TextName = TEXT("mca.txt");
  2013. #else
  2014. CompEntry.HtmlName = TEXT("");
  2015. CompEntry.TextName = TEXT("");
  2016. #endif
  2017. CompEntry.RegKeyName = NULL;
  2018. CompEntry.RegValName = NULL;
  2019. CompEntry.RegValDataSize = 0;
  2020. CompEntry.RegValData = NULL;
  2021. CompEntry.SaveValue = NULL;
  2022. CompEntry.Flags = 0;
  2023. CompEntry.InfName = NULL;
  2024. CompEntry.InfSection = NULL;
  2025. if( !IsMachineSupported( &CompEntry ) ) {
  2026. if(!CompatibilityCallback(&CompEntry, Context)){
  2027. DWORD Error;
  2028. Error = GetLastError();
  2029. }
  2030. }
  2031. return( TRUE );
  2032. }
  2033. BOOL
  2034. GetUserPrintableFileSizeString(
  2035. IN DWORDLONG Size,
  2036. OUT LPTSTR Buffer,
  2037. IN DWORD BufferSize
  2038. )
  2039. /*++
  2040. Routine Description:
  2041. Takes a size and comes up with a printable version of this size,
  2042. using the appropriate size format (ie., KB, MB, GB, Bytes, etc.)
  2043. Arguments:
  2044. Size - size to be converted (in bytes)
  2045. Buffer - string buffer to receive the data
  2046. BufferSize - indicates the buffer size, *in characters*
  2047. Return Value:
  2048. TRUE indicates success, FALSE indicates failure. If we fail,
  2049. call GetLastError() to get extended failure status.
  2050. --*/
  2051. {
  2052. LPTSTR NumberString;
  2053. UINT uResource;
  2054. TCHAR ResourceString[100];
  2055. DWORD cb;
  2056. DWORD d;
  2057. BOOL RetVal = FALSE;
  2058. DWORDLONG TopPart;
  2059. DWORDLONG BottomPart;
  2060. //
  2061. // Determine which resource string to use
  2062. //
  2063. if (Size < 1024) {
  2064. uResource = IDS_SIZE_BYTES;
  2065. TopPart = 0;
  2066. BottomPart = 1;
  2067. wsprintf(ResourceString, TEXT("%u"), Size);
  2068. } else if (Size < (1024 * 1024)) {
  2069. uResource = IDS_SIZE_KBYTES;
  2070. TopPart = (Size%1024)*100;
  2071. BottomPart = 1024;
  2072. wsprintf(ResourceString,
  2073. TEXT("%u.%02u"),
  2074. (DWORD) (Size / 1024),
  2075. (DWORD)(TopPart/BottomPart));
  2076. } else if (Size < (1024 * 1024 * 1024)) {
  2077. uResource = IDS_SIZE_MBYTES;
  2078. TopPart = (Size%(1024*1024))*100;
  2079. BottomPart = 1024*1024;
  2080. wsprintf(ResourceString,
  2081. TEXT("%u.%02u"),
  2082. (DWORD)(Size / (1024 * 1024)),
  2083. (DWORD)(TopPart/BottomPart) );
  2084. } else {
  2085. uResource = IDS_SIZE_GBYTES;
  2086. TopPart = (Size%(1024*1024*1024))*100;
  2087. BottomPart = 1024*1024*1024;
  2088. wsprintf(ResourceString,
  2089. TEXT("%u.%02u"),
  2090. (DWORD)(Size / (1024 * 1024 * 1024)),
  2091. (DWORD)(TopPart/BottomPart) );
  2092. }
  2093. // Format the number string
  2094. cb = GetNumberFormat(LOCALE_USER_DEFAULT, 0, ResourceString, NULL, NULL, 0);
  2095. NumberString = (LPTSTR) MALLOC((cb + 1) * sizeof(TCHAR));
  2096. if (!NumberString) {
  2097. d = ERROR_NOT_ENOUGH_MEMORY;
  2098. RetVal = FALSE;
  2099. goto e0;
  2100. }
  2101. if (!GetNumberFormat(LOCALE_USER_DEFAULT, 0, ResourceString, NULL, NumberString, cb)) {
  2102. *NumberString = 0;
  2103. }
  2104. if (!LoadString(hInst, uResource, ResourceString, ARRAYSIZE(ResourceString))) {
  2105. ResourceString[0] = 0;
  2106. }
  2107. RetVal = SUCCEEDED (StringCchPrintf (Buffer, BufferSize, ResourceString, NumberString));
  2108. d = RetVal ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
  2109. FREE(NumberString);
  2110. e0:
  2111. SetLastError(d);
  2112. return(RetVal);
  2113. }
  2114. BOOL
  2115. BuildSystemPartitionPathToFile (
  2116. IN PCTSTR FileName,
  2117. OUT PTSTR Path,
  2118. IN INT BufferSizeChars
  2119. )
  2120. {
  2121. //
  2122. // must have a root
  2123. //
  2124. if(SystemPartitionDriveLetter) {
  2125. Path[0] = SystemPartitionDriveLetter;
  2126. Path[1] = TEXT(':');
  2127. Path[2] = 0;
  2128. } else {
  2129. #ifdef UNICODE
  2130. if (SystemPartitionVolumeGuid) {
  2131. if (FAILED (StringCchCopy (Path, BufferSizeChars, SystemPartitionVolumeGuid))) {
  2132. //
  2133. // why is the buffer so small?
  2134. //
  2135. MYASSERT (FALSE);
  2136. return FALSE;
  2137. }
  2138. }
  2139. else
  2140. #endif
  2141. {
  2142. MYASSERT (FALSE);
  2143. return FALSE;
  2144. }
  2145. }
  2146. return ConcatenatePaths (Path, FileName, BufferSizeChars);
  2147. }
  2148. PTSTR
  2149. BuildPathEx (
  2150. IN PTSTR DestPath,
  2151. IN DWORD Chars,
  2152. IN PCTSTR Path1,
  2153. IN PCTSTR Path2
  2154. )
  2155. {
  2156. PTSTR p;
  2157. INT i;
  2158. BOOL haveWack = FALSE;
  2159. p = _tcsrchr (Path1, TEXT('\\'));
  2160. if (p && !p[1]) {
  2161. haveWack = TRUE;
  2162. }
  2163. if (*Path2 == TEXT('\\')) {
  2164. if (haveWack) {
  2165. Path2++;
  2166. } else {
  2167. haveWack = TRUE;
  2168. }
  2169. }
  2170. if (FAILED (StringCchPrintfEx (DestPath, Chars, &p, NULL, STRSAFE_NULL_ON_FAILURE, haveWack ? TEXT("%s%s") : TEXT("%s\\%s"), Path1, Path2))) {
  2171. //
  2172. // why is the buffer so small?
  2173. //
  2174. MYASSERT (Chars > sizeof(PTSTR) / sizeof (TCHAR));
  2175. SetLastError (ERROR_INSUFFICIENT_BUFFER);
  2176. return NULL;
  2177. }
  2178. return p;
  2179. }
  2180. BOOL
  2181. EnumFirstFilePattern (
  2182. OUT PFILEPATTERN_ENUM Enum,
  2183. IN PCTSTR Dir,
  2184. IN PCTSTR FilePattern
  2185. )
  2186. {
  2187. TCHAR pattern[MAX_PATH];
  2188. //
  2189. // fail if invalid args are passed in
  2190. // or if [Dir+Backslash] doesn't fit into [Enum->FullPath]
  2191. //
  2192. if (!Dir || !FilePattern || lstrlen (Dir) >= ARRAYSIZE (Enum->FullPath)) {
  2193. SetLastError (ERROR_INVALID_PARAMETER);
  2194. return FALSE;
  2195. }
  2196. if (!BuildPath (pattern, Dir, FilePattern)) {
  2197. return FALSE;
  2198. }
  2199. Enum->Handle = FindFirstFile (pattern, &Enum->FindData);
  2200. if (Enum->Handle == INVALID_HANDLE_VALUE) {
  2201. return FALSE;
  2202. }
  2203. lstrcpy (Enum->FullPath, Dir);
  2204. //
  2205. // set up other members
  2206. //
  2207. Enum->FileName = _tcschr (Enum->FullPath, 0);
  2208. *Enum->FileName++ = TEXT('\\');
  2209. *Enum->FileName = 0;
  2210. if (FAILED (StringCchCopy (
  2211. Enum->FileName,
  2212. ARRAYSIZE (Enum->FullPath) - (Enum->FileName - Enum->FullPath),
  2213. Enum->FindData.cFileName
  2214. ))) {
  2215. //
  2216. // file name too long, skip it
  2217. //
  2218. DebugLog (
  2219. Winnt32LogWarning,
  2220. TEXT("Ignoring object %1\\%2 (name too long)"),
  2221. 0,
  2222. Enum->FullPath,
  2223. Enum->FindData.cFileName
  2224. );
  2225. return EnumNextFilePattern (Enum);
  2226. }
  2227. if (Enum->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  2228. if (!lstrcmp (Enum->FindData.cFileName, TEXT (".")) ||
  2229. !lstrcmp (Enum->FindData.cFileName, TEXT (".."))) {
  2230. return EnumNextFilePattern (Enum);
  2231. }
  2232. }
  2233. return TRUE;
  2234. }
  2235. BOOL
  2236. EnumNextFilePattern (
  2237. IN OUT PFILEPATTERN_ENUM Enum
  2238. )
  2239. {
  2240. while (FindNextFile (Enum->Handle, &Enum->FindData)) {
  2241. if (Enum->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  2242. if (!lstrcmp (Enum->FindData.cFileName, TEXT (".")) ||
  2243. !lstrcmp (Enum->FindData.cFileName, TEXT (".."))) {
  2244. continue;
  2245. }
  2246. }
  2247. if (FAILED (StringCchCopy (
  2248. Enum->FileName,
  2249. ARRAYSIZE (Enum->FullPath) - (Enum->FileName - Enum->FullPath),
  2250. Enum->FindData.cFileName
  2251. ))) {
  2252. //
  2253. // file name too long, skip it
  2254. //
  2255. continue;
  2256. }
  2257. //
  2258. // found a valid object, return it
  2259. //
  2260. return TRUE;
  2261. }
  2262. AbortEnumFilePattern (Enum);
  2263. return FALSE;
  2264. }
  2265. VOID
  2266. AbortEnumFilePattern (
  2267. IN OUT PFILEPATTERN_ENUM Enum
  2268. )
  2269. {
  2270. if (Enum->Handle != INVALID_HANDLE_VALUE) {
  2271. //
  2272. // preserve error code
  2273. //
  2274. DWORD rc = GetLastError ();
  2275. FindClose (Enum->Handle);
  2276. Enum->Handle = INVALID_HANDLE_VALUE;
  2277. SetLastError (rc);
  2278. }
  2279. }
  2280. BOOL
  2281. EnumFirstFilePatternRecursive (
  2282. OUT PFILEPATTERNREC_ENUM Enum,
  2283. IN PCTSTR Dir,
  2284. IN PCTSTR FilePattern,
  2285. IN DWORD ControlFlags
  2286. )
  2287. {
  2288. PFILEENUMLIST dir;
  2289. dir = CreateFileEnumCell (Dir, FilePattern, 0, ENUM_FIRSTFILE);
  2290. if (!dir) {
  2291. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  2292. return FALSE;
  2293. }
  2294. Enum->FilePattern = DupString (FilePattern);
  2295. if (!Enum->FilePattern) {
  2296. DeleteFileEnumCell (dir);
  2297. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  2298. return FALSE;
  2299. }
  2300. Enum->DirCurrent = dir;
  2301. Enum->FindData = &dir->Enum.FindData;
  2302. Enum->RootLen = lstrlen (Dir) + 1;
  2303. Enum->ControlFlags = ControlFlags;
  2304. Enum->Handle = INVALID_HANDLE_VALUE;
  2305. return EnumNextFilePatternRecursive (Enum);
  2306. }
  2307. BOOL
  2308. EnumNextFilePatternRecursive (
  2309. IN OUT PFILEPATTERNREC_ENUM Enum
  2310. )
  2311. {
  2312. TCHAR pattern[MAX_PATH];
  2313. WIN32_FIND_DATA fd;
  2314. PFILEENUMLIST dir;
  2315. while (Enum->DirCurrent) {
  2316. if (Enum->ControlFlags & ECF_ABORT_ENUM_DIR) {
  2317. //
  2318. // caller wants to abort enum of this subdir
  2319. // remove the current node from list
  2320. //
  2321. Enum->ControlFlags &= ~ECF_ABORT_ENUM_DIR;
  2322. dir = Enum->DirCurrent->Next;
  2323. DeleteFileEnumCell (Enum->DirCurrent);
  2324. Enum->DirCurrent = dir;
  2325. if (dir) {
  2326. Enum->FindData = &dir->Enum.FindData;
  2327. }
  2328. continue;
  2329. }
  2330. switch (Enum->DirCurrent->EnumState) {
  2331. case ENUM_FIRSTFILE:
  2332. if (EnumFirstFilePattern (&Enum->DirCurrent->Enum, Enum->DirCurrent->Dir, Enum->FilePattern)) {
  2333. Enum->DirCurrent->EnumState = ENUM_NEXTFILE;
  2334. Enum->FullPath = Enum->DirCurrent->Enum.FullPath;
  2335. Enum->SubPath = Enum->FullPath + Enum->RootLen;
  2336. Enum->FileName = Enum->DirCurrent->Enum.FileName;
  2337. return TRUE;
  2338. }
  2339. Enum->DirCurrent->EnumState = ENUM_SUBDIRS;
  2340. break;
  2341. case ENUM_NEXTFILE:
  2342. if (EnumNextFilePattern (&Enum->DirCurrent->Enum)) {
  2343. Enum->FullPath = Enum->DirCurrent->Enum.FullPath;
  2344. Enum->SubPath = Enum->FullPath + Enum->RootLen;
  2345. Enum->FileName = Enum->DirCurrent->Enum.FileName;
  2346. return TRUE;
  2347. }
  2348. Enum->DirCurrent->EnumState = ENUM_SUBDIRS;
  2349. //
  2350. // fall through
  2351. //
  2352. case ENUM_SUBDIRS:
  2353. if (BuildPath (pattern, Enum->DirCurrent->Dir, TEXT("*"))) {
  2354. Enum->Handle = FindFirstFile (pattern, &fd);
  2355. if (Enum->Handle != INVALID_HANDLE_VALUE) {
  2356. do {
  2357. if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  2358. if (!lstrcmp (fd.cFileName, TEXT (".")) ||
  2359. !lstrcmp (fd.cFileName, TEXT (".."))) {
  2360. continue;
  2361. }
  2362. if (!BuildPath (pattern, Enum->DirCurrent->Dir, fd.cFileName)) {
  2363. //
  2364. // dir name too long
  2365. //
  2366. if (Enum->ControlFlags & ECF_STOP_ON_LONG_PATHS) {
  2367. AbortEnumFilePatternRecursive (Enum);
  2368. //
  2369. // error already set by BuildPath
  2370. //
  2371. return FALSE;
  2372. }
  2373. //
  2374. // just skip it
  2375. //
  2376. DebugLog (
  2377. Winnt32LogWarning,
  2378. TEXT("Ignoring dir %1 (path too long)"),
  2379. 0,
  2380. Enum->DirCurrent->Dir
  2381. );
  2382. continue;
  2383. }
  2384. if (!InsertList (
  2385. (PGENERIC_LIST*)&Enum->DirCurrent,
  2386. (PGENERIC_LIST) CreateFileEnumCell (
  2387. pattern,
  2388. Enum->FilePattern,
  2389. fd.dwFileAttributes,
  2390. Enum->ControlFlags & ECF_ENUM_SUBDIRS ?
  2391. ENUM_SUBDIR : ENUM_FIRSTFILE
  2392. )
  2393. )) {
  2394. AbortEnumFilePatternRecursive (Enum);
  2395. return FALSE;
  2396. }
  2397. }
  2398. } while (FindNextFile (Enum->Handle, &fd));
  2399. FindClose (Enum->Handle);
  2400. Enum->Handle = INVALID_HANDLE_VALUE;
  2401. }
  2402. } else {
  2403. //
  2404. // dir name too long
  2405. //
  2406. if (Enum->ControlFlags & ECF_STOP_ON_LONG_PATHS) {
  2407. AbortEnumFilePatternRecursive (Enum);
  2408. //
  2409. // error already set by BuildPath
  2410. //
  2411. return FALSE;
  2412. }
  2413. //
  2414. // just skip it
  2415. //
  2416. DebugLog (
  2417. Winnt32LogWarning,
  2418. TEXT("Ignoring dir %1 (path too long)"),
  2419. 0,
  2420. Enum->DirCurrent->Dir
  2421. );
  2422. }
  2423. //
  2424. // remove the current node from list
  2425. //
  2426. dir = Enum->DirCurrent->Next;
  2427. DeleteFileEnumCell (Enum->DirCurrent);
  2428. Enum->DirCurrent = dir;
  2429. if (dir) {
  2430. Enum->FindData = &dir->Enum.FindData;
  2431. }
  2432. break;
  2433. case ENUM_SUBDIR:
  2434. Enum->FullPath = Enum->DirCurrent->Dir;
  2435. Enum->SubPath = Enum->FullPath + Enum->RootLen;
  2436. Enum->FileName = _tcsrchr (Enum->FullPath, TEXT('\\')) + 1;
  2437. Enum->DirCurrent->EnumState = ENUM_FIRSTFILE;
  2438. return TRUE;
  2439. }
  2440. }
  2441. return FALSE;
  2442. }
  2443. VOID
  2444. AbortEnumFilePatternRecursive (
  2445. IN OUT PFILEPATTERNREC_ENUM Enum
  2446. )
  2447. {
  2448. //
  2449. // preserve error code
  2450. //
  2451. DWORD rc = GetLastError ();
  2452. if (Enum->DirCurrent) {
  2453. DeleteFileEnumList (Enum->DirCurrent);
  2454. Enum->DirCurrent = NULL;
  2455. }
  2456. if (Enum->Handle != INVALID_HANDLE_VALUE) {
  2457. FindClose (Enum->Handle);
  2458. Enum->Handle = INVALID_HANDLE_VALUE;
  2459. }
  2460. SetLastError (rc);
  2461. }
  2462. BOOL
  2463. CopyTree (
  2464. IN PCTSTR SourceRoot,
  2465. IN PCTSTR DestRoot
  2466. )
  2467. {
  2468. DWORD rc;
  2469. FILEPATTERNREC_ENUM e;
  2470. TCHAR destFile[MAX_PATH];
  2471. PTSTR p;
  2472. BOOL b = TRUE;
  2473. if (EnumFirstFilePatternRecursive (&e, SourceRoot, TEXT("*"), ECF_STOP_ON_LONG_PATHS)) {
  2474. do {
  2475. if (!BuildPath (destFile, DestRoot, e.SubPath)) {
  2476. AbortEnumFilePatternRecursive (&e);
  2477. b = FALSE;
  2478. break;
  2479. }
  2480. p = _tcsrchr (destFile, TEXT('\\'));
  2481. if (!p) {
  2482. continue;
  2483. }
  2484. *p = 0;
  2485. rc = CreateMultiLevelDirectory (destFile);
  2486. if (rc != ERROR_SUCCESS) {
  2487. SetLastError (rc);
  2488. AbortEnumFilePatternRecursive (&e);
  2489. b = FALSE;
  2490. break;
  2491. }
  2492. *p = TEXT('\\');
  2493. SetFileAttributes (destFile, FILE_ATTRIBUTE_NORMAL);
  2494. if (!CopyFile (e.FullPath, destFile, FALSE)) {
  2495. AbortEnumFilePatternRecursive (&e);
  2496. b = FALSE;
  2497. break;
  2498. }
  2499. } while (EnumNextFilePatternRecursive (&e));
  2500. }
  2501. return b;
  2502. }
  2503. PSTRINGLIST
  2504. CreateStringCell (
  2505. IN PCTSTR String
  2506. )
  2507. {
  2508. PSTRINGLIST p = MALLOC (sizeof (STRINGLIST));
  2509. if (p) {
  2510. ZeroMemory (p, sizeof (STRINGLIST));
  2511. if (String) {
  2512. p->String = DupString (String);
  2513. if (!p->String) {
  2514. FREE (p);
  2515. p = NULL;
  2516. }
  2517. } else {
  2518. p->String = NULL;
  2519. }
  2520. }
  2521. return p;
  2522. }
  2523. VOID
  2524. DeleteStringCell (
  2525. IN PSTRINGLIST Cell
  2526. )
  2527. {
  2528. if (Cell) {
  2529. FREE (Cell->String);
  2530. FREE (Cell);
  2531. }
  2532. }
  2533. VOID
  2534. DeleteStringList (
  2535. IN PSTRINGLIST List
  2536. )
  2537. {
  2538. PSTRINGLIST p, q;
  2539. for (p = List; p; p = q) {
  2540. q = p->Next;
  2541. DeleteStringCell (p);
  2542. }
  2543. }
  2544. BOOL
  2545. FindStringCell (
  2546. IN PSTRINGLIST StringList,
  2547. IN PCTSTR String,
  2548. IN BOOL CaseSensitive
  2549. )
  2550. {
  2551. PSTRINGLIST p;
  2552. INT i;
  2553. if (!StringList || !String) {
  2554. return FALSE;
  2555. }
  2556. for (p = StringList; p; p = p->Next) {
  2557. i = CaseSensitive ? _tcscmp (String, p->String) : _tcsicmp (String, p->String);
  2558. if (i == 0) {
  2559. return TRUE;
  2560. }
  2561. }
  2562. return FALSE;
  2563. }
  2564. PFILEENUMLIST
  2565. CreateFileEnumCell (
  2566. IN PCTSTR Dir,
  2567. IN PCTSTR FilePattern,
  2568. IN DWORD Attributes,
  2569. IN DWORD EnumState
  2570. )
  2571. {
  2572. PFILEENUMLIST p = MALLOC (sizeof (FILEENUMLIST));
  2573. if (p) {
  2574. ZeroMemory (p, sizeof (FILEENUMLIST));
  2575. p->Enum.FindData.dwFileAttributes = Attributes;
  2576. p->EnumState = EnumState;
  2577. p->Enum.Handle = INVALID_HANDLE_VALUE;
  2578. p->Dir = DupString (Dir);
  2579. if (!p->Dir) {
  2580. FREE (p);
  2581. p = NULL;
  2582. }
  2583. }
  2584. return p;
  2585. }
  2586. VOID
  2587. DeleteFileEnumCell (
  2588. IN PFILEENUMLIST Cell
  2589. )
  2590. {
  2591. if (Cell) {
  2592. FREE (Cell->Dir);
  2593. AbortEnumFilePattern (&Cell->Enum);
  2594. FREE (Cell);
  2595. }
  2596. }
  2597. BOOL
  2598. InsertList (
  2599. IN OUT PGENERIC_LIST* List,
  2600. IN PGENERIC_LIST NewList
  2601. )
  2602. {
  2603. PGENERIC_LIST p;
  2604. if (!NewList) {
  2605. return FALSE;
  2606. }
  2607. if (*List) {
  2608. for (p = *List; p->Next; p = p->Next) ;
  2609. p->Next = NewList;
  2610. } else {
  2611. *List = NewList;
  2612. }
  2613. return TRUE;
  2614. }
  2615. VOID
  2616. DeleteFileEnumList (
  2617. IN PFILEENUMLIST NewList
  2618. )
  2619. {
  2620. PFILEENUMLIST p, q;
  2621. for (p = NewList; p; p = q) {
  2622. q = p->Next;
  2623. DeleteFileEnumCell (p);
  2624. }
  2625. }
  2626. PCTSTR
  2627. FindSubString (
  2628. IN PCTSTR String,
  2629. IN TCHAR Separator,
  2630. IN PCTSTR SubStr,
  2631. IN BOOL CaseSensitive
  2632. )
  2633. {
  2634. SIZE_T len1, len2;
  2635. PCTSTR end;
  2636. MYASSERT (Separator);
  2637. MYASSERT (!_istleadbyte (Separator));
  2638. MYASSERT (SubStr);
  2639. MYASSERT (!_tcschr (SubStr, Separator));
  2640. len1 = lstrlen (SubStr);
  2641. MYASSERT (SubStr[len1] == 0);
  2642. while (String) {
  2643. end = _tcschr (String, Separator);
  2644. if (end) {
  2645. len2 = end - String;
  2646. } else {
  2647. len2 = lstrlen (String);
  2648. }
  2649. if ((len1 == len2) &&
  2650. (CaseSensitive ?
  2651. !_tcsncmp (String, SubStr, len1) :
  2652. !_tcsnicmp (String, SubStr, len1)
  2653. )) {
  2654. break;
  2655. }
  2656. if (end) {
  2657. String = end + 1;
  2658. } else {
  2659. String = NULL;
  2660. }
  2661. }
  2662. return String;
  2663. }
  2664. VOID
  2665. GetCurrentWinnt32RegKey (
  2666. OUT PTSTR Key,
  2667. IN INT Chars
  2668. )
  2669. {
  2670. INT i = _sntprintf (
  2671. Key,
  2672. Chars,
  2673. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\Winnt32\\%u.%u"),
  2674. VER_PRODUCTMAJORVERSION,
  2675. VER_PRODUCTMINORVERSION
  2676. );
  2677. MYASSERT (i > 0);
  2678. }
  2679. BOOL
  2680. GetFileVersionEx (
  2681. IN PCTSTR FilePath,
  2682. OUT PTSTR FileVersion,
  2683. IN INT CchFileVersion
  2684. )
  2685. {
  2686. DWORD dwLength, dwTemp;
  2687. LPVOID lpData;
  2688. VS_FIXEDFILEINFO *VsInfo;
  2689. UINT DataLength;
  2690. BOOL b = FALSE;
  2691. if (FileExists (FilePath, NULL)) {
  2692. if (dwLength = GetFileVersionInfoSize ((PTSTR)FilePath, &dwTemp)) {
  2693. if (lpData = LocalAlloc (LPTR, dwLength)) {
  2694. if (GetFileVersionInfo ((PTSTR)FilePath, 0, dwLength, lpData)) {
  2695. if (VerQueryValue (lpData, TEXT("\\"), &VsInfo, &DataLength)) {
  2696. if (SUCCEEDED(StringCchPrintf (
  2697. FileVersion,
  2698. CchFileVersion,
  2699. TEXT("%u.%u.%u.%u"),
  2700. (UINT)HIWORD(VsInfo->dwFileVersionMS),
  2701. (UINT)LOWORD(VsInfo->dwFileVersionMS),
  2702. (UINT)HIWORD(VsInfo->dwFileVersionLS),
  2703. (UINT)LOWORD(VsInfo->dwFileVersionLS)
  2704. ))) {
  2705. b = TRUE;
  2706. } else {
  2707. //
  2708. // why is the buffer so small?
  2709. //
  2710. MYASSERT (FALSE);
  2711. }
  2712. }
  2713. }
  2714. LocalFree (lpData);
  2715. }
  2716. }
  2717. }
  2718. return b;
  2719. }
  2720. BOOL
  2721. IsFileVersionLesser (
  2722. IN PCTSTR FileToCompare,
  2723. IN PCTSTR FileToCompareWith
  2724. )
  2725. {
  2726. TCHAR version[20];
  2727. if (GetFileVersion (FileToCompareWith, version) && CheckForFileVersion (FileToCompare, version)) {
  2728. DebugLog (
  2729. Winnt32LogInformation,
  2730. TEXT("File %1 has a smaller version (%2) than %3"),
  2731. 0,
  2732. FileToCompare,
  2733. version,
  2734. FileToCompareWith
  2735. );
  2736. return TRUE;
  2737. }
  2738. return FALSE;
  2739. }
  2740. BOOL
  2741. FindPathToInstallationFileEx (
  2742. IN PCTSTR FileName,
  2743. OUT PTSTR PathToFile,
  2744. IN INT PathToFileBufferSize,
  2745. OUT PBOOL Compressed OPTIONAL
  2746. )
  2747. {
  2748. DWORD i;
  2749. DWORD attr;
  2750. BOOL b;
  2751. HANDLE h;
  2752. WIN32_FIND_DATA fd;
  2753. PTSTR p, q;
  2754. if (!FileName || !*FileName) {
  2755. return FALSE;
  2756. }
  2757. //
  2758. // Search for installation files in this order:
  2759. // 1. AlternateSourcePath (specified on the cmd line with /M:Path)
  2760. // 2. Setup Update files (downloaded from the web)
  2761. // 3. NativeSourcePath(s)
  2762. // 4. SourcePath(s)
  2763. //
  2764. if (AlternateSourcePath[0]) {
  2765. if (BuildPathEx (PathToFile, PathToFileBufferSize, AlternateSourcePath, FileName)) {
  2766. attr = GetFileAttributes (PathToFile);
  2767. if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  2768. return TRUE;
  2769. }
  2770. }
  2771. }
  2772. if (g_DynUpdtStatus->UpdatesPath[0]) {
  2773. if (BuildPathEx (PathToFile, PathToFileBufferSize, g_DynUpdtStatus->UpdatesPath, FileName)) {
  2774. attr = GetFileAttributes (PathToFile);
  2775. if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  2776. return TRUE;
  2777. }
  2778. }
  2779. }
  2780. for (i = 0; i < SourceCount; i++) {
  2781. if (!BuildPathEx (PathToFile, PathToFileBufferSize, NativeSourcePaths[i], FileName)) {
  2782. continue;
  2783. }
  2784. p = CharPrev (PathToFile, _tcschr (PathToFile, 0));
  2785. *p = TEXT('?');
  2786. b = FALSE;
  2787. h = FindFirstFile (PathToFile, &fd);
  2788. if (h != INVALID_HANDLE_VALUE) {
  2789. if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  2790. q = CharPrev (fd.cFileName, _tcschr (fd.cFileName, 0));
  2791. *p = *q;
  2792. if (Compressed) {
  2793. *Compressed = (*q == TEXT('_'));
  2794. }
  2795. b = TRUE;
  2796. }
  2797. FindClose (h);
  2798. }
  2799. if (b) {
  2800. return TRUE;
  2801. }
  2802. }
  2803. for (i = 0; i < SourceCount; i++) {
  2804. if (!BuildPathEx (PathToFile, PathToFileBufferSize, SourcePaths[i], FileName)) {
  2805. continue;
  2806. }
  2807. p = CharPrev (PathToFile, _tcschr (PathToFile, 0));
  2808. *p = TEXT('?');
  2809. b = FALSE;
  2810. h = FindFirstFile (PathToFile, &fd);
  2811. if (h != INVALID_HANDLE_VALUE) {
  2812. if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  2813. q = CharPrev (fd.cFileName, _tcschr (fd.cFileName, 0));
  2814. *p = *q;
  2815. if (Compressed) {
  2816. *Compressed = (*q == TEXT('_'));
  2817. }
  2818. b = TRUE;
  2819. }
  2820. FindClose (h);
  2821. }
  2822. if (b) {
  2823. return TRUE;
  2824. }
  2825. }
  2826. return FALSE;
  2827. }
  2828. BOOL
  2829. FindPathToWinnt32File (
  2830. IN PCTSTR FileRelativePath,
  2831. OUT PTSTR PathToFile,
  2832. IN INT PathToFileBufferSize
  2833. )
  2834. {
  2835. DWORD i;
  2836. DWORD attr;
  2837. TCHAR cdFilePath[MAX_PATH];
  2838. PTSTR p;
  2839. if (!FileRelativePath || !*FileRelativePath || PathToFileBufferSize <= 0) {
  2840. return FALSE;
  2841. }
  2842. if (FileRelativePath[1] == TEXT(':') && FileRelativePath[2] == TEXT('\\') ||
  2843. FileRelativePath[0] == TEXT('\\') && FileRelativePath[1] == TEXT('\\')) {
  2844. //
  2845. // assume either a DOS or a UNC full path was supplied
  2846. //
  2847. attr = GetFileAttributes (FileRelativePath);
  2848. if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  2849. if (lstrlen (FileRelativePath) >= PathToFileBufferSize) {
  2850. return FALSE;
  2851. }
  2852. lstrcpy (PathToFile, FileRelativePath);
  2853. return TRUE;
  2854. }
  2855. }
  2856. if (!MyGetModuleFileName (NULL, cdFilePath, ARRAYSIZE(cdFilePath)) ||
  2857. !(p = _tcsrchr (cdFilePath, TEXT('\\')))) {
  2858. return FALSE;
  2859. }
  2860. //
  2861. // hop over the backslash
  2862. //
  2863. p++;
  2864. if (FAILED (StringCchCopy (p, cdFilePath + ARRAYSIZE(cdFilePath) - p, FileRelativePath))) {
  2865. cdFilePath[0] = 0;
  2866. }
  2867. //
  2868. // Search for winnt32 files in this order:
  2869. // 1. AlternateSourcePath (specified on the cmd line with /M:Path
  2870. // 2. Setup Update files (downloaded from the web)
  2871. // 3. NativeSourcePath(s)
  2872. // 4. SourcePath(s)
  2873. //
  2874. if (AlternateSourcePath[0]) {
  2875. if (BuildPathEx (PathToFile, PathToFileBufferSize, AlternateSourcePath, FileRelativePath)) {
  2876. attr = GetFileAttributes (PathToFile);
  2877. if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  2878. return TRUE;
  2879. }
  2880. p = _tcsrchr (PathToFile, TEXT('\\'));
  2881. if (p) {
  2882. //
  2883. // try the root of /M too, for backwards compatibility with W2K
  2884. //
  2885. if (BuildPathEx (PathToFile, PathToFileBufferSize, AlternateSourcePath, p + 1)) {
  2886. attr = GetFileAttributes (PathToFile);
  2887. if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  2888. return TRUE;
  2889. }
  2890. }
  2891. }
  2892. }
  2893. }
  2894. if (g_DynUpdtStatus && g_DynUpdtStatus->Winnt32Path[0]) {
  2895. if (BuildPathEx (PathToFile, PathToFileBufferSize, g_DynUpdtStatus->Winnt32Path, FileRelativePath)) {
  2896. attr = GetFileAttributes (PathToFile);
  2897. if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  2898. //
  2899. // check file version relative to the CD version
  2900. //
  2901. if (!IsFileVersionLesser (PathToFile, cdFilePath)) {
  2902. return TRUE;
  2903. }
  2904. }
  2905. }
  2906. }
  2907. #ifndef UNICODE
  2908. //
  2909. // on Win9x systems, first check if the file was downloaded in %windir%\winnt32
  2910. // load it from there if it's present
  2911. //
  2912. if (g_LocalSourcePath) {
  2913. if (BuildPathEx (PathToFile, PathToFileBufferSize, g_LocalSourcePath, FileRelativePath)) {
  2914. attr = GetFileAttributes (PathToFile);
  2915. if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  2916. return TRUE;
  2917. }
  2918. }
  2919. }
  2920. #endif
  2921. for (i = 0; i < SourceCount; i++) {
  2922. if (BuildPathEx (PathToFile, PathToFileBufferSize, NativeSourcePaths[i], FileRelativePath)) {
  2923. attr = GetFileAttributes (PathToFile);
  2924. if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  2925. return TRUE;
  2926. }
  2927. }
  2928. }
  2929. for (i = 0; i < SourceCount; i++) {
  2930. if (BuildPathEx (PathToFile, PathToFileBufferSize, SourcePaths[i], FileRelativePath)) {
  2931. attr = GetFileAttributes (PathToFile);
  2932. if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  2933. return TRUE;
  2934. }
  2935. }
  2936. }
  2937. attr = GetFileAttributes (cdFilePath);
  2938. if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
  2939. if (SUCCEEDED (StringCchCopy (PathToFile, PathToFileBufferSize, cdFilePath))) {
  2940. return TRUE;
  2941. }
  2942. }
  2943. PathToFile[0] = 0;
  2944. return FALSE;
  2945. }
  2946. BOOL
  2947. CreateDir (
  2948. IN PCTSTR DirName
  2949. )
  2950. {
  2951. return CreateDirectory (DirName, NULL) || GetLastError () == ERROR_ALREADY_EXISTS;
  2952. }
  2953. BOOL
  2954. GetLinkDate (
  2955. IN PCTSTR FilePath,
  2956. OUT PDWORD LinkDate
  2957. )
  2958. {
  2959. HANDLE hFile;
  2960. HANDLE hFileMapping;
  2961. PVOID pFileBase;
  2962. DWORD fileSize;
  2963. PIMAGE_DOS_HEADER dosHeader;
  2964. PIMAGE_NT_HEADERS pNtHeaders;
  2965. DWORD rc;
  2966. rc = MapFileForRead (FilePath, &fileSize, &hFile, &hFileMapping, &pFileBase);
  2967. if (rc != ERROR_SUCCESS) {
  2968. SetLastError (rc);
  2969. return FALSE;
  2970. }
  2971. __try {
  2972. if (fileSize < sizeof (IMAGE_DOS_HEADER)) {
  2973. rc = ERROR_BAD_FORMAT;
  2974. __leave;
  2975. }
  2976. dosHeader = (PIMAGE_DOS_HEADER)pFileBase;
  2977. if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
  2978. rc = ERROR_BAD_FORMAT;
  2979. __leave;
  2980. }
  2981. if ((DWORD)dosHeader->e_lfanew + sizeof (IMAGE_NT_HEADERS) > fileSize) {
  2982. rc = ERROR_BAD_FORMAT;
  2983. __leave;
  2984. }
  2985. pNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)pFileBase + dosHeader->e_lfanew);
  2986. if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
  2987. rc = ERROR_BAD_FORMAT;
  2988. __leave;
  2989. }
  2990. *LinkDate = pNtHeaders->FileHeader.TimeDateStamp;
  2991. rc = ERROR_SUCCESS;
  2992. } __finally {
  2993. UnmapFile (hFileMapping, pFileBase);
  2994. CloseHandle (hFile);
  2995. SetLastError (rc);
  2996. }
  2997. return rc == ERROR_SUCCESS;
  2998. }
  2999. BOOL
  3000. CheckForFileVersionEx (
  3001. LPCTSTR FileName,
  3002. LPCTSTR FileVer, OPTIONAL
  3003. LPCTSTR BinProductVer, OPTIONAL
  3004. LPCTSTR LinkDate OPTIONAL
  3005. )
  3006. /*
  3007. Arguments -
  3008. FileName - Full path to the file to check
  3009. Filever - Version value to check against of the for x.x.x.x
  3010. BinProductVer - Version value to check against of the for x.x.x.x
  3011. LinkDate - Link date of executable
  3012. Function will check the actual file against the fields specified. The depth of the check
  3013. is as deep as specified in "x.x.x.x" i..e if FileVer = 3.5.1 and actual version on the file
  3014. is 3.5.1.4 we only compare upto 3.5.1.
  3015. Return values -
  3016. TRUE - If the version of the file is <= FileVer which means that the file is an incompatible one
  3017. else we return FALSE
  3018. */
  3019. {
  3020. TCHAR Buffer[MAX_PATH];
  3021. TCHAR temp[MAX_PATH];
  3022. DWORD dwLength, dwTemp;
  3023. UINT DataLength;
  3024. LPVOID lpData;
  3025. VS_FIXEDFILEINFO *VsInfo;
  3026. LPTSTR s,e;
  3027. DWORD Vers[5],File_Vers[5];//MajVer, MinVer;
  3028. INT i, Depth;
  3029. BOOL bEqual, bError = FALSE;
  3030. DWORD linkDate, fileLinkDate;
  3031. BOOL bIncompatible;
  3032. BOOL bIncompFileVer, bIncompBinProdVer;
  3033. if (!ExpandEnvironmentStrings( FileName, Buffer, ARRAYSIZE(Buffer))) {
  3034. return FALSE;
  3035. }
  3036. if(!FileExists(Buffer, NULL)) {
  3037. return FALSE;
  3038. }
  3039. bIncompatible = FALSE;
  3040. if(FileVer && *FileVer || BinProductVer && *BinProductVer) {
  3041. //
  3042. // we need to read the version info
  3043. //
  3044. if(dwLength = GetFileVersionInfoSize( Buffer, &dwTemp )) {
  3045. if(lpData = LocalAlloc( LPTR, dwLength )) {
  3046. if(GetFileVersionInfo( Buffer, 0, dwLength, lpData )) {
  3047. if (VerQueryValue( lpData, TEXT("\\"), &VsInfo, &DataLength )) {
  3048. if (FileVer && *FileVer) {
  3049. File_Vers[0] = (HIWORD(VsInfo->dwFileVersionMS));
  3050. File_Vers[1] = (LOWORD(VsInfo->dwFileVersionMS));
  3051. File_Vers[2] = (HIWORD(VsInfo->dwFileVersionLS));
  3052. File_Vers[3] = (LOWORD(VsInfo->dwFileVersionLS));
  3053. if (FAILED (StringCchCopy (temp, ARRAYSIZE(temp), FileVer))) {
  3054. MYASSERT(FALSE);
  3055. }
  3056. //
  3057. //Parse and get the depth of versioning we look for
  3058. //
  3059. s = e = temp;
  3060. bEqual = FALSE;
  3061. i = 0;
  3062. if (*e == TEXT('=')) {
  3063. bEqual = TRUE;
  3064. e++;
  3065. s++;
  3066. }
  3067. while (e) {
  3068. if (((*e < TEXT('0')) || (*e > TEXT('9'))) &&
  3069. (*e != TEXT('.')) &&
  3070. (*e != TEXT('\0'))
  3071. ) {
  3072. MYASSERT (FALSE);
  3073. bError = TRUE;
  3074. break;
  3075. }
  3076. if (*e == TEXT('\0')) {
  3077. *e = 0;
  3078. Vers[i] = _ttoi(s);
  3079. break;
  3080. }
  3081. if (*e == TEXT('.')) {
  3082. *e = 0;
  3083. Vers[i++] = _ttoi(s);
  3084. s = e+1;
  3085. }
  3086. e++;
  3087. }// while
  3088. if (!bError) {
  3089. Depth = i + 1;
  3090. if (Depth > 4) {
  3091. Depth = 4;
  3092. }
  3093. for (i = 0; i < Depth; i++) {
  3094. if (File_Vers[i] == Vers[i]) {
  3095. continue;
  3096. }
  3097. if (bEqual) {
  3098. break;
  3099. }
  3100. if (File_Vers[i] > Vers[i]) {
  3101. break;
  3102. }
  3103. bIncompatible = TRUE;
  3104. break;
  3105. }
  3106. if (i == Depth) {
  3107. //
  3108. // everything matched - the file is incompatible
  3109. //
  3110. bIncompatible = TRUE;
  3111. }
  3112. }
  3113. } else {
  3114. bIncompatible = TRUE;
  3115. }
  3116. if (!bError && bIncompatible && BinProductVer && *BinProductVer) {
  3117. //
  3118. // reset status
  3119. //
  3120. bIncompatible = FALSE;
  3121. File_Vers[0] = (HIWORD(VsInfo->dwProductVersionMS));
  3122. File_Vers[1] = (LOWORD(VsInfo->dwProductVersionMS));
  3123. File_Vers[2] = (HIWORD(VsInfo->dwProductVersionLS));
  3124. File_Vers[3] = (LOWORD(VsInfo->dwProductVersionLS));
  3125. if (FAILED (StringCchCopy (temp, ARRAYSIZE(temp), BinProductVer))) {
  3126. MYASSERT(FALSE);
  3127. }
  3128. //
  3129. //Parse and get the depth of versioning we look for
  3130. //
  3131. s = e = temp;
  3132. bEqual = FALSE;
  3133. i = 0;
  3134. if (*e == TEXT('=')) {
  3135. bEqual = TRUE;
  3136. e++;
  3137. s++;
  3138. }
  3139. while (e) {
  3140. if (((*e < TEXT('0')) || (*e > TEXT('9'))) &&
  3141. (*e != TEXT('.')) &&
  3142. (*e != TEXT('\0'))
  3143. ) {
  3144. MYASSERT (FALSE);
  3145. bError = TRUE;
  3146. break;
  3147. }
  3148. if (*e == TEXT('\0')) {
  3149. *e = 0;
  3150. Vers[i] = _ttoi(s);
  3151. break;
  3152. }
  3153. if (*e == TEXT('.')) {
  3154. *e = 0;
  3155. Vers[i++] = _ttoi(s);
  3156. s = e+1;
  3157. }
  3158. e++;
  3159. }// while
  3160. if (!bError) {
  3161. Depth = i + 1;
  3162. if (Depth > 4) {
  3163. Depth = 4;
  3164. }
  3165. for (i = 0; i < Depth; i++) {
  3166. if (File_Vers[i] == Vers[i]) {
  3167. continue;
  3168. }
  3169. if (bEqual) {
  3170. break;
  3171. }
  3172. if (File_Vers[i] > Vers[i]) {
  3173. break;
  3174. }
  3175. bIncompatible = TRUE;
  3176. break;
  3177. }
  3178. if (i == Depth) {
  3179. //
  3180. // everything matched - the file is incompatible
  3181. //
  3182. bIncompatible = TRUE;
  3183. }
  3184. }
  3185. }
  3186. }
  3187. }
  3188. LocalFree( lpData );
  3189. }
  3190. }
  3191. } else {
  3192. bIncompatible = TRUE;
  3193. }
  3194. if (!bError && bIncompatible && LinkDate && *LinkDate) {
  3195. bEqual = FALSE;
  3196. if (*LinkDate == TEXT('=')) {
  3197. LinkDate++;
  3198. bEqual = TRUE;
  3199. }
  3200. bIncompatible = FALSE;
  3201. if (StringToInt (LinkDate, &linkDate)) {
  3202. if (GetLinkDate (Buffer, &fileLinkDate)) {
  3203. if (fileLinkDate == linkDate ||
  3204. !bEqual && fileLinkDate < linkDate
  3205. ) {
  3206. bIncompatible = TRUE;
  3207. }
  3208. }
  3209. }
  3210. }
  3211. if (bError) {
  3212. bIncompatible = FALSE;
  3213. }
  3214. return bIncompatible;
  3215. }
  3216. BOOL
  3217. StringToInt (
  3218. IN PCTSTR Field,
  3219. OUT PINT IntegerValue
  3220. )
  3221. /*++
  3222. Routine Description:
  3223. Arguments:
  3224. Return Value:
  3225. Remarks:
  3226. Hexadecimal numbers are also supported. They must be prefixed by '0x' or '0X', with no
  3227. space allowed between the prefix and the number.
  3228. --*/
  3229. {
  3230. INT Value;
  3231. UINT c;
  3232. BOOL Neg;
  3233. UINT Base;
  3234. UINT NextDigitValue;
  3235. INT OverflowCheck;
  3236. BOOL b;
  3237. if(!Field) {
  3238. SetLastError(ERROR_INVALID_PARAMETER);
  3239. return(FALSE);
  3240. }
  3241. if(*Field == TEXT('-')) {
  3242. Neg = TRUE;
  3243. Field++;
  3244. } else {
  3245. Neg = FALSE;
  3246. if(*Field == TEXT('+')) {
  3247. Field++;
  3248. }
  3249. }
  3250. if((*Field == TEXT('0')) &&
  3251. ((*(Field+1) == TEXT('x')) || (*(Field+1) == TEXT('X')))) {
  3252. //
  3253. // The number is in hexadecimal.
  3254. //
  3255. Base = 16;
  3256. Field += 2;
  3257. } else {
  3258. //
  3259. // The number is in decimal.
  3260. //
  3261. Base = 10;
  3262. }
  3263. for(OverflowCheck = Value = 0; *Field; Field++) {
  3264. c = (UINT)*Field;
  3265. if((c >= (UINT)'0') && (c <= (UINT)'9')) {
  3266. NextDigitValue = c - (UINT)'0';
  3267. } else if(Base == 16) {
  3268. if((c >= (UINT)'a') && (c <= (UINT)'f')) {
  3269. NextDigitValue = (c - (UINT)'a') + 10;
  3270. } else if ((c >= (UINT)'A') && (c <= (UINT)'F')) {
  3271. NextDigitValue = (c - (UINT)'A') + 10;
  3272. } else {
  3273. break;
  3274. }
  3275. } else {
  3276. break;
  3277. }
  3278. Value *= Base;
  3279. Value += NextDigitValue;
  3280. //
  3281. // Check for overflow. For decimal numbers, we check to see whether the
  3282. // new value has overflowed into the sign bit (i.e., is less than the
  3283. // previous value. For hexadecimal numbers, we check to make sure we
  3284. // haven't gotten more digits than will fit in a DWORD.
  3285. //
  3286. if(Base == 16) {
  3287. if(++OverflowCheck > (sizeof(INT) * 2)) {
  3288. break;
  3289. }
  3290. } else {
  3291. if(Value < OverflowCheck) {
  3292. break;
  3293. } else {
  3294. OverflowCheck = Value;
  3295. }
  3296. }
  3297. }
  3298. if(*Field) {
  3299. SetLastError(ERROR_INVALID_DATA);
  3300. return(FALSE);
  3301. }
  3302. if(Neg) {
  3303. Value = 0-Value;
  3304. }
  3305. b = TRUE;
  3306. try {
  3307. *IntegerValue = Value;
  3308. } except(EXCEPTION_EXECUTE_HANDLER) {
  3309. b = FALSE;
  3310. }
  3311. if(!b) {
  3312. SetLastError(ERROR_INVALID_PARAMETER);
  3313. }
  3314. return(b);
  3315. }
  3316. BOOLEAN
  3317. CheckForFileVersion (
  3318. LPCTSTR FileName,
  3319. LPCTSTR FileVer
  3320. )
  3321. {
  3322. return (BOOLEAN)CheckForFileVersionEx (FileName, FileVer, NULL, NULL);
  3323. }
  3324. VOID
  3325. FixMissingKnownDlls (
  3326. OUT PSTRINGLIST* MissingKnownDlls,
  3327. IN PCTSTR RestrictedCheckList OPTIONAL
  3328. )
  3329. {
  3330. PCTSTR regStr;
  3331. HKEY key;
  3332. DWORD rc;
  3333. DWORD index;
  3334. TCHAR dllValue[MAX_PATH];
  3335. TCHAR dllName[MAX_PATH];
  3336. DWORD type;
  3337. DWORD size1;
  3338. DWORD size2;
  3339. TCHAR systemDir[MAX_PATH];
  3340. TCHAR dllPath[MAX_PATH];
  3341. BOOL bCheck;
  3342. if (!GetSystemDirectory (systemDir, ARRAYSIZE(systemDir))) {
  3343. return;
  3344. }
  3345. #ifdef UNICODE
  3346. regStr = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs";
  3347. #else
  3348. regStr = "SYSTEM\\CurrentControlSet\\Control\\SessionManager\\KnownDLLs";
  3349. #endif
  3350. rc = RegOpenKey (HKEY_LOCAL_MACHINE, regStr, &key);
  3351. if (rc == ERROR_SUCCESS) {
  3352. index = 0;
  3353. size1 = ARRAYSIZE(dllValue);
  3354. size2 = ARRAYSIZE(dllName);
  3355. while (RegEnumValue (
  3356. key,
  3357. index++,
  3358. dllValue,
  3359. &size1,
  3360. NULL,
  3361. &type,
  3362. (LPBYTE)dllName,
  3363. &size2
  3364. ) == ERROR_SUCCESS) {
  3365. if (type == REG_SZ) {
  3366. bCheck = TRUE;
  3367. if (RestrictedCheckList) {
  3368. PCTSTR fileName = RestrictedCheckList;
  3369. while (*fileName) {
  3370. if (!lstrcmpi (fileName, dllName)) {
  3371. break;
  3372. }
  3373. fileName = _tcschr (fileName, 0) + 1;
  3374. }
  3375. if (*fileName == 0) {
  3376. //
  3377. // we are not interested in this dll
  3378. //
  3379. bCheck = FALSE;
  3380. }
  3381. }
  3382. if (bCheck) {
  3383. if (!BuildPath (dllPath, systemDir, dllName) ||
  3384. !FileExists (dllPath, NULL)) {
  3385. DebugLog (
  3386. Winnt32LogWarning,
  3387. TEXT("The KnownDll %1\\%2 has a name too long or it doesn't exist"),
  3388. 0,
  3389. systemDir,
  3390. dllName
  3391. );
  3392. //
  3393. // OK, we found a bogus reg entry; remove the value and remember the data
  3394. //
  3395. if (RegDeleteValue (key, dllValue) == ERROR_SUCCESS) {
  3396. InsertList (
  3397. (PGENERIC_LIST*)MissingKnownDlls,
  3398. (PGENERIC_LIST)CreateStringCell (dllValue)
  3399. );
  3400. InsertList (
  3401. (PGENERIC_LIST*)MissingKnownDlls,
  3402. (PGENERIC_LIST)CreateStringCell (dllName)
  3403. );
  3404. }
  3405. }
  3406. }
  3407. }
  3408. size1 = ARRAYSIZE(dllValue);
  3409. size2 = ARRAYSIZE(dllName);
  3410. }
  3411. RegCloseKey (key);
  3412. }
  3413. }
  3414. VOID
  3415. UndoFixMissingKnownDlls (
  3416. IN PSTRINGLIST MissingKnownDlls
  3417. )
  3418. {
  3419. PCTSTR regStr;
  3420. HKEY key;
  3421. DWORD rc;
  3422. PSTRINGLIST p, q;
  3423. #ifdef UNICODE
  3424. regStr = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs";
  3425. #else
  3426. regStr = "SYSTEM\\CurrentControlSet\\Control\\SessionManager\\KnownDLLs";
  3427. #endif
  3428. rc = RegOpenKey (HKEY_LOCAL_MACHINE, regStr, &key);
  3429. if (rc == ERROR_SUCCESS) {
  3430. p = MissingKnownDlls;
  3431. while (p) {
  3432. q = p->Next;
  3433. if (q) {
  3434. RegSetValueEx (
  3435. key,
  3436. p->String,
  3437. 0,
  3438. REG_SZ,
  3439. (const PBYTE)q->String,
  3440. (lstrlen (q->String) + 1) * sizeof (TCHAR)
  3441. );
  3442. p = q->Next;
  3443. } else {
  3444. p = NULL;
  3445. }
  3446. }
  3447. RegCloseKey (key);
  3448. }
  3449. DeleteStringList (MissingKnownDlls);
  3450. }
  3451. #ifndef UNICODE
  3452. /*++
  3453. Routine Description:
  3454. IsPatternMatch compares a string against a pattern that may contain
  3455. standard * or ? wildcards.
  3456. Arguments:
  3457. wstrPattern - A pattern possibly containing wildcards
  3458. wstrStr - The string to compare against the pattern
  3459. Return Value:
  3460. TRUE when wstrStr and wstrPattern match when wildcards are expanded.
  3461. FALSE if wstrStr does not match wstrPattern.
  3462. --*/
  3463. #define MBCHAR INT
  3464. BOOL
  3465. IsPatternMatchA (
  3466. IN PCSTR strPattern,
  3467. IN PCSTR strStr
  3468. )
  3469. {
  3470. MBCHAR chSrc, chPat;
  3471. while (*strStr) {
  3472. chSrc = _mbctolower ((MBCHAR) _mbsnextc (strStr));
  3473. chPat = _mbctolower ((MBCHAR) _mbsnextc (strPattern));
  3474. if (chPat == '*') {
  3475. // Skip all asterisks that are grouped together
  3476. while (_mbsnextc (_mbsinc (strPattern)) == '*') {
  3477. strStr = _mbsinc (strPattern);
  3478. }
  3479. // Check if asterisk is at the end. If so, we have a match already.
  3480. if (!_mbsnextc (_mbsinc (strPattern))) {
  3481. return TRUE;
  3482. }
  3483. // do recursive check for rest of pattern
  3484. if (IsPatternMatchA (_mbsinc (strPattern), strStr)) {
  3485. return TRUE;
  3486. }
  3487. // Allow any character and continue
  3488. strStr = _mbsinc (strStr);
  3489. continue;
  3490. }
  3491. if (chPat != '?') {
  3492. if (chSrc != chPat) {
  3493. return FALSE;
  3494. }
  3495. }
  3496. strStr = _mbsinc (strStr);
  3497. strPattern = _mbsinc (strPattern);
  3498. }
  3499. //
  3500. // Fail when there is more pattern and pattern does not end in an asterisk
  3501. //
  3502. while (_mbsnextc (strPattern) == '*') {
  3503. strPattern = _mbsinc (strPattern);
  3504. }
  3505. if (_mbsnextc (strPattern)) {
  3506. return FALSE;
  3507. }
  3508. return TRUE;
  3509. }
  3510. #endif
  3511. // Wierd logic here required to make builds work, as this is defined
  3512. // in another file that gets linked in on x86
  3513. #ifdef _WIN64
  3514. BOOL
  3515. IsPatternMatchW (
  3516. IN PCWSTR wstrPattern,
  3517. IN PCWSTR wstrStr
  3518. )
  3519. {
  3520. WCHAR chSrc, chPat;
  3521. while (*wstrStr) {
  3522. chSrc = towlower (*wstrStr);
  3523. chPat = towlower (*wstrPattern);
  3524. if (chPat == L'*') {
  3525. // Skip all asterisks that are grouped together
  3526. while (wstrPattern[1] == L'*')
  3527. wstrPattern++;
  3528. // Check if asterisk is at the end. If so, we have a match already.
  3529. chPat = towlower (wstrPattern[1]);
  3530. if (!chPat)
  3531. return TRUE;
  3532. // Otherwise check if next pattern char matches current char
  3533. if (chPat == chSrc || chPat == L'?') {
  3534. // do recursive check for rest of pattern
  3535. wstrPattern++;
  3536. if (IsPatternMatchW (wstrPattern, wstrStr))
  3537. return TRUE;
  3538. // no, that didn't work, stick with star
  3539. wstrPattern--;
  3540. }
  3541. //
  3542. // Allow any character and continue
  3543. //
  3544. wstrStr++;
  3545. continue;
  3546. }
  3547. if (chPat != L'?') {
  3548. //
  3549. // if next pattern character is not a question mark, src and pat
  3550. // must be identical.
  3551. //
  3552. if (chSrc != chPat)
  3553. return FALSE;
  3554. }
  3555. //
  3556. // Advance when pattern character matches string character
  3557. //
  3558. wstrPattern++;
  3559. wstrStr++;
  3560. }
  3561. //
  3562. // Fail when there is more pattern and pattern does not end in an asterisk
  3563. //
  3564. chPat = *wstrPattern;
  3565. if (chPat && (chPat != L'*' || wstrPattern[1]))
  3566. return FALSE;
  3567. return TRUE;
  3568. }
  3569. #endif
  3570. typedef BOOL (WINAPI * GETDISKFREESPACEEXA)(
  3571. PCSTR lpDirectoryName, // directory name
  3572. PULARGE_INTEGER lpFreeBytesAvailable, // bytes available to caller
  3573. PULARGE_INTEGER lpTotalNumberOfBytes, // bytes on disk
  3574. PULARGE_INTEGER lpTotalNumberOfFreeBytes // free bytes on disk
  3575. );
  3576. typedef BOOL (WINAPI * GETDISKFREESPACEEXW)(
  3577. PCWSTR lpDirectoryName, // directory name
  3578. PULARGE_INTEGER lpFreeBytesAvailable, // bytes available to caller
  3579. PULARGE_INTEGER lpTotalNumberOfBytes, // bytes on disk
  3580. PULARGE_INTEGER lpTotalNumberOfFreeBytes // free bytes on disk
  3581. );
  3582. BOOL
  3583. Winnt32GetDiskFreeSpaceNewA(
  3584. IN PCSTR DriveName,
  3585. OUT DWORD * OutSectorsPerCluster,
  3586. OUT DWORD * OutBytesPerSector,
  3587. OUT ULARGE_INTEGER * OutNumberOfFreeClusters,
  3588. OUT ULARGE_INTEGER * OutTotalNumberOfClusters
  3589. )
  3590. /*++
  3591. Routine Description:
  3592. On Win9x GetDiskFreeSpace never return free/total space more than 2048MB.
  3593. Winnt32GetDiskFreeSpaceNew use GetDiskFreeSpaceEx to calculate real number of free/total clusters.
  3594. Has same declaration as GetDiskFreeSpaceA.
  3595. Arguments:
  3596. DriveName - supplies directory name
  3597. OutSectorsPerCluster - receive number of sectors per cluster
  3598. OutBytesPerSector - receive number of bytes per sector
  3599. OutNumberOfFreeClusters - receive number of free clusters
  3600. OutTotalNumberOfClusters - receive number of total clusters
  3601. Return Value:
  3602. TRUE if the function succeeds.
  3603. If the function fails, the return value is FALSE. To get extended error information, call GetLastError
  3604. --*/
  3605. {
  3606. ULARGE_INTEGER TotalNumberOfFreeBytes = {0, 0};
  3607. ULARGE_INTEGER TotalNumberOfBytes = {0, 0};
  3608. ULARGE_INTEGER DonotCare;
  3609. HMODULE hKernel32;
  3610. GETDISKFREESPACEEXA pGetDiskFreeSpaceExA;
  3611. ULARGE_INTEGER NumberOfFreeClusters = {0, 0};
  3612. ULARGE_INTEGER TotalNumberOfClusters = {0, 0};
  3613. DWORD SectorsPerCluster;
  3614. DWORD BytesPerSector;
  3615. if(!GetDiskFreeSpaceA(DriveName,
  3616. &SectorsPerCluster,
  3617. &BytesPerSector,
  3618. &NumberOfFreeClusters.LowPart,
  3619. &TotalNumberOfClusters.LowPart)){
  3620. DebugLog (
  3621. Winnt32LogError,
  3622. TEXT("Winnt32GetDiskFreeSpaceNewA: GetDiskFreeSpaceA failed on drive %1"),
  3623. 0,
  3624. DriveName);
  3625. return FALSE;
  3626. }
  3627. hKernel32 = LoadLibraryA("kernel32.dll");
  3628. pGetDiskFreeSpaceExA = (GETDISKFREESPACEEXA)GetProcAddress(hKernel32, "GetDiskFreeSpaceExA");
  3629. if(pGetDiskFreeSpaceExA &&
  3630. pGetDiskFreeSpaceExA(DriveName, &DonotCare, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)){
  3631. NumberOfFreeClusters.QuadPart = TotalNumberOfFreeBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
  3632. TotalNumberOfClusters.QuadPart = TotalNumberOfBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
  3633. }
  3634. else{
  3635. DebugLog (
  3636. Winnt32LogWarning,
  3637. pGetDiskFreeSpaceExA?
  3638. TEXT("Winnt32GetDiskFreeSpaceNewA: GetDiskFreeSpaceExA is failed"):
  3639. TEXT("Winnt32GetDiskFreeSpaceNewA: GetDiskFreeSpaceExA function is not in kernel32.dll"),
  3640. 0);
  3641. }
  3642. FreeLibrary(hKernel32);
  3643. if(OutSectorsPerCluster){
  3644. *OutSectorsPerCluster = SectorsPerCluster;
  3645. }
  3646. if(OutBytesPerSector){
  3647. *OutBytesPerSector = BytesPerSector;
  3648. }
  3649. if(OutNumberOfFreeClusters){
  3650. OutNumberOfFreeClusters->QuadPart = NumberOfFreeClusters.QuadPart;
  3651. }
  3652. if(OutTotalNumberOfClusters){
  3653. OutTotalNumberOfClusters->QuadPart = TotalNumberOfClusters.QuadPart;
  3654. }
  3655. return TRUE;
  3656. }
  3657. BOOL
  3658. Winnt32GetDiskFreeSpaceNewW(
  3659. IN PCWSTR DriveName,
  3660. OUT DWORD * OutSectorsPerCluster,
  3661. OUT DWORD * OutBytesPerSector,
  3662. OUT ULARGE_INTEGER * OutNumberOfFreeClusters,
  3663. OUT ULARGE_INTEGER * OutTotalNumberOfClusters
  3664. )
  3665. /*++
  3666. Routine Description:
  3667. Correct NumberOfFreeClusters and TotalNumberOfClusters out parameters
  3668. with using GetDiskFreeSpace and GetDiskFreeSpaceEx
  3669. Arguments:
  3670. DriveName - supplies directory name
  3671. OutSectorsPerCluster - receive number of sectors per cluster
  3672. OutBytesPerSector - receive number of bytes per sector
  3673. OutNumberOfFreeClusters - receive number of free clusters
  3674. OutTotalNumberOfClusters - receive number of total clusters
  3675. Return Value:
  3676. TRUE if the function succeeds.
  3677. If the function fails, the return value is FALSE. To get extended error information, call GetLastError
  3678. --*/
  3679. {
  3680. ULARGE_INTEGER TotalNumberOfFreeBytes = {0, 0};
  3681. ULARGE_INTEGER TotalNumberOfBytes = {0, 0};
  3682. ULARGE_INTEGER DonotCare;
  3683. HMODULE hKernel32;
  3684. GETDISKFREESPACEEXW pGetDiskFreeSpaceExW;
  3685. ULARGE_INTEGER NumberOfFreeClusters = {0, 0};
  3686. ULARGE_INTEGER TotalNumberOfClusters = {0, 0};
  3687. DWORD SectorsPerCluster;
  3688. DWORD BytesPerSector;
  3689. if(!GetDiskFreeSpaceW(DriveName,
  3690. &SectorsPerCluster,
  3691. &BytesPerSector,
  3692. &NumberOfFreeClusters.LowPart,
  3693. &TotalNumberOfClusters.LowPart)){
  3694. DebugLog (
  3695. Winnt32LogError,
  3696. TEXT("Winnt32GetDiskFreeSpaceNewW: GetDiskFreeSpaceW failed on drive %1"),
  3697. 0,
  3698. DriveName);
  3699. return FALSE;
  3700. }
  3701. hKernel32 = LoadLibraryA("kernel32.dll");
  3702. pGetDiskFreeSpaceExW = (GETDISKFREESPACEEXW)GetProcAddress(hKernel32, "GetDiskFreeSpaceExW");
  3703. if(pGetDiskFreeSpaceExW &&
  3704. pGetDiskFreeSpaceExW(DriveName, &DonotCare, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)){
  3705. NumberOfFreeClusters.QuadPart = TotalNumberOfFreeBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
  3706. TotalNumberOfClusters.QuadPart = TotalNumberOfBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
  3707. }
  3708. else{
  3709. DebugLog (
  3710. Winnt32LogWarning,
  3711. pGetDiskFreeSpaceExW?
  3712. TEXT("Winnt32GetDiskFreeSpaceNewW: GetDiskFreeSpaceExW is failed"):
  3713. TEXT("Winnt32GetDiskFreeSpaceNewW: GetDiskFreeSpaceExW function is not in kernel32.dll"),
  3714. 0);
  3715. }
  3716. FreeLibrary(hKernel32);
  3717. if(OutSectorsPerCluster){
  3718. *OutSectorsPerCluster = SectorsPerCluster;
  3719. }
  3720. if(OutBytesPerSector){
  3721. *OutBytesPerSector = BytesPerSector;
  3722. }
  3723. if(OutNumberOfFreeClusters){
  3724. OutNumberOfFreeClusters->QuadPart = NumberOfFreeClusters.QuadPart;
  3725. }
  3726. if(OutTotalNumberOfClusters){
  3727. OutTotalNumberOfClusters->QuadPart = TotalNumberOfClusters.QuadPart;
  3728. }
  3729. return TRUE;
  3730. }
  3731. BOOL
  3732. ReplaceSubStr(
  3733. IN OUT LPTSTR SrcStr,
  3734. IN LPTSTR SrcSubStr,
  3735. IN LPTSTR DestSubStr
  3736. )
  3737. /*++
  3738. Routine Description:
  3739. Replaces the source substr with the destination substr in the source
  3740. string.
  3741. NOTE : SrcSubStr needs to be longer than or equal in length to
  3742. DestSubStr.
  3743. Arguments:
  3744. SrcStr : The source to operate upon. Also receives the new string.
  3745. SrcSubStr : The source substring to search for and replace.
  3746. DestSubStr : The substring to replace with for the occurences
  3747. of SrcSubStr in SrcStr.
  3748. Return Value:
  3749. TRUE if successful, otherwise FALSE.
  3750. --*/
  3751. {
  3752. BOOL Result = FALSE;
  3753. //
  3754. // Validate the arguments
  3755. //
  3756. if (SrcStr && SrcSubStr && *SrcSubStr &&
  3757. (!DestSubStr || (_tcslen(SrcSubStr) >= _tcslen(DestSubStr)))) {
  3758. if (!DestSubStr || _tcsicmp(SrcSubStr, DestSubStr)) {
  3759. ULONG SrcStrLen = _tcslen(SrcStr);
  3760. ULONG SrcSubStrLen = _tcslen(SrcSubStr);
  3761. ULONG DestSubStrLen = DestSubStr ? _tcslen(DestSubStr) : 0;
  3762. LPTSTR DestStr = malloc((SrcStrLen + 1) * sizeof(TCHAR));
  3763. if (DestStr) {
  3764. LPTSTR CurrDestStr = DestStr;
  3765. LPTSTR PrevSrcStr = SrcStr;
  3766. LPTSTR CurrSrcStr = _tcsstr(SrcStr, SrcSubStr);
  3767. while (CurrSrcStr) {
  3768. //
  3769. // Skip starting substr & copy previous unmatched pattern
  3770. //
  3771. if (PrevSrcStr != CurrSrcStr) {
  3772. _tcsncpy(CurrDestStr, PrevSrcStr, (CurrSrcStr - PrevSrcStr));
  3773. CurrDestStr += (CurrSrcStr - PrevSrcStr);
  3774. *CurrDestStr = TEXT('\0');
  3775. }
  3776. //
  3777. // Copy destination substr
  3778. //
  3779. if (DestSubStr) {
  3780. _tcscpy(CurrDestStr, DestSubStr);
  3781. CurrDestStr += DestSubStrLen;
  3782. *CurrDestStr = TEXT('\0');
  3783. }
  3784. //
  3785. // Look for next substr
  3786. //
  3787. CurrSrcStr += SrcSubStrLen;
  3788. PrevSrcStr = CurrSrcStr;
  3789. CurrSrcStr = _tcsstr(CurrSrcStr, SrcSubStr);
  3790. }
  3791. //
  3792. // Copy remaining src string if any
  3793. //
  3794. if (!_tcsstr(PrevSrcStr, SrcSubStr)) {
  3795. _tcscpy(CurrDestStr, PrevSrcStr);
  3796. }
  3797. //
  3798. // Copy the new string back to the src string
  3799. //
  3800. _tcscpy(SrcStr, DestStr);
  3801. free(DestStr);
  3802. Result = TRUE;
  3803. }
  3804. } else {
  3805. Result = TRUE;
  3806. }
  3807. }
  3808. return Result;
  3809. }
  3810. VOID
  3811. RemoveTrailingWack (
  3812. PTSTR String
  3813. )
  3814. {
  3815. if (String) {
  3816. PTSTR p = _tcsrchr (String, TEXT('\\'));
  3817. if (p && p[1] == 0) {
  3818. *p = 0;
  3819. }
  3820. }
  3821. }
  3822. ULONGLONG
  3823. SystemTimeToFileTime64 (
  3824. IN PSYSTEMTIME SystemTime
  3825. )
  3826. {
  3827. FILETIME ft;
  3828. ULARGE_INTEGER result;
  3829. SystemTimeToFileTime (SystemTime, &ft);
  3830. result.LowPart = ft.dwLowDateTime;
  3831. result.HighPart = ft.dwHighDateTime;
  3832. return result.QuadPart;
  3833. }
  3834. DWORD
  3835. MyGetFullPathName (
  3836. IN PCTSTR FileName, // file name
  3837. IN DWORD BufferLength, // size of path buffer
  3838. IN PTSTR Buffer, // path buffer
  3839. OUT PTSTR* FilePart // address of file name in path
  3840. )
  3841. {
  3842. DWORD d = GetFullPathName (FileName, BufferLength, Buffer, FilePart);
  3843. return d < BufferLength ? d : 0;
  3844. }
  3845. DWORD
  3846. MyGetModuleFileName (
  3847. IN HMODULE Module,
  3848. OUT PTSTR Buffer,
  3849. IN DWORD BufferLength
  3850. )
  3851. {
  3852. DWORD d = GetModuleFileName (Module, Buffer, BufferLength);
  3853. Buffer[BufferLength - 1] = 0;
  3854. return d < BufferLength ? d : 0;
  3855. }