Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5760 lines
163 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. asrbkup.c
  5. Abstract:
  6. This module contains the following ASR routines:
  7. AsrCreateStateFile{A|W}
  8. AsrAddSifEntry{A|W}
  9. AsrFreeContext
  10. Author:
  11. Guhan Suriyanarayanan (guhans) 27-May-2000
  12. Environment:
  13. User-mode only.
  14. Notes:
  15. Naming conventions:
  16. _AsrpXXX private ASR Macros
  17. AsrpXXX private ASR routines
  18. AsrXXX Publically defined and documented routines
  19. Revision History:
  20. 27-May-2000 guhans
  21. Moved ASR-backup related routines from asr.c to
  22. asrbkup.c
  23. 01-Jan-2000 guhans
  24. Initial implementation for Asr routines in asr.c
  25. --*/
  26. #include "setupp.h"
  27. #pragma hdrstop
  28. #include <initguid.h> // DiskClassGuid
  29. #include <diskguid.h> // GPT partition type guids
  30. #include <ntddvol.h> // ioctl_volume_query_failover_set
  31. #include <setupapi.h> // SetupDi routines
  32. #include <mountmgr.h> // mountmgr ioctls
  33. #include <rpcdce.h> // UuidToStringW, RpcStringFreeW
  34. #include <winasr.h> // ASR public routines
  35. #define THIS_MODULE 'B'
  36. #include "asrpriv.h" // Private ASR definitions and routines
  37. //
  38. // --------
  39. // constants local to this module. These are not accessed outside this file.
  40. // --------
  41. //
  42. //
  43. // The Setup Key to find the system partition from
  44. //
  45. const WCHAR ASR_REGKEY_SETUP[] = L"SYSTEM\\SETUP";
  46. const WCHAR ASR_REGVALUE_SYSTEM_PARTITION[] = L"SystemPartition";
  47. //
  48. // The ASR registry entries. Currently, this is used to look
  49. // up the commands to run during an Asr backup, but we could
  50. // have other settings here later.
  51. //
  52. const WCHAR ASR_REGKEY_ASR_COMMANDS[]
  53. = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Asr\\Commands";
  54. const WCHAR ASR_REGKEY_ASR[]
  55. = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Asr\\";
  56. const WCHAR ASR_REGVALUE_TIMEOUT[] = L"ProcessTimeOut";
  57. //
  58. // File to save the PnP information in.
  59. //
  60. const WCHAR ASR_DEFAULT_SIF_PATH[] = L"\\\\?\\%systemroot%\\repair\\asr.sif";
  61. const WCHAR ASRPNP_DEFAULT_SIF_NAME[] = L"asrpnp.sif";
  62. //
  63. // We only support x86, AMD64, and IA64 architectures.
  64. //
  65. const WCHAR ASR_PLATFORM_X86[] = L"x86";
  66. const WCHAR ASR_PLATFORM_AMD64[] = L"AMD64";
  67. const WCHAR ASR_PLATFORM_IA64[] = L"IA64";
  68. //
  69. // This is the suffix that we add when launching the apps registered for ASR.
  70. // Remember to change the length if you're changing this. The length should
  71. // include space for 20 digits (max 64-bit int) + null + space at the beginning.
  72. //
  73. #define ASR_COMMANDLINE_SUFFIX_LEN 35
  74. const WCHAR ASR_COMMANDLINE_SUFFIX[] = L" /context=%I64u";
  75. //
  76. // Miscellaneous constants
  77. //
  78. const WCHAR ASR_DOS_DEVICES_PREFIX[] = L"\\DosDevices\\";
  79. const WCHAR ASR_DEVICE_PATH_PREFIX[] = L"\\Device\\Harddisk";
  80. //
  81. // Sections in asr.sif
  82. //
  83. const WCHAR ASR_SIF_VERSION_SECTION_NAME[] = L"[VERSION]";
  84. const WCHAR ASR_SIF_SYSTEM_SECTION_NAME[] = L"[SYSTEMS]";
  85. const WCHAR ASR_SIF_BUSES_SECTION_NAME[] = L"[BUSES]";
  86. const WCHAR ASR_SIF_MBR_DISKS_SECTION_NAME[] = L"[DISKS.MBR]";
  87. const WCHAR ASR_SIF_GPT_DISKS_SECTION_NAME[] = L"[DISKS.GPT]";
  88. const WCHAR ASR_SIF_MBR_PARTITIONS_SECTION_NAME[] = L"[PARTITIONS.MBR]";
  89. const WCHAR ASR_SIF_GPT_PARTITIONS_SECTION_NAME[] = L"[PARTITIONS.GPT]";
  90. const WCHAR ASR_SIF_PROVIDER_PREFIX[] = L"Provider=";
  91. // wcslen("Provider=""\r\n\0")
  92. #define ASR_SIF_CCH_PROVIDER_STRING 14
  93. //
  94. // While launching registered applications during an ASR backup, we
  95. // add two environment variables to the environment block for the
  96. // process being launched: the AsrContext and the critical volume
  97. // list.
  98. //
  99. #define ASR_CCH_ENVBLOCK_ASR_ENTRIES (32 + 1 + 28 + 2)
  100. const WCHAR ASR_ENVBLOCK_CONTEXT_ENTRY[] = L"_AsrContext=%I64u";
  101. const WCHAR ASR_ENVBLOCK_CRITICAL_VOLUME_ENTRY[]
  102. = L"_AsrCriticalVolumeList=";
  103. //
  104. // Pre-defined flags designating the boot and system partitions
  105. // in the partitions section of asr.sif. Remember to change the
  106. // counter-parts in setupdd.sys if you change these!
  107. //
  108. const BYTE ASR_FLAGS_BOOT_PTN = 1;
  109. const BYTE ASR_FLAGS_SYSTEM_PTN = 2;
  110. //
  111. // For now, we only allow one system per sif file. If a sif
  112. // already exists at the location AsrCreateStateFile is called,
  113. // the existing sif is deleted. The asr.sif architecture does
  114. // allow for multiple systems per sif file, but
  115. // - I don't see any compelling reason to support this, and
  116. // - It would be a test nightmare
  117. //
  118. const BYTE ASR_SYSTEM_KEY = 1;
  119. //
  120. // _AsrpCheckTrue: primarily used with WriteFile calls.
  121. //
  122. #define _AsrpCheckTrue( Expression ) \
  123. if (!Expression) { \
  124. return FALSE; \
  125. }
  126. //
  127. // --------
  128. // constants used across asr modules.
  129. // --------
  130. //
  131. const WCHAR ASR_SIF_SYSTEM_SECTION[] = L"SYSTEMS";
  132. const WCHAR ASR_SIF_BUSES_SECTION[] = L"BUSES";
  133. const WCHAR ASR_SIF_MBR_DISKS_SECTION[] = L"DISKS.MBR";
  134. const WCHAR ASR_SIF_GPT_DISKS_SECTION[] = L"DISKS.GPT";
  135. const WCHAR ASR_SIF_MBR_PARTITIONS_SECTION[] = L"PARTITIONS.MBR";
  136. const WCHAR ASR_SIF_GPT_PARTITIONS_SECTION[] = L"PARTITIONS.GPT";
  137. const WCHAR ASR_WSZ_VOLUME_PREFIX[]
  138. = L"\\??\\Volume";
  139. const WCHAR ASR_WSZ_DEVICE_PATH_FORMAT[]
  140. = L"\\Device\\Harddisk%d\\Partition%d";
  141. //
  142. // --------
  143. // function prototypes
  144. // --------
  145. //
  146. //
  147. // Function prototype for AsrCreatePnpStateFileW.
  148. // (linked into syssetup.dll from pnpsif.lib)
  149. //
  150. BOOL
  151. AsrCreatePnpStateFileW(
  152. IN PCWSTR lpFilePath
  153. );
  154. //
  155. // --------
  156. // private functions
  157. // --------
  158. //
  159. BOOL
  160. AsrpGetMountPoints(
  161. IN PCWSTR DeviceName,
  162. IN CONST DWORD SizeDeviceName,
  163. OUT PMOUNTMGR_MOUNT_POINTS *pMountPointsOut
  164. )
  165. /*++
  166. Routine Description:
  167. Returns the current list of mount-points for DeviceName, by querying the
  168. mount manager.
  169. Arguments:
  170. DeviceName - The device name that the mount-point list is requested for.
  171. Typically, this is something of the form
  172. \Device\HarddiskX\PartitionY or \DosDevices\X:
  173. SizeDeviceName - The size, in bytes, of DeviceName. This includes the
  174. terminating null character.
  175. pMountPointsOut - Receives the output list of mount-points. The caller
  176. must free this memory by calling HeapFree for the current process
  177. heap.
  178. Return Values:
  179. TRUE, if everything went well. MountPointsOut contains the promised data.
  180. FALSE, if the mount manager returned an error. MountPoints is NULL. Call
  181. GetLastError() for more information.
  182. --*/
  183. {
  184. PMOUNTMGR_MOUNT_POINT mountPointIn = NULL;
  185. PMOUNTMGR_MOUNT_POINTS mountPointsOut = NULL;
  186. MOUNTMGR_MOUNT_POINTS mountPointsTemp;
  187. DWORD mountPointsSize = 0;
  188. HANDLE mpHandle = NULL;
  189. HANDLE heapHandle = NULL;
  190. ULONG index = 0;
  191. LONG status = ERROR_SUCCESS;
  192. BOOL result = FALSE;
  193. memset(&mountPointsTemp, 0L, sizeof(MOUNTMGR_MOUNT_POINTS));
  194. MYASSERT(pMountPointsOut);
  195. *pMountPointsOut = NULL;
  196. heapHandle = GetProcessHeap();
  197. MYASSERT(heapHandle);
  198. mountPointIn = (PMOUNTMGR_MOUNT_POINT) HeapAlloc(
  199. heapHandle,
  200. HEAP_ZERO_MEMORY,
  201. sizeof (MOUNTMGR_MOUNT_POINT) + (SizeDeviceName - sizeof(WCHAR))
  202. );
  203. _AsrpErrExitCode((!mountPointIn), status, ERROR_NOT_ENOUGH_MEMORY);
  204. //
  205. // Try with a decently sized mountPoints out: if it isn't big
  206. // enough, we'll realloc as needed
  207. //
  208. mountPointsOut = (PMOUNTMGR_MOUNT_POINTS) HeapAlloc(
  209. heapHandle,
  210. HEAP_ZERO_MEMORY,
  211. (MAX_PATH + 1) * (sizeof(WCHAR))
  212. );
  213. _AsrpErrExitCode(!mountPointsOut, status, ERROR_NOT_ENOUGH_MEMORY);
  214. //
  215. // Get a handle to the mount manager
  216. //
  217. mpHandle = CreateFileW(
  218. MOUNTMGR_DOS_DEVICE_NAME, // lpFileName
  219. 0, // dwDesiredAccess
  220. FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
  221. NULL, // lpSecurityAttributes
  222. OPEN_EXISTING, // dwCreationFlags
  223. FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
  224. NULL // hTemplateFile
  225. );
  226. _AsrpErrExitCode((!mpHandle || INVALID_HANDLE_VALUE == mpHandle),
  227. status,
  228. GetLastError()
  229. );
  230. //
  231. // put the DeviceName right after struct mountPointIn
  232. //
  233. wcsncpy((PWSTR) (mountPointIn + 1),
  234. DeviceName,
  235. (SizeDeviceName / sizeof(WCHAR)) - 1
  236. );
  237. mountPointIn->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
  238. mountPointIn->DeviceNameLength = (USHORT)(SizeDeviceName - sizeof(WCHAR));
  239. result = DeviceIoControl(
  240. mpHandle,
  241. IOCTL_MOUNTMGR_QUERY_POINTS,
  242. mountPointIn,
  243. sizeof(MOUNTMGR_MOUNT_POINT) + mountPointIn->DeviceNameLength,
  244. &mountPointsTemp,
  245. sizeof(MOUNTMGR_MOUNT_POINTS),
  246. &mountPointsSize,
  247. NULL
  248. );
  249. while (!result) {
  250. status = GetLastError();
  251. if (ERROR_MORE_DATA == status) {
  252. //
  253. // The buffer is not big enough, re-size and try again
  254. //
  255. status = ERROR_SUCCESS;
  256. _AsrpHeapFree(mountPointsOut);
  257. mountPointsOut = (PMOUNTMGR_MOUNT_POINTS) HeapAlloc(
  258. heapHandle,
  259. HEAP_ZERO_MEMORY,
  260. mountPointsTemp.Size
  261. );
  262. _AsrpErrExitCode((!mountPointsOut),
  263. status,
  264. ERROR_NOT_ENOUGH_MEMORY);
  265. result = DeviceIoControl(
  266. mpHandle,
  267. IOCTL_MOUNTMGR_QUERY_POINTS,
  268. mountPointIn,
  269. sizeof(MOUNTMGR_MOUNT_POINT) + mountPointIn->DeviceNameLength,
  270. mountPointsOut,
  271. mountPointsTemp.Size,
  272. &mountPointsSize,
  273. NULL
  274. );
  275. _AsrpErrExitCode((!mountPointsSize), status, GetLastError());
  276. }
  277. else {
  278. //
  279. // If some other error occurred, EXIT.
  280. //
  281. result = TRUE;
  282. status = GetLastError();
  283. // _AsrpErrExitCode(status, status, GetLastError());
  284. }
  285. }
  286. EXIT:
  287. //
  288. // Free up locally allocated memory
  289. //
  290. _AsrpHeapFree(mountPointIn);
  291. if (ERROR_SUCCESS != status) {
  292. //
  293. // On failure, free up mountPointsOut as well
  294. //
  295. _AsrpHeapFree(mountPointsOut);
  296. }
  297. _AsrpCloseHandle(mpHandle);
  298. *pMountPointsOut = mountPointsOut;
  299. return (BOOL) (ERROR_SUCCESS == status);
  300. }
  301. BOOL
  302. AsrpGetMorePartitionInfo(
  303. IN PCWSTR DeviceName,
  304. IN CONST DWORD SizeDeviceName,
  305. IN CONST PASR_SYSTEM_INFO pSystemInfo OPTIONAL,
  306. OUT PWSTR pVolumeGuid,
  307. OUT USHORT* pPartitionFlags OPTIONAL,
  308. OUT UCHAR* pFileSystemType OPTIONAL,
  309. OUT LPDWORD pClusterSize OPTIONAL
  310. )
  311. /*++
  312. Routine Description:
  313. Gets additional information about the partition specified by DeviceName,
  314. including the volume guid (if any) for the volume that maps to the
  315. partition specified by DeviceName.
  316. If the partition is the current system or boot drive, pPartitionFlags and
  317. pFileSystemType are set appropriately.
  318. Arguments:
  319. DeviceName - A null terminated string containing the device path to the
  320. partition, typically of the form \Device\HarddiskX\PartitionY
  321. SizeDeviceName - Size, in bytes, of DeviceName, including \0 at the end
  322. pSystemInfo - The SYSTEM_INFO structure for the current system; used for
  323. finding out the current system partition.
  324. This is an optional parameter. If absent, pPartitionFlags will
  325. not have the SYSTEM_FLAG set, even if DeviceName is in fact the
  326. system partition.
  327. pVolumeGuid - Receives a null-terminated string containing the GUID for
  328. the volume on this partition. This is only relevant for basic
  329. disks, where volumes and partitions have a one-on-one
  330. relationship.
  331. This will be set to a blank null-terminated string if there is no
  332. volume (or multiple volumes) on this partition.
  333. *** Note that if ANY of three of the OPTIONAL parameters are not present,
  334. NONE of them will be filled with valid data.
  335. pPartitionFlags - If the current partition is a partition of interest,
  336. this receives the appropriate flags, IN ADDITION TO THE FLAGS
  337. ALREADY SET when the routine is called (i.e., caller should
  338. usually zero this out). Currently, the two flags of interest are:
  339. ASR_FLAGS_BOOT_PTN for the boot partition
  340. ASR_FLAGS_SYSTEM_PTN for (you guessed it) the system partition
  341. pFileSystemType - If (and ONLY if) the current partition is a partition of
  342. interest, this will contain a UCHAR to the file-system type of the
  343. partition. Currently, the three file-systems this recognises are:
  344. PARTITION_HUGE (FAT)
  345. PARTITION_FAT32 (FAT32)
  346. PARTITION_IFS (NTFS)
  347. pClusterSize - The file-system cluster size. Set to 0 if the information
  348. could not be obtained.
  349. Return Value:
  350. If the function succeeds, the return value is a nonzero value.
  351. If the function fails, the return value is zero. To get extended error
  352. information, call GetLastError().
  353. --*/
  354. {
  355. PMOUNTMGR_MOUNT_POINTS mountPointsOut = NULL;
  356. HANDLE heapHandle = NULL;
  357. ULONG index = 0;
  358. LONG status = ERROR_SUCCESS;
  359. BOOL result = FALSE;
  360. BOOL volumeGuidSet = FALSE;
  361. //
  362. // set OUT variables to known values.
  363. //
  364. MYASSERT(pVolumeGuid);
  365. wcscpy(pVolumeGuid, L"");
  366. /* if (ARGUMENT_PRESENT(pPartitionFlags)) {
  367. *pPartitionFlags = 0;
  368. }
  369. */
  370. if (ARGUMENT_PRESENT(pClusterSize)) {
  371. *pClusterSize = 0;
  372. }
  373. heapHandle = GetProcessHeap();
  374. MYASSERT(heapHandle);
  375. //
  376. // Open the mount manager, and get a list of all the symbolic links
  377. // this partition
  378. //
  379. result = AsrpGetMountPoints(DeviceName, SizeDeviceName, &mountPointsOut);
  380. _AsrpErrExitCode((!result), status, GetLastError());
  381. _AsrpErrExitCode((!mountPointsOut), status, ERROR_SUCCESS);
  382. //
  383. // Check if this is the system partition, by comparing the
  384. // device path with the one stored in the Setup key.
  385. //
  386. if (ARGUMENT_PRESENT(pSystemInfo) && ARGUMENT_PRESENT(pPartitionFlags)) {
  387. PWSTR deviceName = (PWSTR) (
  388. ((LPBYTE) mountPointsOut) +
  389. mountPointsOut->MountPoints[index].DeviceNameOffset
  390. );
  391. UINT sizeDeviceName =
  392. (UINT)(mountPointsOut->MountPoints[index].DeviceNameLength);
  393. if ((pSystemInfo->SystemPath) &&
  394. (wcslen(pSystemInfo->SystemPath)==sizeDeviceName/sizeof(WCHAR)) &&
  395. (!wcsncmp(pSystemInfo->SystemPath, deviceName,
  396. sizeDeviceName/sizeof(WCHAR)))
  397. ) {
  398. *pPartitionFlags |= ASR_FLAGS_SYSTEM_PTN;
  399. }
  400. }
  401. for (index = 0; index < mountPointsOut->NumberOfMountPoints; index++) {
  402. //
  403. // Go through the list of mount points returned, and find the one
  404. // that looks like an nt volume guid
  405. //
  406. PWSTR linkName = (PWSTR) (((LPBYTE) mountPointsOut) +
  407. mountPointsOut->MountPoints[index].SymbolicLinkNameOffset
  408. );
  409. UINT sizeLinkName =
  410. (UINT)(mountPointsOut->MountPoints[index].SymbolicLinkNameLength);
  411. if ((!volumeGuidSet) &&
  412. !wcsncmp(ASR_WSZ_VOLUME_PREFIX,
  413. linkName,
  414. wcslen(ASR_WSZ_VOLUME_PREFIX))
  415. ) {
  416. wcsncpy(pVolumeGuid, linkName, sizeLinkName / sizeof(WCHAR));
  417. volumeGuidSet = TRUE; // we got a GUID, no need to check again
  418. }
  419. else if (
  420. ARGUMENT_PRESENT(pSystemInfo) &&
  421. ARGUMENT_PRESENT(pPartitionFlags)
  422. ) {
  423. //
  424. // Also, if this link isn't a GUID, it might be a drive letter.
  425. // use the boot directory's drive letter to check if this
  426. // is the boot volume, and mark it if so.
  427. //
  428. if (!wcsncmp(ASR_DOS_DEVICES_PREFIX,
  429. linkName,
  430. wcslen(ASR_DOS_DEVICES_PREFIX))
  431. ) {
  432. if ((pSystemInfo->BootDirectory) &&
  433. (pSystemInfo->BootDirectory[0]
  434. == linkName[wcslen(ASR_DOS_DEVICES_PREFIX)])
  435. ) {
  436. *pPartitionFlags |= ASR_FLAGS_BOOT_PTN;
  437. }
  438. }
  439. }
  440. }
  441. EXIT:
  442. //
  443. // If this is a partition of interest, we need to get the file-system
  444. // type as well
  445. //
  446. if (ARGUMENT_PRESENT(pFileSystemType) &&
  447. ARGUMENT_PRESENT(pPartitionFlags) &&
  448. ARGUMENT_PRESENT(pClusterSize)
  449. ) {
  450. if (*pPartitionFlags) {
  451. WCHAR fsName[20];
  452. DWORD dwSectorsPerCluster = 0,
  453. dwBytesPerSector = 0,
  454. dwNumFreeClusters = 0,
  455. dwTotalNumClusters = 0;
  456. //
  457. // Convert the NT Volume-GUID (starts with \??\) to a DOS
  458. // Volume (begins with a \\?\, and ends with a back-slash),
  459. // since GetVolumeInformation needs it in this format.
  460. //
  461. pVolumeGuid[1] = L'\\';
  462. wcscat(pVolumeGuid, L"\\");
  463. memset(fsName, 0L, 20*sizeof(WCHAR));
  464. result = GetVolumeInformationW(pVolumeGuid, NULL, 0L,
  465. NULL, NULL, NULL, fsName, 20);
  466. if (result) {
  467. if (!wcscmp(fsName, L"NTFS")) {
  468. *pFileSystemType = PARTITION_IFS;
  469. }
  470. else if (!wcscmp(fsName, L"FAT32")) {
  471. *pFileSystemType = PARTITION_FAT32;
  472. }
  473. else if (!wcscmp(fsName, L"FAT")) {
  474. *pFileSystemType = PARTITION_HUGE;
  475. }
  476. else {
  477. *pFileSystemType = 0;
  478. }
  479. }
  480. else {
  481. GetLastError(); // debug
  482. }
  483. result = GetDiskFreeSpace(pVolumeGuid,
  484. &dwSectorsPerCluster,
  485. &dwBytesPerSector,
  486. &dwNumFreeClusters,
  487. &dwTotalNumClusters
  488. );
  489. if (result) {
  490. *pClusterSize = dwSectorsPerCluster * dwBytesPerSector;
  491. }
  492. else {
  493. GetLastError(); // debug
  494. }
  495. //
  496. // Convert the guid back to NT namespace, by changing \\?\
  497. // to \??\ and removing the trailing slash.
  498. //
  499. pVolumeGuid[1] = L'?';
  500. pVolumeGuid[wcslen(pVolumeGuid)-1] = L'\0';
  501. }
  502. }
  503. //
  504. // Free up locally allocated data
  505. //
  506. _AsrpHeapFree(mountPointsOut);
  507. //
  508. // If we hit errors, make sure the VolumeGuid is a blank string.
  509. //
  510. if (status != ERROR_SUCCESS) {
  511. wcscpy(pVolumeGuid, L"");
  512. }
  513. return (BOOL) (status == ERROR_SUCCESS);
  514. }
  515. BOOL
  516. AsrpDetermineBuses(
  517. IN PASR_DISK_INFO pDiskList
  518. )
  519. /*++
  520. Routine Description:
  521. This attempts to group the disks based on which bus they are on. For
  522. SCSI disks, this is relatively easy, since it can be based on the
  523. location info (port).
  524. For other disks, we attempt to get the PnP parent node of the disks, and
  525. group all disks having the same parent.
  526. The groups are identified by the SifBusKey field of each disk structure--
  527. i.e., all disks that have SifBusKey == 1 are on one bus, SifBusKey == 2
  528. are on another bus, and so on. The SifBusKey values are guaranteed to be
  529. sequential, and not have any holes (i.e., For a system with "n" buses,
  530. the SifBusKey values will be 1,2,3,...,n).
  531. At the end SifBusKey is zero for disks which couldn't be grouped.
  532. Arguments:
  533. pDiskList - The ASR_DISK_INFO list of disks present on the current system.
  534. Return Value:
  535. If the function succeeds, the return value is a nonzero value.
  536. If the function fails, the return value is zero. To get extended error
  537. information, call GetLastError().
  538. --*/
  539. {
  540. BOOL done = FALSE,
  541. newPass = TRUE;
  542. ULONG port = 0,
  543. sifBusKey = 0;
  544. DEVINST parent;
  545. STORAGE_BUS_TYPE busType = BusTypeUnknown;
  546. PASR_DISK_INFO pCurrentDisk = pDiskList;
  547. //
  548. // The first pass goes through and groups all the scsi disks together.
  549. // Note that this works for IDE too, since IDE disks respond to the
  550. // IOCTL_SCSI_GET_ADDRESS and appear to us to have valid location info.
  551. //
  552. do {
  553. sifBusKey++;
  554. pCurrentDisk = pDiskList;
  555. done = TRUE;
  556. newPass = TRUE;
  557. while (pCurrentDisk) {
  558. if ((BusTypeUnknown == pCurrentDisk->BusType) ||
  559. (!pCurrentDisk->pScsiAddress)) {
  560. pCurrentDisk = pCurrentDisk->pNext;
  561. continue;
  562. }
  563. if (0 == pCurrentDisk->SifBusKey) {
  564. done = FALSE;
  565. if (newPass) {
  566. pCurrentDisk->SifBusKey = sifBusKey;
  567. port = pCurrentDisk->pScsiAddress->PortNumber;
  568. busType = pCurrentDisk->BusType;
  569. newPass = FALSE;
  570. }
  571. else {
  572. if ((pCurrentDisk->pScsiAddress->PortNumber == port) &&
  573. (pCurrentDisk->BusType == busType)) {
  574. pCurrentDisk->SifBusKey = sifBusKey;
  575. }
  576. }
  577. }
  578. pCurrentDisk = pCurrentDisk->pNext;
  579. }
  580. } while (!done);
  581. //
  582. // By now, the only disks with SifBusKey is 0 are disks for which
  583. // pScsiAddress is NULL, ie (most-likely) non SCSI/IDE disks. Attempt
  584. // to group them on the basis of their parent dev node (which is usually
  585. // the bus). We may have to loop through multiple times again.
  586. //
  587. --sifBusKey; // compensate for the last pass above
  588. do {
  589. sifBusKey++;
  590. pCurrentDisk = pDiskList;
  591. done = TRUE;
  592. newPass = TRUE;
  593. while (pCurrentDisk) {
  594. if ((BusTypeUnknown == pCurrentDisk->BusType) ||
  595. (!pCurrentDisk->pScsiAddress)) {
  596. if ((0 == pCurrentDisk->SifBusKey)
  597. && (pCurrentDisk->ParentDevInst)) {
  598. done = FALSE;
  599. if (newPass) {
  600. pCurrentDisk->SifBusKey = sifBusKey;
  601. parent = pCurrentDisk->ParentDevInst;
  602. newPass = FALSE;
  603. }
  604. else {
  605. if (pCurrentDisk->ParentDevInst == parent) {
  606. pCurrentDisk->SifBusKey = sifBusKey;
  607. }
  608. }
  609. }
  610. }
  611. pCurrentDisk = pCurrentDisk->pNext;
  612. }
  613. } while (!done);
  614. //
  615. // Disks that still have SifBusKey = 0 couldn't be grouped. Either the
  616. // BusType is unknown, or the parent node couldn't be found.
  617. //
  618. return TRUE;
  619. }
  620. BOOL
  621. AsrpGetDiskLayout(
  622. IN CONST HANDLE hDisk,
  623. IN CONST PASR_SYSTEM_INFO pSystemInfo,
  624. OUT PASR_DISK_INFO pCurrentDisk,
  625. IN BOOL AllDetails
  626. )
  627. /*++
  628. Routine Description:
  629. Fills in the fields of the pCurrentDisk structure with the relevant
  630. information about the disk represented by hDisk, by querying the system
  631. with the appropriate IOCTL's.
  632. Arguments:
  633. hDisk - handle to the disk of interest.
  634. pSystemInfo - The SYSTEM_INFO structure for the current system.
  635. pCurrentDisk - Receives the information about the disk represented by
  636. hDisk
  637. AllDetails - If FALSE, only the pDriveLayout information of pCurrentDisk
  638. is filled in. This is an optimisation that comes in handy when
  639. we're dealing with disks on a shared cluster bus.
  640. If TRUE, all the fields of pCurrentDisk are filled in.
  641. Return Value:
  642. If the function succeeds, the return value is a nonzero value.
  643. If the function fails, the return value is zero. To get extended error
  644. information, call GetLastError().
  645. --*/
  646. {
  647. DWORD index = 0,
  648. status = ERROR_SUCCESS;
  649. DWORD dwBytesReturned = 0L,
  650. bufferLength = 0L;
  651. BOOL result = FALSE;
  652. PDISK_GEOMETRY diskGeometry = NULL;
  653. DWORD sizeDiskGeometry = 0L;
  654. PDRIVE_LAYOUT_INFORMATION_EX driveLayoutEx = NULL;
  655. DWORD sizeDriveLayoutEx = 0L;
  656. STORAGE_DEVICE_NUMBER deviceNumber;
  657. DWORD sizeDeviceNumber = 0L;
  658. PPARTITION_INFORMATION_EX partition0Ex = NULL;
  659. DWORD sizePartition0Ex = 0L;
  660. PASR_PTN_INFO pPartitionTable = NULL;
  661. DWORD sizePartitionTable = 0L;
  662. STORAGE_PROPERTY_QUERY propertyQuery;
  663. STORAGE_DEVICE_DESCRIPTOR *deviceDesc = NULL;
  664. STORAGE_BUS_TYPE busType = BusTypeUnknown;
  665. PSCSI_ADDRESS scsiAddress = NULL;
  666. HANDLE heapHandle = GetProcessHeap(); // For memory allocations
  667. MYASSERT(heapHandle); // It better not be NULL
  668. MYASSERT(pCurrentDisk);
  669. MYASSERT((hDisk) && (INVALID_HANDLE_VALUE != hDisk));
  670. //
  671. // Initialize OUT variables to known values
  672. //
  673. pCurrentDisk->Style = PARTITION_STYLE_RAW;
  674. pCurrentDisk->pDriveLayoutEx = NULL;
  675. pCurrentDisk->sizeDriveLayoutEx = 0L;
  676. pCurrentDisk->pDiskGeometry = NULL;
  677. pCurrentDisk->sizeDiskGeometry = 0L;
  678. pCurrentDisk->pPartition0Ex = NULL;
  679. pCurrentDisk->sizePartition0Ex = 0L;
  680. pCurrentDisk->pScsiAddress = NULL;
  681. pCurrentDisk->BusType = BusTypeUnknown;
  682. pCurrentDisk->SifBusKey = 0L;
  683. SetLastError(ERROR_SUCCESS);
  684. //
  685. // Get the device number for this device. This should succeed even if
  686. // this is a clustered disk that this node doesn't own.
  687. //
  688. result = DeviceIoControl(
  689. hDisk,
  690. IOCTL_STORAGE_GET_DEVICE_NUMBER,
  691. NULL,
  692. 0,
  693. &deviceNumber,
  694. sizeof(STORAGE_DEVICE_NUMBER),
  695. &sizeDeviceNumber,
  696. NULL
  697. );
  698. _AsrpErrExitCode(!result, status, GetLastError());
  699. pCurrentDisk->DeviceNumber = deviceNumber.DeviceNumber;
  700. //
  701. // The output buffer for IOCTL_DISK_GET_DRIVE_LAYOUT_EX consists of a
  702. // DRIVE_LAYOUT_INFORMATION_EX structure as a header, followed by an
  703. // array of PARTITION_INFORMATION_EX structures.
  704. //
  705. // We initially allocate enough space for the DRIVE_LAYOUT_INFORMATION_EX
  706. // struct, which contains a single PARTITION_INFORMATION_EX struct, and
  707. // 3 more PARTITION_INFORMATION_EX structs, since each (MBR) disk will
  708. // have a minimum of four partitions, even if they are not all in use.
  709. // If the disk contains more than four partitions, we'll increase the
  710. // buffer size as needed
  711. //
  712. bufferLength = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
  713. (sizeof(PARTITION_INFORMATION_EX) * 3);
  714. driveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) HeapAlloc(
  715. heapHandle,
  716. HEAP_ZERO_MEMORY,
  717. bufferLength
  718. );
  719. _AsrpErrExitCode(!driveLayoutEx, status, ERROR_NOT_ENOUGH_MEMORY);
  720. result = FALSE;
  721. while (!result) {
  722. result = DeviceIoControl(
  723. hDisk,
  724. IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
  725. NULL,
  726. 0L,
  727. driveLayoutEx,
  728. bufferLength,
  729. &sizeDriveLayoutEx,
  730. NULL
  731. );
  732. if (!result) {
  733. status = GetLastError();
  734. _AsrpHeapFree(driveLayoutEx);
  735. //
  736. // If the buffer is of insufficient size, resize the buffer.
  737. // Note that get-drive-layout-ex could return error-insufficient-
  738. // buffer (instead of? in addition to? error-more-data)
  739. //
  740. if ((ERROR_MORE_DATA == status) ||
  741. (ERROR_INSUFFICIENT_BUFFER == status)
  742. ) {
  743. status = ERROR_SUCCESS;
  744. bufferLength += sizeof(PARTITION_INFORMATION_EX) * 4;
  745. driveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) HeapAlloc(
  746. heapHandle,
  747. HEAP_ZERO_MEMORY,
  748. bufferLength
  749. );
  750. _AsrpErrExitCode(!driveLayoutEx,
  751. status,
  752. ERROR_NOT_ENOUGH_MEMORY
  753. );
  754. }
  755. else {
  756. //
  757. // some other error occurred, EXIT, and go to the next drive.
  758. //
  759. result = TRUE;
  760. status = ERROR_SUCCESS;
  761. }
  762. }
  763. else {
  764. if (!AllDetails) {
  765. //
  766. // If we don't want all the details for this disk, just exit
  767. // now. This is used in the case of clusters, where we don't
  768. // want to get all the details twice even if the current node
  769. // owns the disk
  770. //
  771. pCurrentDisk->pDriveLayoutEx = driveLayoutEx;
  772. pCurrentDisk->sizeDriveLayoutEx = sizeDriveLayoutEx;
  773. //
  774. // Jump to EXIT
  775. //
  776. _AsrpErrExitCode(TRUE, status, ERROR_SUCCESS);
  777. }
  778. //
  779. // The disk geometry: so that we can match the bytes-per-sector
  780. // value during restore.
  781. //
  782. diskGeometry = (PDISK_GEOMETRY) HeapAlloc(
  783. heapHandle,
  784. HEAP_ZERO_MEMORY,
  785. sizeof(DISK_GEOMETRY)
  786. );
  787. _AsrpErrExitCode(!diskGeometry, status, ERROR_NOT_ENOUGH_MEMORY);
  788. result = DeviceIoControl(
  789. hDisk,
  790. IOCTL_DISK_GET_DRIVE_GEOMETRY,
  791. NULL,
  792. 0,
  793. diskGeometry,
  794. sizeof(DISK_GEOMETRY),
  795. &sizeDiskGeometry,
  796. NULL
  797. );
  798. _AsrpErrExitCode(!result, status, ERROR_READ_FAULT);
  799. partition0Ex = (PPARTITION_INFORMATION_EX) HeapAlloc(
  800. heapHandle,
  801. HEAP_ZERO_MEMORY,
  802. sizeof(PARTITION_INFORMATION_EX)
  803. );
  804. _AsrpErrExitCode(!partition0Ex, status, ERROR_NOT_ENOUGH_MEMORY);
  805. //
  806. // Information about partition 0 (the whole disk), to get the true
  807. // sector count of the disk
  808. //
  809. result = DeviceIoControl(
  810. hDisk,
  811. IOCTL_DISK_GET_PARTITION_INFO_EX,
  812. NULL,
  813. 0,
  814. partition0Ex,
  815. sizeof(PARTITION_INFORMATION_EX),
  816. &sizePartition0Ex,
  817. NULL
  818. );
  819. _AsrpErrExitCode(!result, status, ERROR_READ_FAULT);
  820. //
  821. // Figure out the bus that this disk is on. This will only be
  822. // used to group the disks--all the disks on a bus will be
  823. // restored to the same bus if possible
  824. //
  825. propertyQuery.QueryType = PropertyStandardQuery;
  826. propertyQuery.PropertyId = StorageDeviceProperty;
  827. deviceDesc = (STORAGE_DEVICE_DESCRIPTOR *) HeapAlloc(
  828. heapHandle,
  829. HEAP_ZERO_MEMORY,
  830. ASR_BUFFER_SIZE
  831. );
  832. _AsrpErrExitCode(!deviceDesc, status, ERROR_NOT_ENOUGH_MEMORY);
  833. result = DeviceIoControl(
  834. hDisk,
  835. IOCTL_STORAGE_QUERY_PROPERTY,
  836. &propertyQuery,
  837. sizeof(STORAGE_PROPERTY_QUERY),
  838. deviceDesc,
  839. ASR_BUFFER_SIZE,
  840. &dwBytesReturned,
  841. NULL
  842. );
  843. if (result) {
  844. busType = deviceDesc->BusType;
  845. }
  846. _AsrpHeapFree(deviceDesc);
  847. scsiAddress = (PSCSI_ADDRESS) HeapAlloc(
  848. heapHandle,
  849. HEAP_ZERO_MEMORY,
  850. sizeof(SCSI_ADDRESS)
  851. );
  852. _AsrpErrExitCode(!scsiAddress, status, ERROR_NOT_ENOUGH_MEMORY);
  853. result = DeviceIoControl(
  854. hDisk,
  855. IOCTL_SCSI_GET_ADDRESS,
  856. NULL,
  857. 0,
  858. scsiAddress,
  859. sizeof(SCSI_ADDRESS),
  860. &dwBytesReturned,
  861. NULL
  862. );
  863. if (!result) { // Not fatal--expected for non SCSI/IDE disks
  864. _AsrpHeapFree(scsiAddress);
  865. result = TRUE;
  866. }
  867. }
  868. }
  869. if (driveLayoutEx) {
  870. PPARTITION_INFORMATION_EX currentPartitionEx = NULL;
  871. WCHAR devicePath[MAX_PATH + 1];
  872. pCurrentDisk->Style = driveLayoutEx->PartitionStyle;
  873. sizePartitionTable = sizeof(ASR_PTN_INFO) *
  874. (driveLayoutEx->PartitionCount);
  875. pPartitionTable = (PASR_PTN_INFO) HeapAlloc(
  876. heapHandle,
  877. HEAP_ZERO_MEMORY,
  878. sizePartitionTable
  879. );
  880. _AsrpErrExitCode(!pPartitionTable, status, ERROR_NOT_ENOUGH_MEMORY);
  881. for (index = 0; index < driveLayoutEx->PartitionCount; index++) {
  882. currentPartitionEx = &driveLayoutEx->PartitionEntry[index];
  883. pPartitionTable[index].SlotIndex = index;
  884. if (currentPartitionEx->PartitionNumber) {
  885. swprintf(devicePath,
  886. ASR_WSZ_DEVICE_PATH_FORMAT,
  887. deviceNumber.DeviceNumber,
  888. currentPartitionEx->PartitionNumber
  889. );
  890. pPartitionTable[index].PartitionFlags = 0;
  891. //
  892. // Check specially for the EFI system partition
  893. //
  894. if ((PARTITION_STYLE_GPT == driveLayoutEx->PartitionStyle) &&
  895. IsEqualGUID(&(currentPartitionEx->Gpt.PartitionType), &(PARTITION_SYSTEM_GUID))
  896. ) {
  897. pPartitionTable[index].PartitionFlags |= ASR_FLAGS_SYSTEM_PTN;
  898. }
  899. AsrpGetMorePartitionInfo(
  900. devicePath,
  901. (wcslen(devicePath)+1) * sizeof(WCHAR), // cb including \0
  902. pSystemInfo,
  903. pPartitionTable[index].szVolumeGuid,
  904. &(pPartitionTable[index].PartitionFlags),
  905. &(pPartitionTable[index].FileSystemType),
  906. &(pPartitionTable[index].ClusterSize)
  907. );
  908. //
  909. // Make sure that the file-system type for the EFI system
  910. // partition is set to FAT
  911. //
  912. if ((PARTITION_STYLE_GPT == driveLayoutEx->PartitionStyle) &&
  913. IsEqualGUID(&(currentPartitionEx->Gpt.PartitionType), &(PARTITION_SYSTEM_GUID))
  914. ) {
  915. pPartitionTable[index].FileSystemType = PARTITION_HUGE;
  916. }
  917. if (pPartitionTable[index].PartitionFlags) {
  918. pCurrentDisk->IsCritical = TRUE;
  919. }
  920. }
  921. }
  922. }
  923. pCurrentDisk->pDriveLayoutEx = driveLayoutEx;
  924. pCurrentDisk->sizeDriveLayoutEx = sizeDriveLayoutEx;
  925. pCurrentDisk->pDiskGeometry = diskGeometry;
  926. pCurrentDisk->sizeDiskGeometry = sizeDiskGeometry;
  927. pCurrentDisk->DeviceNumber = deviceNumber.DeviceNumber;
  928. pCurrentDisk->pPartition0Ex = partition0Ex;
  929. pCurrentDisk->sizePartition0Ex = sizePartition0Ex;
  930. pCurrentDisk->pScsiAddress = scsiAddress;
  931. pCurrentDisk->BusType = busType;
  932. pCurrentDisk->PartitionInfoTable = pPartitionTable;
  933. pCurrentDisk->sizePartitionInfoTable = sizePartitionTable;
  934. EXIT:
  935. //
  936. // Free up locally allocated memory on failure
  937. //
  938. if (status != ERROR_SUCCESS) {
  939. _AsrpHeapFree(driveLayoutEx);
  940. _AsrpHeapFree(diskGeometry);
  941. _AsrpHeapFree(partition0Ex);
  942. _AsrpHeapFree(scsiAddress);
  943. _AsrpHeapFree(pPartitionTable);
  944. }
  945. //
  946. // Make sure the last error is set if we are going to return FALSE
  947. //
  948. if ((ERROR_SUCCESS != status) && (ERROR_SUCCESS == GetLastError())) {
  949. SetLastError(status);
  950. }
  951. return (BOOL) (status == ERROR_SUCCESS);
  952. }
  953. BOOL
  954. AsrpGetSystemPath(
  955. IN PASR_SYSTEM_INFO pSystemInfo
  956. )
  957. /*++
  958. Routine Description:
  959. Gets the system partition DevicePath, and fills in the SystemPath field
  960. of the ASR_SYSTEM_INFO struct, by looking up the HKLM\Setup registry key.
  961. This key is updated at every boot with the path to the current system
  962. device. The path is of the form
  963. \Device\Harddisk0\Partition1 (basic disks)
  964. \Device\HarddiskDmVolumes\DgName\Volume1 (dynamic disks)
  965. Arguments:
  966. pSystemInfo - The SystemPath field of this struct will be filled with
  967. a pointer to the path to the current system device
  968. Return Value:
  969. If the function succeeds, the return value is a nonzero value.
  970. pSystemInfo->SystemPath is a pointer to a null-terminated string
  971. containing the path to the current system device. The caller is
  972. reponsible for freeing this memory with a call to
  973. HeapFree(GetProcessHeap(),...).
  974. If the function fails, the return value is zero. To get extended error
  975. information, call GetLastError(). pSystemInfo->SystemPath is set
  976. to NULL.
  977. --*/
  978. {
  979. HKEY regKey = NULL;
  980. DWORD type = 0L;
  981. HANDLE heapHandle = NULL;
  982. DWORD status = ERROR_SUCCESS;
  983. PWSTR systemPartition = NULL;
  984. DWORD cbSystemPartition = 0L;
  985. heapHandle = GetProcessHeap();
  986. MYASSERT(heapHandle);
  987. MYASSERT(pSystemInfo);
  988. if (!pSystemInfo) {
  989. SetLastError(ERROR_BAD_ENVIRONMENT);
  990. return FALSE;
  991. }
  992. pSystemInfo->SystemPath = NULL;
  993. //
  994. // Open the reg key to find the system partition
  995. //
  996. status = RegOpenKeyExW(
  997. HKEY_LOCAL_MACHINE, // hKey
  998. ASR_REGKEY_SETUP, // lpSubKey
  999. 0, // ulOptions--Reserved, must be 0
  1000. MAXIMUM_ALLOWED, // samDesired
  1001. &regKey // phkResult
  1002. );
  1003. _AsrpErrExitCode(status, status, ERROR_REGISTRY_IO_FAILED);
  1004. //
  1005. // Allocate a reasonably sized buffer for the system partition, to
  1006. // start off with. If this isn't big enough, we'll re-allocate as
  1007. // needed.
  1008. //
  1009. cbSystemPartition = (MAX_PATH + 1) * sizeof(WCHAR);
  1010. systemPartition = HeapAlloc(heapHandle,
  1011. HEAP_ZERO_MEMORY,
  1012. cbSystemPartition
  1013. );
  1014. _AsrpErrExitCode((!systemPartition), status, ERROR_NOT_ENOUGH_MEMORY);
  1015. //
  1016. // Get the system partition device Name. This is of the form
  1017. // \Device\Harddisk0\Partition1 (basic disks)
  1018. // \Device\HarddiskDmVolumes\DgName\Volume1 (dynamic disks)
  1019. //
  1020. status = RegQueryValueExW(
  1021. regKey,
  1022. ASR_REGVALUE_SYSTEM_PARTITION,
  1023. NULL,
  1024. &type,
  1025. (LPBYTE)systemPartition,
  1026. &cbSystemPartition // \0 is included
  1027. );
  1028. _AsrpErrExitCode((type != REG_SZ), status, ERROR_REGISTRY_IO_FAILED);
  1029. while (ERROR_MORE_DATA == status) {
  1030. //
  1031. // Our buffer wasn't big enough, cbSystemPartition contains
  1032. // the required size.
  1033. //
  1034. _AsrpHeapFree(systemPartition);
  1035. systemPartition = HeapAlloc(heapHandle,
  1036. HEAP_ZERO_MEMORY,
  1037. cbSystemPartition
  1038. );
  1039. _AsrpErrExitCode((!systemPartition), status, ERROR_NOT_ENOUGH_MEMORY);
  1040. status = RegQueryValueExW(
  1041. regKey,
  1042. ASR_REGVALUE_SYSTEM_PARTITION,
  1043. NULL,
  1044. &type,
  1045. (LPBYTE)systemPartition,
  1046. &cbSystemPartition // \0 is included
  1047. );
  1048. }
  1049. EXIT:
  1050. if (regKey) {
  1051. RegCloseKey(regKey);
  1052. regKey = NULL;
  1053. }
  1054. if (ERROR_SUCCESS != status) {
  1055. _AsrpHeapFree(systemPartition);
  1056. return FALSE;
  1057. }
  1058. else {
  1059. pSystemInfo->SystemPath = systemPartition;
  1060. return TRUE;
  1061. }
  1062. }
  1063. BOOL
  1064. AsrpInitSystemInformation(
  1065. IN OUT PASR_SYSTEM_INFO pSystemInfo,
  1066. IN CONST BOOL bEnableAutoExtend
  1067. )
  1068. /*++
  1069. Routine Description:
  1070. Initialisation routine to allocate memory for various fields in the
  1071. ASR_SYSTEM_INFO structure, and fill them in with the relevant information.
  1072. Arguments:
  1073. pSystemInfo - The struct to be filled in with info about the current
  1074. system.
  1075. Return Value:
  1076. If the function succeeds, the return value is a nonzero value. The caller
  1077. is responsible for freeing memory pointed to by the various
  1078. pointers in the struct, using HeapFree(GetProcessHeap(),...).
  1079. If the function fails, the return value is zero. To get extended error
  1080. information, call GetLastError(). The caller is still responsible
  1081. for checking the fields and releasing any non-NULL pointers using
  1082. HeapFree(GetProcessHeap(),...).
  1083. --*/
  1084. {
  1085. DWORD cchBootDirectory = 0L,
  1086. reqdSize = 0L;
  1087. BOOL result = FALSE;
  1088. HANDLE heapHandle = GetProcessHeap();
  1089. //
  1090. // Initialise the structure to zeroes
  1091. //
  1092. memset(pSystemInfo, 0L, sizeof (ASR_SYSTEM_INFO));
  1093. //
  1094. // The auto-extension feature
  1095. //
  1096. pSystemInfo->AutoExtendEnabled = bEnableAutoExtend;
  1097. //
  1098. // Get the machine name
  1099. //
  1100. pSystemInfo->sizeComputerName = MAX_COMPUTERNAME_LENGTH + 1;
  1101. if (!GetComputerNameW(pSystemInfo->ComputerName,
  1102. &(pSystemInfo->sizeComputerName)
  1103. )) {
  1104. //
  1105. // GetComputerName sets LastError
  1106. //
  1107. return FALSE;
  1108. }
  1109. //
  1110. // Get the Processor Architecture. We expect the process architecture
  1111. // to be a either x86, amd64, or ia64, so if it doesn't fit in our buffer of
  1112. // six characters, we don't support it anyway.
  1113. //
  1114. pSystemInfo->Platform = HeapAlloc(heapHandle,
  1115. HEAP_ZERO_MEMORY,
  1116. 6*sizeof(WCHAR)
  1117. );
  1118. if (!pSystemInfo->Platform) {
  1119. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1120. return FALSE;
  1121. }
  1122. reqdSize = GetEnvironmentVariableW(L"PROCESSOR_ARCHITECTURE",
  1123. pSystemInfo->Platform,
  1124. 6
  1125. );
  1126. if (0 == reqdSize) {
  1127. //
  1128. // We couldn't find the PROCESSOR_ARCHITECTURE variable
  1129. //
  1130. SetLastError(ERROR_BAD_ENVIRONMENT);
  1131. return FALSE;
  1132. }
  1133. if (reqdSize > 6) {
  1134. //
  1135. // The architecture didn't fit in our buffer
  1136. //
  1137. SetLastError(ERROR_NOT_SUPPORTED);
  1138. return FALSE;
  1139. }
  1140. //
  1141. // Get the OS version
  1142. //
  1143. pSystemInfo->OsVersionEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  1144. result = GetVersionEx((LPOSVERSIONINFO) (&(pSystemInfo->OsVersionEx)));
  1145. if (!result) {
  1146. //
  1147. // GetVersionEx sets the LastError
  1148. //
  1149. return FALSE;
  1150. }
  1151. //
  1152. // Get the boot directory
  1153. //
  1154. pSystemInfo->BootDirectory = HeapAlloc(heapHandle,
  1155. HEAP_ZERO_MEMORY,
  1156. (MAX_PATH+1)*sizeof(WCHAR)
  1157. );
  1158. if (!(pSystemInfo->BootDirectory)) {
  1159. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1160. return FALSE;
  1161. }
  1162. cchBootDirectory = GetWindowsDirectoryW(pSystemInfo->BootDirectory,
  1163. MAX_PATH + 1
  1164. );
  1165. if (0 == cchBootDirectory) {
  1166. //
  1167. // GetWindowsDirectory sets LastError
  1168. //
  1169. return FALSE;
  1170. }
  1171. if (cchBootDirectory >
  1172. ASR_SIF_ENTRY_MAX_CHARS - MAX_COMPUTERNAME_LENGTH - 26) {
  1173. //
  1174. // We can't write out sif lines that are more than
  1175. // ASR_SIF_ENTRY_MAX_CHARS chars long
  1176. //
  1177. SetLastError(ERROR_BAD_ENVIRONMENT);
  1178. return FALSE;
  1179. }
  1180. if (cchBootDirectory > MAX_PATH) {
  1181. UINT cchNewSize = cchBootDirectory + 1;
  1182. //
  1183. // Our buffer wasn't big enough, free and re-alloc as needed
  1184. //
  1185. _AsrpHeapFree(pSystemInfo->BootDirectory);
  1186. pSystemInfo->BootDirectory = HeapAlloc(heapHandle,
  1187. HEAP_ZERO_MEMORY,
  1188. (cchNewSize + 1) * sizeof(WCHAR)
  1189. );
  1190. if (!(pSystemInfo->BootDirectory)) {
  1191. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1192. return FALSE;
  1193. }
  1194. cchBootDirectory = GetWindowsDirectoryW(pSystemInfo->BootDirectory,
  1195. MAX_PATH + 1
  1196. );
  1197. if (!cchBootDirectory) {
  1198. //
  1199. // GetWindowsDirectory sets LastError
  1200. //
  1201. return FALSE;
  1202. }
  1203. if (cchBootDirectory > cchNewSize) {
  1204. SetLastError(ERROR_BAD_ENVIRONMENT);
  1205. return FALSE;
  1206. }
  1207. }
  1208. //
  1209. // Get the system directory
  1210. //
  1211. if (!AsrpGetSystemPath(pSystemInfo)) {
  1212. //
  1213. // AsrpGetSystemPath sets LastError
  1214. //
  1215. return FALSE;
  1216. }
  1217. //
  1218. // Get the time-zone information. We need to save and restore this since
  1219. // GUI-mode Setup (ASR) will otherwise default to GMT, and the file-time
  1220. // stamps on all the restored files will be off, since most back-up apps
  1221. // assume that they're restoring in the same time-zone that they backed
  1222. // up in and do nothing special to restore the time-zone first.
  1223. //
  1224. GetTimeZoneInformation(&(pSystemInfo->TimeZoneInformation));
  1225. return TRUE;
  1226. }
  1227. BOOL
  1228. AsrpInitLayoutInformation(
  1229. IN CONST PASR_SYSTEM_INFO pSystemInfo,
  1230. IN OUT PASR_DISK_INFO pDiskList,
  1231. OUT PULONG MaxDeviceNumber OPTIONAL,
  1232. IN BOOL AllDetails
  1233. )
  1234. /*++
  1235. Routine Description:
  1236. Initialisation routine to fill in layout and other interesting information
  1237. about the disks on the system.
  1238. Arguments:
  1239. pSystemInfo - the ASR_SYSTEM_INFO for the current system
  1240. pDiskList - ASR_DISK_INFO list of disks on the current system, with
  1241. the DevicePath field for each disk pointing to a null terminated
  1242. path to the disk, that can be used to open a handle to the disk.
  1243. The other fields of the structure are filled in by this routine,
  1244. if the disk could be accessed and the appropriate info could be
  1245. obtained.
  1246. MaxDeviceNumber - Receives the max device number of all the disks on the
  1247. system. This can be used as an optimisation to allocate memory
  1248. for a table of disks based on the device number.
  1249. This is an optional argument.
  1250. AllDetails - If FALSE, only the pDriveLayout information is filled in for
  1251. each disk. This is an optimisation that comes in handy when
  1252. we're dealing with disks on a shared cluster bus.
  1253. If TRUE, all the fields are filled in for each disk.
  1254. Return Value:
  1255. If the function succeeds, the return value is a nonzero value.
  1256. If the function fails, the return value is zero. To get extended error
  1257. information, call GetLastError().
  1258. --*/
  1259. {
  1260. BOOL result = FALSE;
  1261. HANDLE hDisk = NULL;
  1262. PASR_DISK_INFO currentDisk = pDiskList;
  1263. if (ARGUMENT_PRESENT(MaxDeviceNumber)) {
  1264. *MaxDeviceNumber = 0;
  1265. }
  1266. while (currentDisk) {
  1267. //
  1268. // Open the disk. If an error occurs, get the next
  1269. // disk from the disk list and continue.
  1270. //
  1271. hDisk = CreateFileW(
  1272. currentDisk->DevicePath, // lpFileName
  1273. 0, // dwDesiredAccess
  1274. FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
  1275. NULL, // lpSecurityAttributes
  1276. OPEN_EXISTING, // dwCreationFlags
  1277. FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
  1278. NULL // hTemplateFile
  1279. );
  1280. if ((!hDisk) || (INVALID_HANDLE_VALUE == hDisk)) {
  1281. //
  1282. // We couldn't open the disk. If this is a critical disk, we'll
  1283. // fail later in AsrpMarkCriticalDisks, so it's okay to ignore
  1284. // this error for now.
  1285. //
  1286. currentDisk = currentDisk->pNext;
  1287. continue;
  1288. }
  1289. //
  1290. // Get the layout and other interesting info for this disk.
  1291. // If this fails, we must abort.
  1292. //
  1293. result = AsrpGetDiskLayout(hDisk,
  1294. pSystemInfo,
  1295. currentDisk,
  1296. AllDetails
  1297. );
  1298. if (!result) {
  1299. DWORD status = GetLastError();
  1300. _AsrpCloseHandle(hDisk); // this may change LastError.
  1301. SetLastError(status);
  1302. return FALSE;
  1303. }
  1304. _AsrpCloseHandle(hDisk);
  1305. //
  1306. // Set the max device number if needed
  1307. //
  1308. if (ARGUMENT_PRESENT(MaxDeviceNumber) &&
  1309. (currentDisk->DeviceNumber > *MaxDeviceNumber)
  1310. ) {
  1311. *MaxDeviceNumber = currentDisk->DeviceNumber;
  1312. }
  1313. //
  1314. // Get the next drive from the drive list.
  1315. //
  1316. currentDisk = currentDisk->pNext;
  1317. }
  1318. return TRUE;
  1319. }
  1320. BOOL
  1321. AsrpInitDiskInformation(
  1322. OUT PASR_DISK_INFO *ppDiskList
  1323. )
  1324. /*++
  1325. Routine Description:
  1326. Initialisation routine to get a list of disks present on the system. This
  1327. routine allocates a ASR_DISK_INFO struct for each disk on the machine, and
  1328. fills in the DevicePath and ParentDevInst fields of each with a path to
  1329. the disk. It is expected that the other fields will be filled in with a
  1330. subsequent call to AsrpInitLayoutInformation().
  1331. Arguments:
  1332. ppDiskList - Receives the location of the first ASR_DISK_INFO struct.
  1333. Return Value:
  1334. If the function succeeds, the return value is a nonzero value.
  1335. If the function fails, the return value is zero. To get extended error
  1336. information, call GetLastError(). ppDiskList may point to an
  1337. incomplete list of disks on the system, and it is the caller's
  1338. responsibility to free the memory allocated, if any, using
  1339. HeapFree(GetProcessHeap(),...).
  1340. --*/
  1341. {
  1342. DWORD count = 0,
  1343. status = ERROR_SUCCESS;
  1344. HDEVINFO hdevInfo = NULL;
  1345. BOOL result = FALSE;
  1346. PASR_DISK_INFO pNewDisk = NULL;
  1347. HANDLE heapHandle = NULL;
  1348. PSP_DEVICE_INTERFACE_DETAIL_DATA_W pDiDetail = NULL;
  1349. SP_DEVICE_INTERFACE_DATA devInterfaceData;
  1350. DWORD sizeDiDetail = 0;
  1351. SP_DEVINFO_DATA devInfoData;
  1352. //
  1353. // Initialise stuff to zeros
  1354. //
  1355. memset(&devInterfaceData, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
  1356. *ppDiskList = NULL;
  1357. heapHandle = GetProcessHeap(); // used for HeapAlloc functions
  1358. MYASSERT(heapHandle);
  1359. //
  1360. // Get a device interface set which includes all Disk devices
  1361. // present on the machine. DiskClassGuid is a predefined GUID that
  1362. // will return all disk-type device interfaces
  1363. //
  1364. hdevInfo = SetupDiGetClassDevsW(
  1365. &DiskClassGuid,
  1366. NULL,
  1367. NULL,
  1368. DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
  1369. );
  1370. _AsrpErrExitCode(
  1371. ((NULL == hdevInfo) || (INVALID_HANDLE_VALUE == hdevInfo)),
  1372. status,
  1373. ERROR_IO_DEVICE
  1374. );
  1375. //
  1376. // Iterate over all devices interfaces in the set
  1377. //
  1378. for (count = 0; ; count++) {
  1379. // must set size first
  1380. devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  1381. //
  1382. // Retrieve the device interface data for each device interface
  1383. //
  1384. result = SetupDiEnumDeviceInterfaces(
  1385. hdevInfo,
  1386. NULL,
  1387. &DiskClassGuid,
  1388. count,
  1389. &devInterfaceData
  1390. );
  1391. if (!result) {
  1392. //
  1393. // If we retrieved the last item, break
  1394. //
  1395. status = GetLastError();
  1396. if (ERROR_NO_MORE_ITEMS == status) {
  1397. status = ERROR_SUCCESS;
  1398. break;
  1399. }
  1400. else {
  1401. //
  1402. // Some other error occured, goto EXIT. We overwrite the
  1403. // last error.
  1404. //
  1405. _AsrpErrExitCode(status, status, ERROR_IO_DEVICE);
  1406. }
  1407. }
  1408. //
  1409. // Get the required buffer-size for the device path
  1410. //
  1411. result = SetupDiGetDeviceInterfaceDetailW(
  1412. hdevInfo,
  1413. &devInterfaceData,
  1414. NULL,
  1415. 0,
  1416. &sizeDiDetail,
  1417. NULL
  1418. );
  1419. if (!result) {
  1420. status = GetLastError();
  1421. //
  1422. // If a value other than "insufficient buffer" is returned,
  1423. // an error occured
  1424. //
  1425. _AsrpErrExitCode((ERROR_INSUFFICIENT_BUFFER != status),
  1426. status,
  1427. ERROR_IO_DEVICE
  1428. );
  1429. }
  1430. else {
  1431. //
  1432. // The call should have failed since we're getting the
  1433. // required buffer size. If it doesn't, and error occurred.
  1434. //
  1435. _AsrpErrExitCode(status, status, ERROR_IO_DEVICE);
  1436. }
  1437. //
  1438. // Allocate memory for the buffer
  1439. //
  1440. pDiDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W) HeapAlloc(
  1441. heapHandle,
  1442. HEAP_ZERO_MEMORY,
  1443. sizeDiDetail
  1444. );
  1445. _AsrpErrExitCode(!pDiDetail, status, ERROR_NOT_ENOUGH_MEMORY);
  1446. // must set the struct's size member
  1447. pDiDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
  1448. devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  1449. //
  1450. // Finally, retrieve the device interface detail info
  1451. //
  1452. result = SetupDiGetDeviceInterfaceDetailW(
  1453. hdevInfo,
  1454. &devInterfaceData,
  1455. pDiDetail,
  1456. sizeDiDetail,
  1457. NULL,
  1458. &devInfoData
  1459. );
  1460. _AsrpErrExitCode(!result, status, GetLastError());
  1461. //
  1462. // Okay, now alloc a struct for this disk, and fill in the DevicePath
  1463. // field with the Path from the interface detail.
  1464. //
  1465. pNewDisk = (PASR_DISK_INFO) HeapAlloc(
  1466. heapHandle,
  1467. HEAP_ZERO_MEMORY,
  1468. sizeof(ASR_DISK_INFO)
  1469. );
  1470. _AsrpErrExitCode(!pNewDisk, status, ERROR_NOT_ENOUGH_MEMORY);
  1471. //
  1472. // Insert at the head so this is O(1) and not O(n!)
  1473. //
  1474. pNewDisk->pNext = *ppDiskList;
  1475. *ppDiskList = pNewDisk;
  1476. pNewDisk->DevicePath = (PWSTR) HeapAlloc(
  1477. heapHandle,
  1478. HEAP_ZERO_MEMORY,
  1479. sizeof(WCHAR) * (wcslen(pDiDetail->DevicePath) + 1)
  1480. );
  1481. _AsrpErrExitCode(!(pNewDisk->DevicePath),
  1482. status,
  1483. ERROR_NOT_ENOUGH_MEMORY
  1484. );
  1485. wcscpy(pNewDisk->DevicePath, pDiDetail->DevicePath);
  1486. //
  1487. // Get the PnP parent of this disk, so we can use it for grouping
  1488. // disks later based on the bus they are on.
  1489. //
  1490. CM_Get_Parent(&(pNewDisk->ParentDevInst),
  1491. devInfoData.DevInst,
  1492. 0
  1493. );
  1494. _AsrpHeapFree(pDiDetail);
  1495. }
  1496. EXIT:
  1497. //
  1498. // Free local mem allocs
  1499. //
  1500. _AsrpHeapFree(pDiDetail);
  1501. if ((hdevInfo) && (INVALID_HANDLE_VALUE != hdevInfo)) {
  1502. SetupDiDestroyDeviceInfoList(hdevInfo);
  1503. hdevInfo = NULL;
  1504. }
  1505. return (BOOL) (status == ERROR_SUCCESS);
  1506. }
  1507. BOOL
  1508. AsrpMarkCriticalDisks(
  1509. IN PASR_DISK_INFO pDiskList,
  1510. IN PCWSTR CriticalVolumeList,
  1511. IN ULONG MaxDeviceNumber
  1512. )
  1513. /*++
  1514. Routine Description:
  1515. Sets the IsCritical flag of each of the critical disks on the system. A
  1516. disk is deemed "critical" if it is part of part of the failover set for
  1517. any of the critical volumes present on the system.
  1518. Arguments:
  1519. pDiskList - The list of disks on the current system.
  1520. CriticalVolumeList - A multi-string containing a list of the volume GUID's
  1521. of each of the critical volumes present on the system. The GUID's
  1522. must be in the NT name-space, i.e., must be of the form:
  1523. \??\Volume{GUID}
  1524. MaxDeviceNumber - The highest storage device number of the disks present
  1525. in the disk list, as determined by calling
  1526. IOCTL_STORAGE_GET_DEVICE_NUMBER for each of them.
  1527. Return Value:
  1528. If the function succeeds, the return value is a nonzero value.
  1529. If the function fails, the return value is zero. To get extended error
  1530. information, call GetLastError().
  1531. --*/
  1532. {
  1533. PCWSTR volGuid = NULL;
  1534. PASR_DISK_INFO currentDisk = NULL;
  1535. PVOLUME_FAILOVER_SET failoverSet = NULL;
  1536. DWORD index = 0,
  1537. reqdSize=0,
  1538. sizeFailoverSet = 0,
  1539. status = ERROR_SUCCESS;
  1540. BOOL result = TRUE,
  1541. *criticalDiskTable = NULL;
  1542. WCHAR devicePath[ASR_CCH_DEVICE_PATH_FORMAT + 1];
  1543. HANDLE heapHandle = NULL,
  1544. hDevice = NULL;
  1545. memset(devicePath, 0L, (ASR_CCH_DEVICE_PATH_FORMAT+1)*sizeof(WCHAR));
  1546. if (!CriticalVolumeList) {
  1547. //
  1548. // No critical volumes:
  1549. //
  1550. #ifdef PRERELEASE
  1551. return TRUE;
  1552. #else
  1553. return FALSE;
  1554. #endif
  1555. }
  1556. if (!pDiskList) {
  1557. //
  1558. // No disks on machine?!
  1559. //
  1560. MYASSERT(0 && L"DiskList is NULL");
  1561. return FALSE;
  1562. }
  1563. heapHandle = GetProcessHeap();
  1564. MYASSERT(heapHandle);
  1565. //
  1566. // criticalDiskTable is our table of BOOL values.
  1567. //
  1568. criticalDiskTable = (BOOL *) HeapAlloc(
  1569. heapHandle,
  1570. HEAP_ZERO_MEMORY,
  1571. sizeof (BOOL) * (MaxDeviceNumber + 1)
  1572. );
  1573. _AsrpErrExitCode(!criticalDiskTable, status, ERROR_NOT_ENOUGH_MEMORY);
  1574. //
  1575. // Try with a reasonable sized buffer first--say 10 disks. We'll
  1576. // realloc as needed if this isn't enough.
  1577. //
  1578. sizeFailoverSet = sizeof(VOLUME_FAILOVER_SET) + (10 * sizeof(ULONG));
  1579. failoverSet = (PVOLUME_FAILOVER_SET) HeapAlloc(
  1580. heapHandle,
  1581. HEAP_ZERO_MEMORY,
  1582. sizeFailoverSet
  1583. );
  1584. _AsrpErrExitCode(!failoverSet, status, ERROR_NOT_ENOUGH_MEMORY);
  1585. volGuid = CriticalVolumeList;
  1586. while (*volGuid) {
  1587. //
  1588. // Convert the \??\ to \\?\ so that CreateFile can use it
  1589. //
  1590. wcsncpy(devicePath, volGuid, ASR_CCH_DEVICE_PATH_FORMAT);
  1591. devicePath[1] = L'\\';
  1592. //
  1593. // Get a handle so we can send the ioctl
  1594. //
  1595. hDevice = CreateFileW(
  1596. devicePath, // lpFileName
  1597. 0, // dwDesiredAccess
  1598. FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
  1599. NULL, // lpSecurityAttributes
  1600. OPEN_EXISTING, // dwCreationFlags
  1601. 0, // dwFlagsAndAttributes
  1602. NULL // hTemplateFile
  1603. );
  1604. _AsrpErrExitCode(((!hDevice) || (INVALID_HANDLE_VALUE == hDevice)),
  1605. status,
  1606. GetLastError());
  1607. result = DeviceIoControl(
  1608. hDevice,
  1609. IOCTL_VOLUME_QUERY_FAILOVER_SET,
  1610. NULL,
  1611. 0,
  1612. failoverSet,
  1613. sizeFailoverSet,
  1614. &reqdSize,
  1615. NULL
  1616. );
  1617. //
  1618. // We're doing this in a while loop because if the disk configuration
  1619. // changes in the small interval between when we get the reqd buffer
  1620. // size and when we send the ioctl again with a buffer of the "reqd"
  1621. // size, we may still end up with a buffer that isn't big enough.
  1622. //
  1623. while (!result) {
  1624. status = GetLastError();
  1625. if (ERROR_MORE_DATA == status) {
  1626. //
  1627. // The buffer was too small, reallocate the reqd size.
  1628. //
  1629. status = ERROR_SUCCESS;
  1630. sizeFailoverSet = (sizeof(VOLUME_FAILOVER_SET) +
  1631. ((failoverSet->NumberOfDisks) * sizeof(ULONG)));
  1632. _AsrpHeapFree(failoverSet);
  1633. failoverSet = (PVOLUME_FAILOVER_SET) HeapAlloc(
  1634. heapHandle,
  1635. HEAP_ZERO_MEMORY,
  1636. sizeFailoverSet
  1637. );
  1638. _AsrpErrExitCode(!failoverSet,
  1639. status,
  1640. ERROR_NOT_ENOUGH_MEMORY
  1641. );
  1642. result = DeviceIoControl(
  1643. hDevice,
  1644. IOCTL_VOLUME_QUERY_FAILOVER_SET,
  1645. NULL,
  1646. 0,
  1647. failoverSet,
  1648. sizeFailoverSet,
  1649. &reqdSize,
  1650. NULL
  1651. );
  1652. }
  1653. else {
  1654. //
  1655. // The IOCTL failed because of something else, this is
  1656. // fatal since we can't find the critical disk list now.
  1657. //
  1658. _AsrpErrExitCode((TRUE), status, status);
  1659. }
  1660. }
  1661. //
  1662. // Mark the appropriate entries in our table
  1663. //
  1664. for (index = 0; index < failoverSet->NumberOfDisks; index++) {
  1665. criticalDiskTable[failoverSet->DiskNumbers[index]] = 1;
  1666. }
  1667. _AsrpCloseHandle(hDevice);
  1668. //
  1669. // Repeat for next volumeguid in list
  1670. //
  1671. volGuid += (wcslen(CriticalVolumeList) + 1);
  1672. }
  1673. //
  1674. // Now go through the list of disks, and mark the critical flags.
  1675. //
  1676. currentDisk = pDiskList;
  1677. while (currentDisk) {
  1678. if (currentDisk->IsClusterShared) {
  1679. //
  1680. // By definition, cluster shared disks cannot be critical.
  1681. //
  1682. currentDisk = currentDisk->pNext;
  1683. continue;
  1684. }
  1685. currentDisk->IsCritical =
  1686. (criticalDiskTable[currentDisk->DeviceNumber] ? TRUE : FALSE);
  1687. //
  1688. // Increment the entry, so that we can track how many critical volumes
  1689. // reside on this disk, and--more importantly--ensure that all the
  1690. // critical disks exist on the system (next loop below)
  1691. //
  1692. if (currentDisk->IsCritical) {
  1693. ++(criticalDiskTable[currentDisk->DeviceNumber]);
  1694. }
  1695. currentDisk = currentDisk->pNext;
  1696. }
  1697. //
  1698. // Finally, we want to make sure that we don't have any critical disks
  1699. // in our table that we didn't find physical disks for. (I.e., make
  1700. // sure that the system has no "missing" critical disks)
  1701. //
  1702. for (index = 0; index < MaxDeviceNumber; index++) {
  1703. if (1 == criticalDiskTable[index]) {
  1704. //
  1705. // If the table still has "1" for the value, it was never
  1706. // incremented in the while loop above, ie our diskList doesn't
  1707. // have a disk corresponding to this.
  1708. //
  1709. _AsrpErrExitCode(TRUE, status, ERROR_DEV_NOT_EXIST);
  1710. }
  1711. }
  1712. EXIT:
  1713. _AsrpHeapFree(failoverSet);
  1714. _AsrpHeapFree(criticalDiskTable);
  1715. _AsrpCloseHandle(hDevice);
  1716. return (BOOL)(ERROR_SUCCESS == status);
  1717. }
  1718. PASR_DISK_INFO
  1719. AsrpFreeDiskInfo(
  1720. PASR_DISK_INFO pCurrentDisk
  1721. )
  1722. /*++
  1723. Routine Description:
  1724. Helper function to free memory pointed to by various pointers in the
  1725. ASR_DISK_INFO struct, and then free the struct itself.
  1726. Arguments:
  1727. pCurrentDisk - the struct to be freed
  1728. Return Value:
  1729. pCurrentDisk->Next, which is a pointer to the next disk in the list
  1730. --*/
  1731. {
  1732. HANDLE heapHandle = NULL;
  1733. PASR_DISK_INFO pNext = NULL;
  1734. heapHandle = GetProcessHeap();
  1735. MYASSERT(heapHandle);
  1736. if (pCurrentDisk) {
  1737. pNext = pCurrentDisk->pNext;
  1738. //
  1739. // If it's a packed struct, then we only need to free the struct
  1740. // itself. If not, we need to free the memory the pointers point
  1741. // to as well.
  1742. //
  1743. if (!pCurrentDisk->IsPacked) {
  1744. _AsrpHeapFree(pCurrentDisk->DevicePath);
  1745. _AsrpHeapFree(pCurrentDisk->pDriveLayoutEx);
  1746. _AsrpHeapFree(pCurrentDisk->pDiskGeometry);
  1747. _AsrpHeapFree(pCurrentDisk->pPartition0Ex);
  1748. _AsrpHeapFree(pCurrentDisk->pScsiAddress);
  1749. _AsrpHeapFree(pCurrentDisk->PartitionInfoTable);
  1750. }
  1751. _AsrpHeapFree(pCurrentDisk);
  1752. }
  1753. return pNext;
  1754. }
  1755. BOOL
  1756. AsrpIsRemovableOrInaccesibleMedia(
  1757. IN PASR_DISK_INFO pDisk
  1758. )
  1759. /*++
  1760. Routine Description:
  1761. Checks if the disk represented by pDisk should be removed from our list
  1762. of disks that we'll store information on in the state file.
  1763. Disks that should be removed include disks that are removable, or disks
  1764. that we couldn't access.
  1765. Arguments:
  1766. pDisk - the disk structure to be checked
  1767. Return Value:
  1768. TRUE if the device is removable, or some key information about the disk is
  1769. missing. Since code depends on the driveLayout being non-NULL,
  1770. for instance, we shall just remove the disk from the list if we
  1771. couldn't get it's drive layout. We shall therefore not backup
  1772. information about any disk whose drive geo or layout we couldn't
  1773. get, and not restore to any such disk.
  1774. FALSE if the structure contains all the required information, and is not
  1775. a removable device.
  1776. --*/
  1777. {
  1778. if ((NULL == pDisk->pDiskGeometry) ||
  1779. (NULL == pDisk->pDriveLayoutEx) ||
  1780. (NULL == pDisk->pPartition0Ex) ||
  1781. (FixedMedia != pDisk->pDiskGeometry->MediaType)
  1782. ) {
  1783. return TRUE;
  1784. }
  1785. return FALSE;
  1786. }
  1787. BOOL
  1788. AsrpFreeNonFixedMedia(
  1789. IN OUT PASR_DISK_INFO *ppDiskList
  1790. )
  1791. /*++
  1792. Routine Description:
  1793. Removes removable media, and disks that are inaccessible, from the list of
  1794. disks passed in.
  1795. Arguments:
  1796. ppDiskList - a pointer to the address of the first disk in the list of all
  1797. the disks present on the current system.
  1798. Return Value:
  1799. If the function succeeds, the return value is a nonzero value.
  1800. If the function fails, the return value is zero. To get extended error
  1801. information, call GetLastError().
  1802. Currently, this function always succeeds.
  1803. --*/
  1804. {
  1805. PASR_DISK_INFO prevDisk = NULL,
  1806. currentDisk = *ppDiskList;
  1807. while (currentDisk) {
  1808. if (AsrpIsRemovableOrInaccesibleMedia(currentDisk)) {
  1809. //
  1810. // Disk is not Fixed, we should remove it from out list
  1811. //
  1812. if (NULL == prevDisk) { // this is the first disk in the list
  1813. *ppDiskList = currentDisk->pNext;
  1814. }
  1815. else {
  1816. prevDisk->pNext = currentDisk->pNext;
  1817. }
  1818. //
  1819. // Free it and get a pointer to the next disk
  1820. //
  1821. currentDisk = AsrpFreeDiskInfo(currentDisk);
  1822. }
  1823. else {
  1824. //
  1825. // Disk is okay, move on to the next disk
  1826. //
  1827. prevDisk = currentDisk;
  1828. currentDisk = currentDisk->pNext;
  1829. }
  1830. }
  1831. return TRUE;
  1832. }
  1833. VOID
  1834. AsrpFreeStateInformation(
  1835. IN OUT PASR_DISK_INFO *ppDiskList OPTIONAL,
  1836. IN OUT PASR_SYSTEM_INFO pSystemInfo OPTIONAL
  1837. )
  1838. /*++
  1839. Routine Description:
  1840. Frees the memory addressed by pointers in the ASR_DISK_INFO and
  1841. ASR_SYSTEM_INFO structs.
  1842. Frees the list of disks pointed to by the ASR_DISK_INFO struct.
  1843. Arguments:
  1844. ppDiskList - Pointer to the address of the first Disk in the DiskList
  1845. being freed. The address is set to NULL after the list is freed,
  1846. to prevent further unintended accesses to the freed object.
  1847. pSystemInfo - A pointer to the ASR_SYSTEM_INFO struct, containing the
  1848. pointers to be freed.
  1849. Return Value:
  1850. If the function succeeds, the return value is a nonzero value.
  1851. *ppDiskList is set to NULL.
  1852. If the function fails, the return value is zero. To get extended error
  1853. information, call GetLastError().
  1854. Currently, this function always succeeds.
  1855. --*/
  1856. {
  1857. PASR_DISK_INFO pTempDisk = NULL;
  1858. HANDLE heapHandle = GetProcessHeap();
  1859. MYASSERT(heapHandle);
  1860. if (ARGUMENT_PRESENT(ppDiskList)) {
  1861. pTempDisk = *ppDiskList;
  1862. while (pTempDisk) {
  1863. pTempDisk = AsrpFreeDiskInfo(pTempDisk);
  1864. }
  1865. *ppDiskList = NULL;
  1866. }
  1867. if (ARGUMENT_PRESENT(pSystemInfo)) {
  1868. _AsrpHeapFree(pSystemInfo->SystemPath);
  1869. _AsrpHeapFree(pSystemInfo->BootDirectory);
  1870. }
  1871. }
  1872. VOID
  1873. AsrpFreePartitionList(
  1874. IN OUT PASR_PTN_INFO_LIST *ppPtnList OPTIONAL
  1875. )
  1876. /*++
  1877. Routine Description:
  1878. Frees the list of partitions, along with memory addressed by all the
  1879. pointers in the list.
  1880. Arguments:
  1881. ppPtnList - Pointer to the address of the first partition in the list
  1882. being freed. The address is set to NULL after the list is freed,
  1883. to prevent further unintended accesses to the freed object.
  1884. Return Value:
  1885. If the function succeeds, the return value is a nonzero value.
  1886. *ppPtnList is set to NULL.
  1887. If the function fails, the return value is zero. To get extended error
  1888. information, call GetLastError().
  1889. Currently, this function always succeeds.
  1890. --*/
  1891. {
  1892. DWORD index = 0,
  1893. numberOfPartitions = 0;
  1894. PASR_PTN_INFO_LIST pList = NULL;
  1895. PASR_PTN_INFO pCurrent = NULL,
  1896. pNext = NULL;
  1897. HANDLE heapHandle = GetProcessHeap();
  1898. if (!ARGUMENT_PRESENT(ppPtnList) || !(*ppPtnList)) {
  1899. return;
  1900. }
  1901. pList = *ppPtnList;
  1902. numberOfPartitions = pList[0].numTotalPtns;
  1903. for (index = 0; index < numberOfPartitions; index++) {
  1904. pCurrent = pList[index].pOffsetHead;
  1905. while (pCurrent) {
  1906. //
  1907. // Save a pointer to the next
  1908. //
  1909. pNext = pCurrent->pOffsetNext;
  1910. //
  1911. // No pointers in PASR_PTN_INFO, okay to free as-is.
  1912. //
  1913. _AsrpHeapFree(pCurrent);
  1914. pCurrent = pNext;
  1915. }
  1916. }
  1917. _AsrpHeapFree(pList);
  1918. *ppPtnList = NULL;
  1919. }
  1920. BOOL
  1921. AsrpWriteVersionSection(
  1922. IN CONST HANDLE SifHandle,
  1923. IN PCWSTR Provider OPTIONAL
  1924. )
  1925. /*++
  1926. Routine Description:
  1927. Creates the VERSION section of the ASR state file, and writes out the
  1928. entries in that section to file.
  1929. Arguments:
  1930. SifHandle - Handle to asr.sif, the ASR state file.
  1931. Provider - Pointer to a null-terminated string containing the name of the
  1932. application creating the asr.sif. The length of this string must
  1933. not exceed (ASR_SIF_ENTRY_MAX_CHARS - ASR_SIF_CCH_PROVIDER_STRING)
  1934. characters.
  1935. This is an optional argument.
  1936. Return Value:
  1937. If the function succeeds, the return value is a nonzero value.
  1938. If the function fails, the return value is zero. To get extended error
  1939. information, call GetLastError().
  1940. --*/
  1941. {
  1942. WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
  1943. DWORD size;
  1944. //
  1945. // Write out the section name
  1946. //
  1947. swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_VERSION_SECTION_NAME);
  1948. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  1949. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  1950. //
  1951. // Section Entries
  1952. //
  1953. wcscpy(infstring, L"Signature=\"$Windows NT$\"\r\n");
  1954. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  1955. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  1956. wcscpy(infstring, L"ASR-Version=\"1.0\"\r\n");
  1957. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  1958. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  1959. if (ARGUMENT_PRESENT(Provider)) {
  1960. if (wcslen(Provider) >
  1961. (ASR_SIF_ENTRY_MAX_CHARS - ASR_SIF_CCH_PROVIDER_STRING)
  1962. ) {
  1963. //
  1964. // This string is too long to fit into one line in asr.sif
  1965. //
  1966. SetLastError(ERROR_INVALID_PARAMETER);
  1967. return FALSE;
  1968. }
  1969. swprintf(infstring, L"%ws\"%.*ws\"\r\n",
  1970. ASR_SIF_PROVIDER_PREFIX,
  1971. (ASR_SIF_ENTRY_MAX_CHARS - ASR_SIF_CCH_PROVIDER_STRING),
  1972. Provider
  1973. );
  1974. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  1975. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  1976. }
  1977. return TRUE;
  1978. }
  1979. BOOL
  1980. AsrpWriteSystemsSection(
  1981. IN CONST HANDLE SifHandle,
  1982. IN CONST PASR_SYSTEM_INFO pSystemInfo
  1983. )
  1984. /*++
  1985. Routine Description:
  1986. Creates the SYSTEMS section of the ASR state file, and writes out the
  1987. entries in that section to file.
  1988. Arguments:
  1989. SifHandle - Handle to asr.sif, the ASR state file.
  1990. pSystemInfo - Pointer to information about the current system.
  1991. Return Value:
  1992. If the function succeeds, the return value is a nonzero value.
  1993. If the function fails, the return value is zero. To get extended error
  1994. information, call GetLastError().
  1995. --*/
  1996. {
  1997. WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
  1998. DWORD size = 0, SKU = 0;
  1999. if ((!pSystemInfo) || (!pSystemInfo->BootDirectory)) {
  2000. //
  2001. // We need a boot directory
  2002. //
  2003. SetLastError(ERROR_BAD_ENVIRONMENT);
  2004. return FALSE;
  2005. }
  2006. //
  2007. // Write out the section name
  2008. //
  2009. swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_SYSTEM_SECTION_NAME);
  2010. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2011. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2012. SKU = (DWORD) (pSystemInfo->OsVersionEx.wProductType);
  2013. SKU = SKU << 16; // shift the ProductType left 2 bytes
  2014. SKU = SKU | (DWORD) (pSystemInfo->OsVersionEx.wSuiteMask);
  2015. //
  2016. // Create the section entry, and write it out to file.
  2017. //
  2018. swprintf(infstring,
  2019. L"1=\"%ws\",\"%ws\",\"%d.%d\",\"%ws\",%d,0x%08x,\"%ld %ld %ld %hd-%hd-%hd-%hd %hd:%02hd:%02hd.%hd %hd-%hd-%hd-%hd %hd:%02hd:%02hd.%hd\",\"%ws\",\"%ws\"\r\n",
  2020. pSystemInfo->ComputerName,
  2021. pSystemInfo->Platform,
  2022. pSystemInfo->OsVersionEx.dwMajorVersion,
  2023. pSystemInfo->OsVersionEx.dwMinorVersion,
  2024. pSystemInfo->BootDirectory,
  2025. ((pSystemInfo->AutoExtendEnabled) ? 1 : 0),
  2026. // Product SKU
  2027. SKU,
  2028. // Time-zone stuff
  2029. pSystemInfo->TimeZoneInformation.Bias,
  2030. pSystemInfo->TimeZoneInformation.StandardBias,
  2031. pSystemInfo->TimeZoneInformation.DaylightBias,
  2032. pSystemInfo->TimeZoneInformation.StandardDate.wYear,
  2033. pSystemInfo->TimeZoneInformation.StandardDate.wMonth,
  2034. pSystemInfo->TimeZoneInformation.StandardDate.wDayOfWeek,
  2035. pSystemInfo->TimeZoneInformation.StandardDate.wDay,
  2036. pSystemInfo->TimeZoneInformation.StandardDate.wHour,
  2037. pSystemInfo->TimeZoneInformation.StandardDate.wMinute,
  2038. pSystemInfo->TimeZoneInformation.StandardDate.wSecond,
  2039. pSystemInfo->TimeZoneInformation.StandardDate.wMilliseconds,
  2040. pSystemInfo->TimeZoneInformation.DaylightDate.wYear,
  2041. pSystemInfo->TimeZoneInformation.DaylightDate.wMonth,
  2042. pSystemInfo->TimeZoneInformation.DaylightDate.wDayOfWeek,
  2043. pSystemInfo->TimeZoneInformation.DaylightDate.wDay,
  2044. pSystemInfo->TimeZoneInformation.DaylightDate.wHour,
  2045. pSystemInfo->TimeZoneInformation.DaylightDate.wMinute,
  2046. pSystemInfo->TimeZoneInformation.DaylightDate.wSecond,
  2047. pSystemInfo->TimeZoneInformation.DaylightDate.wMilliseconds,
  2048. pSystemInfo->TimeZoneInformation.StandardName,
  2049. pSystemInfo->TimeZoneInformation.DaylightName
  2050. );
  2051. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2052. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2053. return TRUE;
  2054. }
  2055. BOOL
  2056. AsrpWriteBusesSection(
  2057. IN CONST HANDLE SifHandle,
  2058. IN CONST PASR_DISK_INFO pDiskList
  2059. )
  2060. /*++
  2061. Routine Description:
  2062. Creates the BUSES section of the ASR state file, and writes out the
  2063. entries in that section to file.
  2064. Arguments:
  2065. SifHandle - Handle to asr.sif, the ASR state file.
  2066. pDiskList - List of disks present on the current system.
  2067. Return Value:
  2068. If the function succeeds, the return value is a nonzero value.
  2069. If the function fails, the return value is zero. To get extended error
  2070. information, call GetLastError().
  2071. --*/
  2072. {
  2073. DWORD size = 0,
  2074. busKey = 1;
  2075. BOOL done = FALSE;
  2076. WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
  2077. PASR_DISK_INFO pCurrentDisk = NULL;
  2078. //
  2079. // Write out the section name
  2080. //
  2081. swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_BUSES_SECTION_NAME);
  2082. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2083. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2084. //
  2085. // Create the list of buses. This routine fills in the SifBusKey field
  2086. // for each disk.
  2087. //
  2088. AsrpDetermineBuses(pDiskList);
  2089. //
  2090. // Go through the list of disks now, and add one entry in asr.sif for each
  2091. // bus present on the system (i.e., each unique SifBusKey value). Note
  2092. // that we won't care about disks for which we couldn't get any bus info--
  2093. // SifBusKey is 0 for such disks, and we start here from SifBusKey == 1.
  2094. //
  2095. // Also, we assume that SifBusKey values have no holes.
  2096. //
  2097. while (!done) {
  2098. done = TRUE; // assume that we've been through all the buses.
  2099. //
  2100. // Start from the beginning of the list
  2101. //
  2102. pCurrentDisk = pDiskList;
  2103. while (pCurrentDisk) {
  2104. if (pCurrentDisk->SifBusKey > busKey) {
  2105. //
  2106. // There are SifBusKeys we haven't covered yet.
  2107. //
  2108. done = FALSE;
  2109. }
  2110. if (pCurrentDisk->SifBusKey == busKey) {
  2111. //
  2112. // This is the SifBusKey we're looking for, so lets write
  2113. // out the bus type to file.
  2114. //
  2115. swprintf(infstring, L"%lu=%d,%lu\r\n",
  2116. busKey,
  2117. ASR_SYSTEM_KEY,
  2118. pCurrentDisk->BusType
  2119. );
  2120. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2121. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2122. //
  2123. // We've already covered this SifBusKey, lets move on to the
  2124. // next.
  2125. //
  2126. ++busKey;
  2127. }
  2128. pCurrentDisk = pCurrentDisk->pNext;
  2129. }
  2130. }
  2131. return TRUE;
  2132. }
  2133. BOOL
  2134. AsrpWriteMbrDisksSection(
  2135. IN CONST HANDLE SifHandle, // handle to the state file
  2136. IN CONST PASR_DISK_INFO pDiskList
  2137. )
  2138. /*++
  2139. Routine Description:
  2140. Creates the DISKS.MBR section of the ASR state file, and writes out the
  2141. entries in that section to file.
  2142. Arguments:
  2143. SifHandle - Handle to asr.sif, the ASR state file.
  2144. pDiskList - List of disks present on the current system.
  2145. Return Value:
  2146. If the function succeeds, the return value is a nonzero value.
  2147. If the function fails, the return value is zero. To get extended error
  2148. information, call GetLastError().
  2149. --*/
  2150. {
  2151. DWORD size = 0,
  2152. diskKey = 1;
  2153. WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
  2154. PASR_DISK_INFO pCurrentDisk = pDiskList;
  2155. //
  2156. // Write out the section name: [DISKS.MBR]
  2157. //
  2158. swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_MBR_DISKS_SECTION_NAME);
  2159. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2160. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2161. //
  2162. // Go through the list of disks, and write one entry for each MBR disk
  2163. // on the list.
  2164. //
  2165. while (pCurrentDisk) {
  2166. if (PARTITION_STYLE_MBR !=
  2167. pCurrentDisk->pDriveLayoutEx->PartitionStyle
  2168. ) {
  2169. //
  2170. // Skip non-MBR (i.e., GPT) disks.
  2171. //
  2172. pCurrentDisk = pCurrentDisk->pNext;
  2173. continue;
  2174. }
  2175. pCurrentDisk->SifDiskKey = diskKey;
  2176. swprintf(infstring, L"%lu=%d,%lu,%lu,0x%08x,%lu,%lu,%lu,%I64u\r\n",
  2177. diskKey,
  2178. ASR_SYSTEM_KEY,
  2179. pCurrentDisk->SifBusKey,
  2180. pCurrentDisk->IsCritical,
  2181. pCurrentDisk->pDriveLayoutEx->Mbr.Signature,
  2182. pCurrentDisk->pDiskGeometry->BytesPerSector,
  2183. pCurrentDisk->pDiskGeometry->SectorsPerTrack,
  2184. pCurrentDisk->pDiskGeometry->TracksPerCylinder,
  2185. (ULONG64)(pCurrentDisk->pPartition0Ex->PartitionLength.QuadPart /
  2186. pCurrentDisk->pDiskGeometry->BytesPerSector)
  2187. );
  2188. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2189. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2190. ++diskKey;
  2191. pCurrentDisk = pCurrentDisk->pNext;
  2192. }
  2193. return TRUE;
  2194. }
  2195. BOOL
  2196. AsrpWriteGptDisksSection(
  2197. IN CONST HANDLE SifHandle, // handle to the state file
  2198. IN CONST PASR_DISK_INFO pDiskList
  2199. )
  2200. /*++
  2201. Routine Description:
  2202. Creates the DISKS.GPT section of the ASR state file, and writes out the
  2203. entries in that section to file.
  2204. Arguments:
  2205. SifHandle - Handle to asr.sif, the ASR state file.
  2206. pDiskList - List of disks present on the current system.
  2207. Return Value:
  2208. If the function succeeds, the return value is a nonzero value.
  2209. If the function fails, the return value is zero. To get extended error
  2210. information, call GetLastError().
  2211. --*/
  2212. {
  2213. DWORD size = 0,
  2214. diskKey = 1;
  2215. PWSTR lpGuidString = NULL;
  2216. RPC_STATUS rpcStatus = RPC_S_OK;
  2217. PASR_DISK_INFO pCurrentDisk = pDiskList;
  2218. WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
  2219. //
  2220. // Write out the section name: [DISKS.GPT]
  2221. //
  2222. swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_GPT_DISKS_SECTION_NAME);
  2223. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2224. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2225. //
  2226. // Go through the list of disks, and write one entry for each GPT disk
  2227. // on the list.
  2228. //
  2229. while (pCurrentDisk) {
  2230. if (PARTITION_STYLE_GPT !=
  2231. pCurrentDisk->pDriveLayoutEx->PartitionStyle
  2232. ) {
  2233. //
  2234. // Skip non-GPT (i.e., MBR) disks.
  2235. //
  2236. pCurrentDisk = pCurrentDisk->pNext;
  2237. continue;
  2238. }
  2239. //
  2240. // Convert the DiskId to a printable string
  2241. //
  2242. rpcStatus = UuidToStringW(
  2243. &pCurrentDisk->pDriveLayoutEx->Gpt.DiskId,
  2244. &lpGuidString
  2245. );
  2246. if (rpcStatus != RPC_S_OK) {
  2247. if (lpGuidString) {
  2248. RpcStringFreeW(&lpGuidString);
  2249. }
  2250. //
  2251. // The only error from UuidToStringW is RPC_S_OUT_OF_MEMORY
  2252. //
  2253. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  2254. return FALSE;
  2255. }
  2256. pCurrentDisk->SifDiskKey = diskKey;
  2257. swprintf(infstring, L"%lu=%d,%lu,%lu,%ws%ws%ws,%lu,%lu,%lu,%lu,%I64u\r\n",
  2258. diskKey,
  2259. ASR_SYSTEM_KEY,
  2260. pCurrentDisk->SifBusKey,
  2261. pCurrentDisk->IsCritical,
  2262. (lpGuidString ? L"\"" : L""),
  2263. (lpGuidString ? lpGuidString : L""),
  2264. (lpGuidString ? L"\"" : L""),
  2265. pCurrentDisk->pDriveLayoutEx->Gpt.MaxPartitionCount,
  2266. pCurrentDisk->pDiskGeometry->BytesPerSector,
  2267. pCurrentDisk->pDiskGeometry->SectorsPerTrack,
  2268. pCurrentDisk->pDiskGeometry->TracksPerCylinder,
  2269. (ULONG64) (pCurrentDisk->pPartition0Ex->PartitionLength.QuadPart /
  2270. pCurrentDisk->pDiskGeometry->BytesPerSector)
  2271. );
  2272. _AsrpCheckTrue(WriteFile(SifHandle, infstring, wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2273. if (lpGuidString) {
  2274. RpcStringFreeW(&lpGuidString);
  2275. lpGuidString = NULL;
  2276. }
  2277. ++diskKey;
  2278. pCurrentDisk = pCurrentDisk->pNext;
  2279. }
  2280. return TRUE;
  2281. }
  2282. BOOL
  2283. AsrpWriteMbrPartitionsSection(
  2284. IN CONST HANDLE SifHandle, // handle to the state file
  2285. IN CONST PASR_DISK_INFO pDiskList,
  2286. IN CONST PASR_SYSTEM_INFO pSystemInfo
  2287. )
  2288. /*++
  2289. Routine Description:
  2290. Creates the PARTITIONS.MBR section of the ASR state file, and writes
  2291. out the entries in that section to file.
  2292. Arguments:
  2293. SifHandle - Handle to asr.sif, the ASR state file.
  2294. pDiskList - List of disks present on the current system.
  2295. pSystemInfo - Info about the current system, used to determine the current
  2296. boot and system partitions (and mark them appropriately in
  2297. asr.sif)
  2298. Return Value:
  2299. If the function succeeds, the return value is a nonzero value.
  2300. If the function fails, the return value is zero. To get extended error
  2301. information, call GetLastError().
  2302. --*/
  2303. {
  2304. DWORD size = 0,
  2305. index = 0,
  2306. partitionKey = 1;
  2307. UCHAR fsType = 0;
  2308. PWSTR volumeGuid = NULL;
  2309. BOOL writeVolumeGuid = FALSE;
  2310. PASR_DISK_INFO pCurrentDisk = pDiskList;
  2311. WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
  2312. PPARTITION_INFORMATION_EX currentPartitionEx = NULL;
  2313. //
  2314. // Write out the section name: [PARTITIONS.MBR]
  2315. //
  2316. swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_MBR_PARTITIONS_SECTION_NAME);
  2317. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2318. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2319. //
  2320. // Go through the list of disks, and write one entry for each partition on
  2321. // each of the MBR disks on the list.
  2322. //
  2323. while (pCurrentDisk) {
  2324. if (pCurrentDisk->pDriveLayoutEx) {
  2325. if (PARTITION_STYLE_MBR !=
  2326. pCurrentDisk->pDriveLayoutEx->PartitionStyle
  2327. ) {
  2328. //
  2329. // Skip non-MBR (i.e., GPT) disks
  2330. //
  2331. pCurrentDisk = pCurrentDisk->pNext;
  2332. continue;
  2333. }
  2334. //
  2335. // Enumerate partitions on the disk. We expect to find only
  2336. // MBR partitions.
  2337. //
  2338. for (index =0;
  2339. index < pCurrentDisk->pDriveLayoutEx->PartitionCount;
  2340. index++
  2341. ) {
  2342. currentPartitionEx =
  2343. &pCurrentDisk->pDriveLayoutEx->PartitionEntry[index];
  2344. MYASSERT(currentPartitionEx->PartitionStyle ==
  2345. PARTITION_STYLE_MBR);
  2346. if (currentPartitionEx->Mbr.PartitionType == 0) {
  2347. //
  2348. // Empty partition table entry.
  2349. //
  2350. continue;
  2351. }
  2352. fsType =
  2353. pCurrentDisk->PartitionInfoTable[index].FileSystemType;
  2354. volumeGuid =
  2355. pCurrentDisk->PartitionInfoTable[index].szVolumeGuid;
  2356. //
  2357. // We only want to write out the Volume GUID for basic
  2358. // (recognized) partitions/volumes, since it does not make
  2359. // sense in the context of LDM or other unknown partition
  2360. // types which would need special handling from their
  2361. // respective recovery agents such as asr_ldm in GUI-mode
  2362. // Setup.
  2363. //
  2364. writeVolumeGuid = (wcslen(volumeGuid) > 0) &&
  2365. IsRecognizedPartition(currentPartitionEx->Mbr.PartitionType);
  2366. //
  2367. // Create the entry and write it to file.
  2368. //
  2369. swprintf(
  2370. infstring,
  2371. L"%d=%d,%d,%lu,%ws%ws%ws,0x%02x,0x%02x,0x%02x,%I64u,%I64u,0x%x\r\n",
  2372. partitionKey,
  2373. pCurrentDisk->SifDiskKey,
  2374. index,
  2375. pCurrentDisk->PartitionInfoTable[index].PartitionFlags,
  2376. (writeVolumeGuid ? L"\"" : L""),
  2377. (writeVolumeGuid ? volumeGuid : L""),
  2378. (writeVolumeGuid ? L"\"" : L""),
  2379. (currentPartitionEx->Mbr.BootIndicator)?0x80:0,
  2380. currentPartitionEx->Mbr.PartitionType,
  2381. ((fsType) ? fsType :
  2382. currentPartitionEx->Mbr.PartitionType),
  2383. (ULONG64) ((currentPartitionEx->StartingOffset.QuadPart)/
  2384. (pCurrentDisk->pDiskGeometry->BytesPerSector)),
  2385. (ULONG64) ((currentPartitionEx->PartitionLength.QuadPart)/
  2386. (pCurrentDisk->pDiskGeometry->BytesPerSector)),
  2387. pCurrentDisk->PartitionInfoTable[index].ClusterSize
  2388. );
  2389. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2390. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2391. ++partitionKey;
  2392. }
  2393. }
  2394. pCurrentDisk = pCurrentDisk->pNext;
  2395. }
  2396. return TRUE;
  2397. }
  2398. BOOL
  2399. AsrpWriteGptPartitionsSection(
  2400. IN CONST HANDLE SifHandle,
  2401. IN CONST PASR_DISK_INFO pDiskList,
  2402. IN CONST PASR_SYSTEM_INFO pSystemInfo
  2403. )
  2404. /*++
  2405. Routine Description:
  2406. Creates the PARTITIONS.GPT section of the ASR state file, and writes
  2407. out the entries in that section to file.
  2408. Arguments:
  2409. SifHandle - Handle to asr.sif, the ASR state file.
  2410. pDiskList - List of disks present on the current system.
  2411. pSystemInfo - Info about the current system, used to determine the current
  2412. boot and system partitions (and mark them appropriately in
  2413. asr.sif)
  2414. Return Value:
  2415. If the function succeeds, the return value is a nonzero value.
  2416. If the function fails, the return value is zero. To get extended error
  2417. information, call GetLastError().
  2418. --*/
  2419. {
  2420. DWORD size = 0,
  2421. index = 0,
  2422. partitionKey = 1;
  2423. UCHAR fsType = 0;
  2424. PWSTR volumeGuid = NULL,
  2425. partitionId = NULL,
  2426. partitionType = NULL;
  2427. BOOL writeVolumeGuid = FALSE;
  2428. RPC_STATUS rpcStatus = RPC_S_OK;
  2429. PASR_DISK_INFO pCurrentDisk = pDiskList;
  2430. WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
  2431. PPARTITION_INFORMATION_EX currentPartitionEx = NULL;
  2432. //
  2433. // Write out the section name: [PARTITIONS.GPT]
  2434. //
  2435. swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_GPT_PARTITIONS_SECTION_NAME);
  2436. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2437. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2438. //
  2439. // Go through the list of disks, and write one entry for each partition on
  2440. // each of the GPT disks on the list.
  2441. //
  2442. while (pCurrentDisk) {
  2443. if (pCurrentDisk->pDriveLayoutEx) {
  2444. if (PARTITION_STYLE_GPT !=
  2445. pCurrentDisk->pDriveLayoutEx->PartitionStyle
  2446. ) {
  2447. //
  2448. // Skip non-GPT (i.e., MBR) disks.
  2449. //
  2450. pCurrentDisk = pCurrentDisk->pNext;
  2451. continue;
  2452. }
  2453. //
  2454. // Enumerate partitions on the disk. We expect to find only
  2455. // GPT partitions.
  2456. //
  2457. for (index =0;
  2458. index < pCurrentDisk->pDriveLayoutEx->PartitionCount;
  2459. index++) {
  2460. currentPartitionEx =
  2461. &pCurrentDisk->pDriveLayoutEx->PartitionEntry[index];
  2462. MYASSERT(currentPartitionEx->PartitionStyle ==
  2463. PARTITION_STYLE_GPT);
  2464. //
  2465. // Convert the Guids to printable strings
  2466. //
  2467. rpcStatus = UuidToStringW(
  2468. &currentPartitionEx->Gpt.PartitionType,
  2469. &partitionType
  2470. );
  2471. if (rpcStatus != RPC_S_OK) {
  2472. if (partitionType) {
  2473. RpcStringFreeW(&partitionType);
  2474. partitionType = NULL;
  2475. }
  2476. //
  2477. // The only error from UuidToString is RPC_S_OUT_OF_MEMORY
  2478. //
  2479. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  2480. return FALSE;
  2481. }
  2482. rpcStatus = UuidToStringW(
  2483. &currentPartitionEx->Gpt.PartitionId,
  2484. &partitionId
  2485. );
  2486. if (rpcStatus != RPC_S_OK) {
  2487. if (partitionType) {
  2488. RpcStringFreeW(&partitionType);
  2489. partitionType = NULL;
  2490. }
  2491. if (partitionId) {
  2492. RpcStringFreeW(&partitionId);
  2493. partitionId = NULL;
  2494. }
  2495. //
  2496. // The only error from UuidToString is RPC_S_OUT_OF_MEMORY
  2497. //
  2498. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  2499. return FALSE;
  2500. }
  2501. fsType =
  2502. pCurrentDisk->PartitionInfoTable[index].FileSystemType;
  2503. volumeGuid =
  2504. pCurrentDisk->PartitionInfoTable[index].szVolumeGuid;
  2505. //
  2506. // We only want to write out the Volume GUID for basic
  2507. // (recognized) partitions/volumes, since it does not make
  2508. // sense in the context of LDM or other unknown partition
  2509. // types which would need special handling from their
  2510. // respective recovery agents such as asr_ldm in GUI-mode
  2511. // Setup.
  2512. //
  2513. writeVolumeGuid = (wcslen(volumeGuid) > 0) &&
  2514. IsEqualGUID(&(partitionType), &(PARTITION_BASIC_DATA_GUID));
  2515. //
  2516. // Create the entry and write it to file.
  2517. //
  2518. swprintf(
  2519. infstring,
  2520. L"%d=%d,%d,%d,%ws%ws%ws,%ws%ws%ws,%ws%ws%ws,0x%I64x,%ws%ws%ws,0x%02x,%I64u,%I64u,0x%x\r\n",
  2521. partitionKey,
  2522. pCurrentDisk->SifDiskKey,
  2523. index, //slot-index
  2524. pCurrentDisk->PartitionInfoTable[index].PartitionFlags,
  2525. (writeVolumeGuid ? L"\"" : L""),
  2526. (writeVolumeGuid ? volumeGuid : L""),
  2527. (writeVolumeGuid ? L"\"" : L""),
  2528. (partitionType ? L"\"" : L""),
  2529. (partitionType ? partitionType : L""),
  2530. (partitionType ? L"\"" : L""),
  2531. (partitionId ? L"\"" : L""),
  2532. (partitionId ? partitionId : L""),
  2533. (partitionId ? L"\"" : L""),
  2534. currentPartitionEx->Gpt.Attributes,
  2535. (currentPartitionEx->Gpt.Name ? L"\"" : L""),
  2536. (currentPartitionEx->Gpt.Name ?
  2537. currentPartitionEx->Gpt.Name : L""),
  2538. (currentPartitionEx->Gpt.Name ? L"\"" : L""),
  2539. //
  2540. // ISSUE-2000/04/12-guhans: GetVolumeInformation does not
  2541. // work on GPT and fstype is always zero
  2542. //
  2543. fsType,
  2544. (ULONG64) ((currentPartitionEx->StartingOffset.QuadPart)/
  2545. (pCurrentDisk->pDiskGeometry->BytesPerSector)),
  2546. (ULONG64) ((currentPartitionEx->PartitionLength.QuadPart)/
  2547. (pCurrentDisk->pDiskGeometry->BytesPerSector)),
  2548. pCurrentDisk->PartitionInfoTable[index].ClusterSize
  2549. );
  2550. _AsrpCheckTrue(WriteFile(SifHandle, infstring,
  2551. wcslen(infstring)*sizeof(WCHAR), &size, NULL));
  2552. if (partitionType) {
  2553. RpcStringFreeW(&partitionType);
  2554. partitionType = NULL;
  2555. }
  2556. if (partitionId) {
  2557. RpcStringFreeW(&partitionId);
  2558. partitionId = NULL;
  2559. }
  2560. ++partitionKey;
  2561. }
  2562. }
  2563. pCurrentDisk = pCurrentDisk->pNext;
  2564. }
  2565. return TRUE;
  2566. }
  2567. BOOL
  2568. AsrpCreateEnvironmentBlock(
  2569. IN PCWSTR CriticalVolumeList,
  2570. IN HANDLE SifHandle,
  2571. OUT PWSTR *NewBlock
  2572. )
  2573. /*++
  2574. Routine Description:
  2575. Creates a new environment block that is passed in to apps launched as part
  2576. of an ASR backup. This routine retrieves the current process's
  2577. environment block, adds the ASR environment variables to it, and creates a
  2578. multi-sz suitable for being passed in as the lpEnvironment parameter of
  2579. CreateProcess.
  2580. Arguments:
  2581. CriticalVolumeList - A multi-string containing a list of the volume GUID's
  2582. of each of the critical volumes present on the system. The GUID's
  2583. must be in the NT name-space, i.e., must be of the form:
  2584. \??\Volume{GUID}
  2585. This multi-sz is used to create the semi-colon separated list of
  2586. volumes in the "_AsrCriticalVolumeList" variable in NewBlock.
  2587. SifHandle - A (duplicate) handle to asr.sif, the ASR state file. This is
  2588. used in creating the "_AsrContext" variable in NewBlock.
  2589. NewBlock - Receives the new environment block. In addition to all the
  2590. environment variables in the current process environment block,
  2591. this block contains two additional "ASR" variables:
  2592. _AsrContext=<DWORD_PTR value>
  2593. _AsrCriticalVolumeList=<volumeguid>;<volumeguid>;...;<volumeguid>
  2594. The caller is responsible for freeing this block when it is no
  2595. longer needed, using HeapFree(GetProcessHeap(),...).
  2596. Return Value:
  2597. If the function succeeds, the return value is a nonzero value.
  2598. If the function fails, the return value is zero. To get extended error
  2599. information, call GetLastError(). (*NewBlock) is set to NULL.
  2600. --*/
  2601. {
  2602. PCWSTR lpTemp = CriticalVolumeList;
  2603. PWSTR lpCurrentEnvStrings = NULL;
  2604. DWORD cchContextEntry = 0,
  2605. cchEnvBlock = 0,
  2606. cbEnvBlock = 0,
  2607. cbCurrentProcessEnvBlock = 0,
  2608. status = ERROR_SUCCESS;
  2609. HANDLE heapHandle = GetProcessHeap();
  2610. MYASSERT(NewBlock);
  2611. //
  2612. // Find out how much space the environment block will need
  2613. //
  2614. //
  2615. // For _AsrContext=1234 and _AsrCriticalVolumes="..." entries
  2616. //
  2617. lpTemp = CriticalVolumeList;
  2618. if (CriticalVolumeList) {
  2619. while (*lpTemp) {
  2620. lpTemp += (wcslen(lpTemp) + 1);
  2621. }
  2622. }
  2623. cbEnvBlock = (DWORD) ((lpTemp - CriticalVolumeList + 1) * sizeof(WCHAR));
  2624. cbEnvBlock += ASR_CCH_ENVBLOCK_ASR_ENTRIES * sizeof(WCHAR);
  2625. //
  2626. // For all the current environment strings
  2627. //
  2628. lpCurrentEnvStrings = GetEnvironmentStringsW();
  2629. lpTemp = lpCurrentEnvStrings;
  2630. if (lpCurrentEnvStrings ) {
  2631. while (*lpTemp) {
  2632. lpTemp += (wcslen(lpTemp) + 1);
  2633. }
  2634. }
  2635. cbCurrentProcessEnvBlock = (DWORD) ((lpTemp - lpCurrentEnvStrings + 1) * sizeof(WCHAR));
  2636. cbEnvBlock += cbCurrentProcessEnvBlock;
  2637. //
  2638. // And allocate the space
  2639. //
  2640. *NewBlock = (PWSTR) HeapAlloc(
  2641. heapHandle,
  2642. HEAP_ZERO_MEMORY,
  2643. cbEnvBlock
  2644. );
  2645. _AsrpErrExitCode(!(*NewBlock), status, ERROR_NOT_ENOUGH_MEMORY);
  2646. //
  2647. // First, add the AsrContext=1234 entry in the environment block
  2648. //
  2649. swprintf(
  2650. (*NewBlock),
  2651. ASR_ENVBLOCK_CONTEXT_ENTRY,
  2652. (ULONG64) (SifHandle)
  2653. );
  2654. //
  2655. // Keep track of where this entry ends, so we can add a NULL at this
  2656. // index later.
  2657. //
  2658. cchContextEntry = wcslen((*NewBlock));
  2659. wcscat((*NewBlock), L" "); // this character will be replaced by a NULL later
  2660. //
  2661. // Append each critical volume GUID, separated by a semi-colon.
  2662. //
  2663. wcscat((*NewBlock), ASR_ENVBLOCK_CRITICAL_VOLUME_ENTRY);
  2664. if (CriticalVolumeList) {
  2665. lpTemp = CriticalVolumeList;
  2666. while (*lpTemp) {
  2667. wcscat((*NewBlock), lpTemp);
  2668. wcscat((*NewBlock), L";");
  2669. lpTemp += (wcslen(lpTemp) + 1);
  2670. }
  2671. }
  2672. else {
  2673. wcscat((*NewBlock), L";");
  2674. }
  2675. //
  2676. // Mark the end with two NULL's
  2677. //
  2678. cchEnvBlock = wcslen(*NewBlock) - 1;
  2679. // (*NewBlock)[cchEnvBlock - 1] = L'"';
  2680. (*NewBlock)[cchEnvBlock] = L'\0';
  2681. //
  2682. // Separate the two entries with a NULL
  2683. //
  2684. (*NewBlock)[cchContextEntry] = L'\0';
  2685. //
  2686. // Copy over the current environment strings
  2687. //
  2688. RtlCopyMemory(&(*NewBlock)[cchEnvBlock + 1],
  2689. lpCurrentEnvStrings,
  2690. cbCurrentProcessEnvBlock
  2691. );
  2692. EXIT:
  2693. if (lpCurrentEnvStrings) {
  2694. FreeEnvironmentStringsW(lpCurrentEnvStrings);
  2695. lpCurrentEnvStrings = NULL;
  2696. }
  2697. if (ERROR_SUCCESS != status) {
  2698. _AsrpHeapFree((*NewBlock));
  2699. }
  2700. return (BOOL) (ERROR_SUCCESS == status);
  2701. }
  2702. BOOL
  2703. AsrpLaunchRegisteredCommands(
  2704. IN HANDLE SifHandle,
  2705. IN PCWSTR CriticalVolumeList
  2706. )
  2707. /*++
  2708. Routine Description:
  2709. This launches apps that have registered to be part of an ASR-backup. The
  2710. commands are read from the following ASR-Commands key:
  2711. "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Asr\\Commands"
  2712. This key contains a REG_EXPAND_SZ entry for each application to be
  2713. launched, with the data containing the full command-line to be invoked:
  2714. ApplicationName::REG_EXPAND_SZ::<Command-line with parameters>
  2715. Such as:
  2716. ASR utility::REG_EXPAND_SZ::"%systemroot%\\system32\\asr_fmt.exe /backup"
  2717. When invoking the app, we expand out all the environment variables in the
  2718. command-line. In addition, we append a "context" parameter to the command
  2719. line, which the app is expected to use in calls to AsrAddSifEntry.
  2720. The above entry would thus translate something like:
  2721. c:\windows\system32\asr_fmt.exe /backup /context=2000
  2722. The environment block of the process is a duplicate of the current process
  2723. environment block, with one exception--it contains two additional "Asr"
  2724. variables:
  2725. _AsrContext=<DWORD_PTR value>
  2726. _AsrCriticalVolumeList=<volumeguid>;<volumeguid>;...;<volumeguid>
  2727. Each application invoked must complete in the allowed time-out value.
  2728. The time-out is configurable in the registry, by changing the value of the
  2729. "ProcessTimeOut" value under the ASR key. We ship with a default of 3600
  2730. seconds, but the sys-admin can change it if needed. (0=infinite).
  2731. Arguments:
  2732. SifHandle - A handle to asr.sif, the ASR state file. A duplicate of this
  2733. handle is passed in to applications as the "context" parameter,
  2734. and as the "_AsrContext" variable in the environment block.
  2735. CriticalVolumeList - A multi-string containing a list of the volume GUID's
  2736. of each of the critical volumes present on the system. The GUID's
  2737. must be in the NT name-space, i.e., must be of the form:
  2738. \??\Volume{GUID}
  2739. This multi-sz is used to create the semi-colon separated list of
  2740. volumes in the "_AsrCriticalVolumeList" variable in the env
  2741. block of the new processes.
  2742. Applications (such as volume-managers) can use this list to
  2743. determine if they manage any critical volumes, and make a note of
  2744. it in the asr.sif. This way, they can intelligently decide to
  2745. abort the ASR restore process if needed.
  2746. Return Value:
  2747. If the function succeeds, the return value is a nonzero value. This
  2748. implies that all the applications invoked were successful (i.e.,
  2749. returned an exit code of 0).
  2750. If the function fails, the return value is zero. To get extended error
  2751. information, call GetLastError().
  2752. Note that if any of the applications returned an exit code other than 0,
  2753. we interpret that as a fatal error, and will return an error.
  2754. --*/
  2755. {
  2756. HKEY regKey = NULL;
  2757. DWORD status = ERROR_SUCCESS,
  2758. waitResult = WAIT_ABANDONED,
  2759. lpcValues = 0L,
  2760. index = 0L,
  2761. cbData = 0L,
  2762. cbMaxDataLen = 0L,
  2763. cchValueName = 0L,
  2764. cchMaxValueLen = 0L,
  2765. cbCommand = 0L,
  2766. cchReqd = 0L,
  2767. timeLeft = 0L,
  2768. maxTimeOutValue = 0L;
  2769. HANDLE heapHandle = NULL,
  2770. processHandle = NULL,
  2771. dupSifHandle = NULL;
  2772. PWSTR valueName = NULL,
  2773. data = NULL,
  2774. command = NULL,
  2775. lpEnvBlock = NULL;
  2776. WCHAR cmdLineSuffix[ASR_COMMANDLINE_SUFFIX_LEN + 1];
  2777. BOOL result = FALSE;
  2778. STARTUPINFOW startUpInfo;
  2779. PROCESS_INFORMATION processInfo;
  2780. heapHandle = GetProcessHeap();
  2781. processHandle = GetCurrentProcess();
  2782. MYASSERT(heapHandle && processHandle);
  2783. ZeroMemory(cmdLineSuffix, (ASR_COMMANDLINE_SUFFIX_LEN + 1) * sizeof(WCHAR));
  2784. ZeroMemory(&startUpInfo, sizeof(STARTUPINFOW));
  2785. ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));
  2786. //
  2787. // Get the time out value for processes, if set in the registry
  2788. // If the key is missing, or is set to "0", the timeout is set
  2789. // to INFINITE.
  2790. //
  2791. status = RegOpenKeyExW(
  2792. HKEY_LOCAL_MACHINE, // hKey
  2793. ASR_REGKEY_ASR, // lpSubKey
  2794. 0, // ulOptions--Reserved, must be 0
  2795. MAXIMUM_ALLOWED, // samDesired
  2796. &regKey // phkResult
  2797. );
  2798. if ((regKey) && (ERROR_SUCCESS == status)) {
  2799. DWORD type = 0L,
  2800. timeOut = 0L,
  2801. cbTimeOut = (sizeof(DWORD));
  2802. status = RegQueryValueExW(
  2803. regKey, // hKey
  2804. ASR_REGVALUE_TIMEOUT, // lpValueName
  2805. NULL, // lpReserved
  2806. &type, // lpType
  2807. (LPBYTE) &timeOut, // lpData
  2808. &cbTimeOut // lpcbData
  2809. );
  2810. if ((ERROR_SUCCESS == status) && (REG_DWORD == type)) {
  2811. maxTimeOutValue = timeOut;
  2812. }
  2813. }
  2814. if (regKey) {
  2815. RegCloseKey(regKey);
  2816. regKey = NULL;
  2817. }
  2818. //
  2819. // Open and enumerate the entries in the ASR command key. If
  2820. // the key doesn't exist, we don't have to execute anything
  2821. //
  2822. status = RegOpenKeyExW(
  2823. HKEY_LOCAL_MACHINE, // hKey
  2824. ASR_REGKEY_ASR_COMMANDS, // lpSubKey
  2825. 0, // ulOptions--Reserved, must be 0
  2826. MAXIMUM_ALLOWED, // samDesired
  2827. &regKey // phkResult
  2828. );
  2829. if ((!regKey) || (ERROR_SUCCESS != status)) {
  2830. return TRUE;
  2831. }
  2832. //
  2833. // Get the max ValueName and Data entries, and
  2834. // allocate memory for them
  2835. //
  2836. status = RegQueryInfoKey(
  2837. regKey,
  2838. NULL, // class
  2839. NULL, // lpcClass
  2840. NULL, // lpReserved
  2841. NULL, // lpcSubKeys
  2842. NULL, // lpcMaxSubKeyLen
  2843. NULL, // lpcMaxClassLen,
  2844. &lpcValues, // number of values
  2845. &cchMaxValueLen, // max value length, in cch
  2846. &cbMaxDataLen, // max data length, in cb
  2847. NULL, // lpcbSecurityDescriptor
  2848. NULL // lpftLastWriteTime
  2849. );
  2850. _AsrpErrExitCode((ERROR_SUCCESS != status), status, status);
  2851. _AsrpErrExitCode((0 == lpcValues), status, ERROR_SUCCESS); // Key is empty, we're done
  2852. valueName = (PWSTR) HeapAlloc(
  2853. heapHandle,
  2854. HEAP_ZERO_MEMORY,
  2855. (cchMaxValueLen + 1) * sizeof (WCHAR) // cch not cb
  2856. );
  2857. _AsrpErrExitCode(!valueName, status, ERROR_NOT_ENOUGH_MEMORY);
  2858. data = (PWSTR) HeapAlloc(
  2859. heapHandle,
  2860. HEAP_ZERO_MEMORY,
  2861. cbMaxDataLen + ((ASR_COMMANDLINE_SUFFIX_LEN + 2) * sizeof(WCHAR))
  2862. );
  2863. _AsrpErrExitCode(!data, status, ERROR_NOT_ENOUGH_MEMORY);
  2864. //
  2865. // "command" will contain the full command string, after any environment
  2866. // variables (eg %systemroot%) in "data" have been expanded. We'll start
  2867. // off with "command" being MAX_PATH characters longer than "data", and
  2868. // we'll re-allocate a bigger buffer if/when needed
  2869. //
  2870. cbCommand = cbMaxDataLen +
  2871. ((ASR_COMMANDLINE_SUFFIX_LEN + MAX_PATH + 2) * sizeof(WCHAR));
  2872. command = (PWSTR) HeapAlloc(
  2873. heapHandle,
  2874. HEAP_ZERO_MEMORY,
  2875. cbCommand
  2876. );
  2877. _AsrpErrExitCode(!command, status, ERROR_NOT_ENOUGH_MEMORY);
  2878. do {
  2879. cchValueName = cchMaxValueLen + 1;
  2880. cbData = cbMaxDataLen + sizeof(WCHAR);
  2881. //
  2882. // Enumerate the commands, and execute them one after the other
  2883. //
  2884. status = RegEnumValueW(
  2885. regKey, // hKey
  2886. index++, // dwIndex
  2887. valueName, // lpValueName
  2888. &cchValueName, // lpcValueName
  2889. NULL, // lpReserved
  2890. NULL, // lpType
  2891. (LPBYTE)data, // lpData
  2892. &cbData // lpcbData
  2893. );
  2894. _AsrpErrExitCode((ERROR_NO_MORE_ITEMS == status),
  2895. status,
  2896. ERROR_SUCCESS
  2897. ); // done with enum
  2898. _AsrpErrExitCode((ERROR_SUCCESS != status), status, status);
  2899. //
  2900. // Create a copy of the sif handle to pass to the app launched.
  2901. // We clean-up close the handle after the app is done.
  2902. //
  2903. result = DuplicateHandle(
  2904. processHandle,
  2905. SifHandle,
  2906. processHandle,
  2907. &dupSifHandle,
  2908. 0L,
  2909. TRUE,
  2910. DUPLICATE_SAME_ACCESS
  2911. );
  2912. _AsrpErrExitCode((!result), status, GetLastError());
  2913. //
  2914. // Append the "/context=<duplicate-sif-handle>" to
  2915. // the command line
  2916. //
  2917. swprintf(cmdLineSuffix,
  2918. ASR_COMMANDLINE_SUFFIX,
  2919. (ULONG64)(dupSifHandle)
  2920. );
  2921. wcscat(data, cmdLineSuffix);
  2922. //
  2923. // Expand any environment strings in the command line
  2924. //
  2925. cchReqd = ExpandEnvironmentStringsW(data,
  2926. command,
  2927. (cbCommand / sizeof(WCHAR))
  2928. );
  2929. _AsrpErrExitCode((!cchReqd), status, GetLastError());
  2930. if ((cchReqd * sizeof(WCHAR)) > cbCommand) {
  2931. //
  2932. // Our "command" buffer wasn't big enough, re-allocate as needed
  2933. //
  2934. _AsrpHeapFree(command);
  2935. cbCommand = ((cchReqd + 1) * sizeof(WCHAR));
  2936. command = HeapAlloc(heapHandle, HEAP_ZERO_MEMORY, cbCommand);
  2937. _AsrpErrExitCode(!command, status, ERROR_NOT_ENOUGH_MEMORY);
  2938. //
  2939. // Try expanding the env strings again ...
  2940. //
  2941. cchReqd = ExpandEnvironmentStringsW(data,
  2942. command,
  2943. (cbCommand / sizeof(WCHAR))
  2944. );
  2945. _AsrpErrExitCode(
  2946. ((!cchReqd) || (cchReqd * sizeof(WCHAR)) > cbCommand),
  2947. status,
  2948. GetLastError()
  2949. );
  2950. }
  2951. //
  2952. // Create the environment block to be passed to the
  2953. // process being launched. The environment block
  2954. // contains the entries:
  2955. // _AsrCriticalVolumes=\??\Volume{Guid1};\??\Volume{Guid2}
  2956. // _AsrContext=<duplicate-sif-handle>
  2957. //
  2958. // in addition to all the environment strings in the current process.
  2959. //
  2960. result = AsrpCreateEnvironmentBlock(CriticalVolumeList,
  2961. dupSifHandle,
  2962. &lpEnvBlock
  2963. );
  2964. _AsrpErrExitCode((!result), status, GetLastError());
  2965. //
  2966. // Execute the command as a separate process
  2967. //
  2968. memset(&startUpInfo, 0L, sizeof (startUpInfo));
  2969. result = CreateProcessW(
  2970. NULL, // lpApplicationName
  2971. command, // lpCommandLine
  2972. NULL, // lpProcessAttributes
  2973. NULL, // lpThreadAttributes
  2974. TRUE, // bInheritHandles
  2975. CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
  2976. lpEnvBlock, // new environment block
  2977. NULL, // current directory name (null=current dir)
  2978. &startUpInfo, // statup information
  2979. &processInfo // process information
  2980. );
  2981. _AsrpErrExitCode((!result),
  2982. status,
  2983. GetLastError()
  2984. ); // process couldn't be launched
  2985. //
  2986. // Process was launched: start the timer countdown if a maximum
  2987. // timeout was specified in the registry. Loop till either the
  2988. // process completes, or the timer expires
  2989. //
  2990. timeLeft = maxTimeOutValue;
  2991. if (timeLeft) {
  2992. do {
  2993. waitResult = WaitForSingleObject(processInfo.hProcess, 1000); // 1000 ms = 1 sec
  2994. --timeLeft;
  2995. } while ((WAIT_TIMEOUT == waitResult) && (timeLeft));
  2996. if (!timeLeft) {
  2997. //
  2998. // The process did not terminate in the allowed time. We treat
  2999. // this as a fatal error--terminate the process, and set its
  3000. // error code to ERROR_TIMEOUT
  3001. //
  3002. TerminateProcess(processInfo.hProcess, ERROR_TIMEOUT);
  3003. }
  3004. }
  3005. else {
  3006. //
  3007. // No timeout was specified in the registry, wait for process to
  3008. // complete.
  3009. //
  3010. waitResult = WaitForSingleObject(processInfo.hProcess, INFINITE);
  3011. }
  3012. //
  3013. // Check if the wait failed above. If last error is something useful,
  3014. // we don't want to destroy it--if it's ERROR_SUCCESS, we'll set it to
  3015. // ERROR_TIMEOUT
  3016. //
  3017. status = GetLastError();
  3018. _AsrpErrExitCode((WAIT_OBJECT_0!=waitResult), status,
  3019. (ERROR_SUCCESS == status ? ERROR_TIMEOUT : status)); // wait failed above
  3020. //
  3021. // Get the process's exit code: if it doesn't return ERROR_SUCCESS,
  3022. // we exit the loop, set the last error to the error returned,
  3023. // and return FALSE
  3024. //
  3025. GetExitCodeProcess(processInfo.hProcess, &status);
  3026. _AsrpErrExitCode((ERROR_SUCCESS != status), status, status);
  3027. _AsrpCloseHandle(dupSifHandle);
  3028. _AsrpHeapFree(lpEnvBlock);
  3029. } while (ERROR_SUCCESS == status);
  3030. EXIT:
  3031. //
  3032. // Clean-up
  3033. //
  3034. if (regKey) {
  3035. RegCloseKey(regKey);
  3036. regKey = NULL;
  3037. }
  3038. _AsrpCloseHandle(dupSifHandle);
  3039. _AsrpHeapFree(valueName);
  3040. _AsrpHeapFree(data);
  3041. _AsrpHeapFree(command);
  3042. _AsrpHeapFree(lpEnvBlock);
  3043. if (ERROR_SUCCESS != status) {
  3044. SetLastError(status);
  3045. return FALSE;
  3046. }
  3047. else {
  3048. return TRUE;
  3049. }
  3050. }
  3051. BOOL
  3052. AsrpIsSupportedConfiguration(
  3053. IN CONST PASR_DISK_INFO pDiskList,
  3054. IN CONST PASR_SYSTEM_INFO pSystemInfo
  3055. )
  3056. /*++
  3057. Routine Description:
  3058. Checks if ASR backup can be performed on the system. We do not support
  3059. systems that have:
  3060. - PROCESSOR_ARCHITECTURE other than "x86", "amd64", or "ia64"
  3061. - any FT volumes present anywhere on the system
  3062. Arguments:
  3063. pDiskList - The list of disks on the system.
  3064. pSystemInfo - System information for this system.
  3065. Return Value:
  3066. If we support this ASR configuration, the return value is non-zero.
  3067. If this configuration is not supported, the return value is zero.
  3068. GetLastError() will return ERROR_NOT_SUPPORTED.
  3069. --*/
  3070. {
  3071. PASR_DISK_INFO pCurrentDisk = pDiskList;
  3072. ULONG index;
  3073. //
  3074. // 1. platform must be x86, amd64, or ia64
  3075. //
  3076. if (wcscmp(pSystemInfo->Platform, ASR_PLATFORM_X86) &&
  3077. wcscmp(pSystemInfo->Platform, ASR_PLATFORM_AMD64) &&
  3078. wcscmp(pSystemInfo->Platform, ASR_PLATFORM_IA64)) {
  3079. SetLastError(ERROR_NOT_SUPPORTED);
  3080. return FALSE;
  3081. }
  3082. //
  3083. // 2. System cannot any FT volumes. All mirrors, stripes and so on are
  3084. // expected to be LDM volumes on dynamic disks.
  3085. //
  3086. while (pCurrentDisk) {
  3087. if (!(pCurrentDisk->pDriveLayoutEx) || !(pCurrentDisk->pDiskGeometry)) {
  3088. MYASSERT(0);
  3089. pCurrentDisk = pCurrentDisk->pNext;
  3090. continue;
  3091. }
  3092. if (pCurrentDisk->pDriveLayoutEx->PartitionStyle == PARTITION_STYLE_MBR) {
  3093. for (index =0; index < pCurrentDisk->pDriveLayoutEx->PartitionCount; index++) {
  3094. MYASSERT(pCurrentDisk->pDriveLayoutEx->PartitionEntry[index].PartitionStyle == PARTITION_STYLE_MBR);
  3095. if (IsFTPartition(pCurrentDisk->pDriveLayoutEx->PartitionEntry[index].Mbr.PartitionType)) {
  3096. SetLastError(ERROR_NOT_SUPPORTED);
  3097. return FALSE;
  3098. }
  3099. }
  3100. }
  3101. else if (pCurrentDisk->pDriveLayoutEx->PartitionStyle == PARTITION_STYLE_GPT) {
  3102. //
  3103. // GPT disks can't have FT Mirrors.
  3104. //
  3105. }
  3106. pCurrentDisk = pCurrentDisk->pNext;
  3107. }
  3108. return TRUE;
  3109. }
  3110. //
  3111. // -----
  3112. // The following routines are helpers for AsrAddSifEntry
  3113. // -----
  3114. //
  3115. BOOL
  3116. AsrpSifCheckSectionNameSyntax(
  3117. IN PCWSTR lpSectionName
  3118. )
  3119. /*++
  3120. Routine Description:
  3121. Performs some basic validation of lpSectionName to make sure that
  3122. it conforms to the expected format for a section header
  3123. Arguments:
  3124. lpSectionName - The null-terminated string to be checked.
  3125. Return Value:
  3126. If lpSectionName appears to be a valid section name, the return value is a
  3127. nonzero value.
  3128. If lpSectionName does not pass our basic validation, the return value is
  3129. zero. Note that GetLastError will NOT return additional error
  3130. information in this case.
  3131. --*/
  3132. {
  3133. UINT i = 0;
  3134. WCHAR wch = 0;
  3135. //
  3136. // Must be non-null
  3137. //
  3138. if (!lpSectionName) {
  3139. return FALSE;
  3140. }
  3141. //
  3142. // Must have atleast 3 chars, ([.]) and at most ASR_SIF_ENTRY_MAX_CHARS
  3143. // chars
  3144. //
  3145. if ((ASR_SIF_ENTRY_MAX_CHARS < wcslen(lpSectionName)) ||
  3146. 3 > wcslen(lpSectionName)) {
  3147. return FALSE;
  3148. }
  3149. //
  3150. // First char must be [, and last char must be ].
  3151. //
  3152. if (L'[' != lpSectionName[0] ||
  3153. L']' != lpSectionName[wcslen(lpSectionName)-1]) {
  3154. return FALSE;
  3155. }
  3156. //
  3157. // Check for illegal characters. Legal set of chars: A-Z a-z . _
  3158. //
  3159. for (i = 1; i < wcslen(lpSectionName)-1; i++) {
  3160. wch = lpSectionName[i];
  3161. if ((wch < L'A' || wch > 'Z') &&
  3162. (wch < L'a' || wch > 'z') &&
  3163. (wch < L'0' || wch > '9') &&
  3164. (wch != L'.') &&
  3165. (wch != '_')) {
  3166. return FALSE;
  3167. }
  3168. }
  3169. return TRUE;
  3170. }
  3171. BOOL
  3172. AsrpSifCheckCommandsEntrySyntax(
  3173. PCWSTR pwszEntry
  3174. )
  3175. /*++
  3176. Routine Description:
  3177. Performs some basic validation of pwszEntry to make sure that it conforms
  3178. to the expected entry format for the Commands section
  3179. Arguments:
  3180. pwszEntry - The null-terminated string to be checked.
  3181. Return Value:
  3182. If pwszEntry appears to be a valid section name, the return value is a
  3183. nonzero value.
  3184. If pwszEntry does not pass our basic validation, the return value is
  3185. zero. Note that GetLastError will NOT return additional error
  3186. information in this case.
  3187. --*/
  3188. {
  3189. BOOL fValid = FALSE;
  3190. if (!pwszEntry) {
  3191. return TRUE; // NULL is okay
  3192. }
  3193. //
  3194. // COMMANDS section entry format:
  3195. // system-key,sequence-number,action-on-completion,"command","parameters"
  3196. // system-key must be 1
  3197. // 1000 <= sequence-number <= 4999
  3198. // 0 <= action-on-completion <= 1
  3199. // command: no syntax check
  3200. // parameters: no syntax check
  3201. //
  3202. fValid = (
  3203. // must be atleast 10 chars (1,0000,0,c)
  3204. 10 <= wcslen(pwszEntry) &&
  3205. // system-key must be 1
  3206. L'1' == pwszEntry[0] &&
  3207. L',' == pwszEntry[1] &&
  3208. // 1000 <= sequence-number <= 4999
  3209. L'1' <= pwszEntry[2] &&
  3210. L'4' >= pwszEntry[2] &&
  3211. L'0' <= pwszEntry[3] &&
  3212. L'9' >= pwszEntry[3] &&
  3213. L'0' <= pwszEntry[4] &&
  3214. L'9' >= pwszEntry[4] &&
  3215. L'0' <= pwszEntry[5] &&
  3216. L'9' >= pwszEntry[5] &&
  3217. L',' == pwszEntry[6] &&
  3218. // action-on-completion = [0|1]
  3219. L'0' <= pwszEntry[7] &&
  3220. L'1' >= pwszEntry[7]
  3221. );
  3222. return fValid;
  3223. }
  3224. INT
  3225. AsrpSkipMatchingQuotes(
  3226. IN PCWSTR pwszEntry,
  3227. IN const INT StartingOffset
  3228. )
  3229. /*++
  3230. Routine Description:
  3231. Checks if this entry starts with a quote. If it does, it finds the ending
  3232. quote, and returns the index of the char after the ending quote (usually
  3233. is a comma).
  3234. Arguments:
  3235. pwszEntry - The null-terminated string to check.
  3236. StartingOffset - The index of the starting-quote in pwszEntry.
  3237. Return Value:
  3238. If the character at StartingOffset is a quote, this returns the index of
  3239. the character after the next quote (the matching end-quote) in the
  3240. string. If a matching end-quote is not found, it returns -1.
  3241. If the character at StartingOffset is not a quote, this returns
  3242. StartingOffset.
  3243. Essentially, this returns the position where we expect the next comma in
  3244. the sif entry to be.
  3245. --*/
  3246. {
  3247. INT offset = StartingOffset;
  3248. if (pwszEntry[offset] == L'"') {
  3249. //
  3250. // Find the ending quote and make sure we don't go out of bounds.
  3251. //
  3252. while ( (pwszEntry[++offset]) &&
  3253. (pwszEntry[offset] != L'\"')) {
  3254. ;
  3255. }
  3256. if (!pwszEntry[offset]) {
  3257. //
  3258. // We didn't find the closing quotes--we went out of bounds
  3259. //
  3260. offset = -1;
  3261. }
  3262. else {
  3263. //
  3264. // Found closing quote
  3265. //
  3266. offset++;
  3267. }
  3268. }
  3269. return offset;
  3270. }
  3271. BOOL
  3272. AsrpSifCheckInstallFilesEntrySyntax(
  3273. IN PCWSTR pwszEntry,
  3274. OUT PINT DestinationFilePathIndex OPTIONAL
  3275. )
  3276. /*++
  3277. Routine Description:
  3278. Performs some basic validation of pwszEntry to make sure that it conforms
  3279. to the expected entry format for the InstallFiles section
  3280. Arguments:
  3281. pwszEntry - The null-terminated string to be checked.
  3282. DestinationFilePathIndex - This receives the index at which the
  3283. destination-file-path field in the sif entry (pwszEntry) begins.
  3284. This is an optional parameter.
  3285. Return Value:
  3286. If pwszEntry appears to be a valid section name, the return value is a
  3287. nonzero value.
  3288. If pwszEntry does not pass our basic validation, the return value is
  3289. zero. Note that GetLastError will NOT return additional error
  3290. information in this case.
  3291. --*/
  3292. {
  3293. INT offset = 0;
  3294. if (ARGUMENT_PRESENT(DestinationFilePathIndex)) {
  3295. *DestinationFilePathIndex = 0;
  3296. }
  3297. //
  3298. // NULL is okay
  3299. //
  3300. if (!pwszEntry) {
  3301. return TRUE;
  3302. }
  3303. //
  3304. // INSTALLFILES section entry format:
  3305. // system-key,source-media-label,source-device,
  3306. // source-file-path,destination-file-path,vendor-name,flags
  3307. //
  3308. // system-key must be 1
  3309. //
  3310. // must be atleast 10 chars (1,m,d,p,,v)
  3311. //
  3312. if (wcslen(pwszEntry) < 10) {
  3313. return FALSE;
  3314. }
  3315. //
  3316. // system-key must be 1
  3317. //
  3318. if (L'1' != pwszEntry[0] || L',' != pwszEntry[1] || L'"' != pwszEntry[2]) {
  3319. return FALSE;
  3320. }
  3321. offset = 2;
  3322. //
  3323. // source-media-label
  3324. //
  3325. offset = AsrpSkipMatchingQuotes(pwszEntry, offset);
  3326. if ((offset < 0) || L',' != pwszEntry[offset]) {
  3327. return FALSE;
  3328. }
  3329. //
  3330. // source-device
  3331. //
  3332. if (L'"' != pwszEntry[++offset]) {
  3333. return FALSE;
  3334. }
  3335. offset = AsrpSkipMatchingQuotes(pwszEntry, offset);
  3336. if ((offset < 0) || L',' != pwszEntry[offset]) {
  3337. return FALSE;
  3338. }
  3339. //
  3340. // source-file-path, must be enclosed in quotes.
  3341. //
  3342. if (L'"' != pwszEntry[++offset]) {
  3343. return FALSE;
  3344. }
  3345. offset = AsrpSkipMatchingQuotes(pwszEntry, offset);
  3346. if ((offset < 0) || L',' != pwszEntry[offset]) {
  3347. return FALSE;
  3348. }
  3349. //
  3350. // destination-file-path, must be enclosed in quotes.
  3351. //
  3352. if (L'"' != pwszEntry[++offset]) {
  3353. return FALSE;
  3354. }
  3355. if (ARGUMENT_PRESENT(DestinationFilePathIndex)) {
  3356. *DestinationFilePathIndex = offset;
  3357. }
  3358. offset = AsrpSkipMatchingQuotes(pwszEntry, offset);
  3359. if ((offset < 0) || L',' != pwszEntry[offset]) {
  3360. return FALSE;
  3361. }
  3362. //
  3363. // vendor-name, must be enclosed in quotes.
  3364. //
  3365. if (L'"' != pwszEntry[++offset]) {
  3366. return FALSE;
  3367. }
  3368. offset = AsrpSkipMatchingQuotes(pwszEntry, offset);
  3369. if (offset < 0) {
  3370. return FALSE;
  3371. }
  3372. return TRUE;
  3373. }
  3374. BOOL
  3375. AsrpIsRunningOnPersonalSKU(
  3376. VOID
  3377. )
  3378. /*++
  3379. Routine Description:
  3380. This function checks the system to see if we are running on the personal
  3381. version of the operating system.
  3382. The personal version is denoted by the product id equal to WINNT, which is
  3383. really workstation, and the product suite containing the personal suite
  3384. string.
  3385. This is lifted from "IsRunningOnPersonal" by WesW.
  3386. Arguments:
  3387. None.
  3388. Return Value:
  3389. TRUE if we are running on personal, FALSE otherwise.
  3390. --*/
  3391. {
  3392. OSVERSIONINFOEXW OsVer = {0};
  3393. ULONGLONG ConditionMask = 0;
  3394. OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  3395. OsVer.wSuiteMask = VER_SUITE_PERSONAL;
  3396. OsVer.wProductType = VER_NT_WORKSTATION;
  3397. VER_SET_CONDITION(ConditionMask, VER_PRODUCT_TYPE, VER_EQUAL);
  3398. VER_SET_CONDITION(ConditionMask, VER_SUITENAME, VER_AND);
  3399. return VerifyVersionInfo(&OsVer,
  3400. VER_PRODUCT_TYPE | VER_SUITENAME,
  3401. ConditionMask
  3402. );
  3403. }
  3404. BOOL
  3405. AsrpIsInGroup(
  3406. IN CONST DWORD dwGroup
  3407. )
  3408. /*++
  3409. Routine Description:
  3410. This function checks to see if the specified SID is enabled
  3411. in the primary access token for the current thread.
  3412. This is based on a similar function in dmadmin.exe.
  3413. Arguments:
  3414. dwGroup - The SID to be checked for
  3415. Return Value:
  3416. TRUE if the specified SID is enabled, FALSE otherwise.
  3417. --*/
  3418. {
  3419. SID_IDENTIFIER_AUTHORITY sidAuth = SECURITY_NT_AUTHORITY;
  3420. PSID sidGroup = NULL;
  3421. BOOL bResult = FALSE,
  3422. bIsInGroup = TRUE;
  3423. //
  3424. // Build the SID for the Administrators group
  3425. //
  3426. bResult = AllocateAndInitializeSid(&sidAuth,
  3427. 2,
  3428. SECURITY_BUILTIN_DOMAIN_RID,
  3429. dwGroup,
  3430. 0,
  3431. 0,
  3432. 0,
  3433. 0,
  3434. 0,
  3435. 0,
  3436. &sidGroup
  3437. );
  3438. if (!bResult) {
  3439. return FALSE;
  3440. }
  3441. //
  3442. // Check the current thread token membership
  3443. //
  3444. bResult = CheckTokenMembership(NULL, sidGroup, &bIsInGroup);
  3445. FreeSid(sidGroup);
  3446. return (bResult && bIsInGroup);
  3447. }
  3448. BOOL
  3449. AsrpHasPrivilege(
  3450. CONST PCWSTR szPrivilege
  3451. )
  3452. /*++
  3453. Routine Description:
  3454. This function checks to see if the specified privilege is enabled
  3455. in the primary access token for the current thread.
  3456. This is based on a similar function in dmadmin.exe.
  3457. Arguments:
  3458. szPrivilege - The privilege to be checked for
  3459. Return Value:
  3460. TRUE if the specified privilege is enabled, FALSE otherwise.
  3461. --*/
  3462. {
  3463. LUID luidValue; // LUID (locally unique ID) for the privilege
  3464. BOOL bResult = FALSE,
  3465. bHasPrivilege = FALSE;
  3466. HANDLE hToken = NULL;
  3467. PRIVILEGE_SET privilegeSet;
  3468. //
  3469. // Get the LUID for the privilege from the privilege name
  3470. //
  3471. bResult = LookupPrivilegeValue(
  3472. NULL,
  3473. szPrivilege,
  3474. &luidValue
  3475. );
  3476. if (!bResult) {
  3477. return FALSE;
  3478. }
  3479. //
  3480. // We want to use the token for the current process
  3481. //
  3482. bResult = OpenProcessToken(GetCurrentProcess(),
  3483. MAXIMUM_ALLOWED,
  3484. &hToken
  3485. );
  3486. if (!bResult) {
  3487. return FALSE;
  3488. }
  3489. //
  3490. // And check for the privilege
  3491. //
  3492. privilegeSet.PrivilegeCount = 1;
  3493. privilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
  3494. privilegeSet.Privilege[0].Luid = luidValue;
  3495. privilegeSet.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
  3496. bResult = PrivilegeCheck(hToken, &privilegeSet, &bHasPrivilege);
  3497. CloseHandle(hToken);
  3498. return (bResult && bHasPrivilege);
  3499. }
  3500. BOOL
  3501. AsrpCheckBackupPrivilege(
  3502. VOID
  3503. )
  3504. /*++
  3505. Routine Description:
  3506. This function checks to see if the current process has the
  3507. SE_BACKUP_NAME privilege enabled.
  3508. This is based on a similar function in dmadmin.exe.
  3509. Arguments:
  3510. None.
  3511. Return Value:
  3512. TRUE if the SE_BACKUP_NAME privilege is enabled, FALSE otherwise.
  3513. --*/
  3514. {
  3515. BOOL bHasPrivilege = FALSE;
  3516. bHasPrivilege = AsrpHasPrivilege(SE_BACKUP_NAME);
  3517. /*
  3518. //
  3519. // Don't give up yet--check for the local administrator rights
  3520. //
  3521. if (!bHasPrivilege) {
  3522. bHasPrivilege = AsrpIsInGroup(DOMAIN_ALIAS_RID_ADMINS);
  3523. }
  3524. */
  3525. if (!bHasPrivilege) {
  3526. SetLastError(ERROR_PRIVILEGE_NOT_HELD);
  3527. }
  3528. return bHasPrivilege;
  3529. }
  3530. //
  3531. // -------------------
  3532. // Public functions
  3533. // -------------------
  3534. //
  3535. // The functions below are for use by external backup and
  3536. // restore applications supporting ASR.
  3537. //
  3538. //
  3539. // ---- AsrCreateStateFile
  3540. //
  3541. BOOL
  3542. AsrCreateStateFileW(
  3543. IN PCWSTR lpFilePath OPTIONAL,
  3544. IN PCWSTR lpProviderName OPTIONAL,
  3545. IN CONST BOOL bEnableAutoExtend,
  3546. IN PCWSTR mszCriticalVolumes,
  3547. OUT DWORD_PTR *lpAsrContext
  3548. )
  3549. /*--
  3550. Routine Description:
  3551. AsrCreateStateFile creates an ASR state file with basic information about
  3552. the system, and launches third-party applications that have been
  3553. registered to be run as part of an ASR backup.
  3554. Arguments:
  3555. lpFileName - Pointer to a null-terminated string that specifies the
  3556. full path where the ASR state-file is to be created. If a file
  3557. already exists at the location pointed to by this parameter, it is
  3558. over-written.
  3559. This parameter can be NULL. If it is NULL, the ASR state-file is
  3560. created at the default location (%systemroot%\repair\asr.sif).
  3561. lpProviderName - Pointer to a null-terminated string that specifies the
  3562. full name and version of the backup-and-restore application
  3563. calling AsrCreateStateFile. There is a string size limit of
  3564. (ASR_SIF_ENTRY_MAX_CHARS - ASR_SIF_CCH_PROVIDER_STRING) characters
  3565. for this parameter.
  3566. This parameter can be NULL. If it is NULL, a "Provider=" entry is
  3567. not created in the Version section of the ASR state file.
  3568. bEnableAutoExtend - Indicates whether partitions are to be auto-extended
  3569. during an ASR restore. If this parameter is TRUE, partitions will be
  3570. auto-extended during the ASR restore. If this is FALSE,
  3571. partitions will not be extended.
  3572. lpCriticalVolumes - Pointer to a multi-string containing volume-GUIDs for
  3573. the critical volumes. This list is used to obtain the list of
  3574. critical disks in the system that must be restored for a
  3575. successful ASR.
  3576. The volume-GUID's must be in the NT-namespace, of the form
  3577. \??\Volume{Guid}
  3578. This parameter cannot be NULL.
  3579. lpAsrContext - Pointer to a variable receiving an ASR context. The
  3580. context returned should be used in calls to the other ASR API,
  3581. including AsrAddSifEntry. The calling application must call
  3582. AsrFreeContext to free this context when it is no longer needed.
  3583. This parameter cannot be NULL.
  3584. Return Value:
  3585. If the function succeeds, the return value is a nonzero value.
  3586. If the function fails, the return value is zero. To get extended error
  3587. information, call GetLastError().
  3588. --*/
  3589. {
  3590. BOOL result = FALSE;
  3591. DWORD status = ERROR_SUCCESS,
  3592. size = 0;
  3593. ULONG maxDeviceNumber = 0;
  3594. HANDLE sifhandle = NULL,
  3595. heapHandle = NULL;
  3596. PWSTR asrSifPath = NULL,
  3597. pnpSifPath = NULL,
  3598. tempPointer = NULL;
  3599. UINT cchAsrSifPath = 0;
  3600. char UnicodeFlag[3];
  3601. WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
  3602. SECURITY_ATTRIBUTES securityAttributes;
  3603. ASR_SYSTEM_INFO SystemInfo;
  3604. PASR_DISK_INFO OriginalDiskList = NULL;
  3605. if (AsrpIsRunningOnPersonalSKU()) {
  3606. //
  3607. // ASR is not supported on the Personal SKU
  3608. //
  3609. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  3610. return FALSE;
  3611. }
  3612. if (!AsrpCheckBackupPrivilege()) {
  3613. //
  3614. // The caller needs to first acquire SE_BACKUP_NAME
  3615. //
  3616. SetLastError(ERROR_PRIVILEGE_NOT_HELD);
  3617. return FALSE;
  3618. }
  3619. //
  3620. // Check the IN parameters:
  3621. //
  3622. #ifdef PRERELEASE
  3623. //
  3624. // Don't enforce "CriticalVolumes must be non-NULL" for test
  3625. //
  3626. if (!(lpAsrContext))
  3627. #else
  3628. if (!(lpAsrContext && mszCriticalVolumes))
  3629. #endif
  3630. {
  3631. SetLastError(ERROR_INVALID_PARAMETER);
  3632. return FALSE;
  3633. }
  3634. //
  3635. // Set the OUT paramaters to known error values
  3636. //
  3637. *lpAsrContext = 0;
  3638. //
  3639. // Guard ourselves against returning ERROR_SUCCESS if we encountered
  3640. // an unexpected error. We should never actually return this, since
  3641. // we always SetLastError whereever we return FALSE from.
  3642. //
  3643. SetLastError(ERROR_CAN_NOT_COMPLETE);
  3644. //
  3645. // Zero out structs
  3646. //
  3647. memset(&SystemInfo, 0L, sizeof (SYSTEM_INFO));
  3648. heapHandle = GetProcessHeap();
  3649. //
  3650. // Determine the file-path. If lpFilePath is provided, copy it over to
  3651. // locally allocated memory and use it, else use the default path.
  3652. //
  3653. if (ARGUMENT_PRESENT(lpFilePath)) {
  3654. cchAsrSifPath = wcslen(lpFilePath);
  3655. //
  3656. // Do a sanity check: we don't want to allow a file path
  3657. // more than 4096 characters long.
  3658. //
  3659. if (cchAsrSifPath > ASR_SIF_ENTRY_MAX_CHARS) {
  3660. SetLastError(ERROR_INVALID_PARAMETER);
  3661. return FALSE;
  3662. }
  3663. asrSifPath = (PWSTR) HeapAlloc(
  3664. heapHandle,
  3665. HEAP_ZERO_MEMORY,
  3666. ((cchAsrSifPath + 1) * sizeof(WCHAR))
  3667. );
  3668. _AsrpErrExitCode(!asrSifPath, status, ERROR_NOT_ENOUGH_MEMORY);
  3669. wcsncpy(asrSifPath, lpFilePath, cchAsrSifPath);
  3670. }
  3671. else {
  3672. //
  3673. // lpFilePath is NULL, form the default path (of the form
  3674. // \\?\c:\windows\repair\asr.sif)
  3675. //
  3676. //
  3677. // Try with a reasonably sized buffer to begin with.
  3678. //
  3679. asrSifPath = AsrpExpandEnvStrings(ASR_DEFAULT_SIF_PATH);
  3680. _AsrpErrExitCode(!asrSifPath, status, ERROR_BAD_ENVIRONMENT);
  3681. //
  3682. // Set cchAsrSifPath to the size of the asrSif buffer, since we
  3683. // use this in determining the size of the pnpSif buffer below.
  3684. //
  3685. cchAsrSifPath = wcslen(asrSifPath);
  3686. }
  3687. //
  3688. // Determine the file-path of the asrpnp.sif file, based on the location
  3689. // of the asr.sif file.
  3690. //
  3691. pnpSifPath = (PWSTR) HeapAlloc(
  3692. heapHandle,
  3693. HEAP_ZERO_MEMORY,
  3694. ((cchAsrSifPath + 1 + wcslen(ASRPNP_DEFAULT_SIF_NAME))* sizeof(WCHAR))
  3695. );
  3696. _AsrpErrExitCode(!pnpSifPath, status, ERROR_NOT_ENOUGH_MEMORY);
  3697. wcscpy(pnpSifPath, asrSifPath);
  3698. tempPointer = pnpSifPath;
  3699. while (*tempPointer) {
  3700. tempPointer++;
  3701. }
  3702. while ((*tempPointer != L'\\')
  3703. && (*tempPointer != L':')
  3704. && (tempPointer >= pnpSifPath)
  3705. ) {
  3706. tempPointer--;
  3707. }
  3708. tempPointer++;
  3709. wcscpy(tempPointer, ASRPNP_DEFAULT_SIF_NAME);
  3710. //
  3711. // We need to make the handle to asr.sif inheritable, since it will
  3712. // be passed (in the guise of the "AsrContext") to apps that have
  3713. // registered to be run as part of ASR.
  3714. //
  3715. securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  3716. securityAttributes.lpSecurityDescriptor = NULL;
  3717. securityAttributes.bInheritHandle = TRUE;
  3718. //
  3719. // Create the file. The handle will be closed by the calling backup-app.
  3720. //
  3721. sifhandle = CreateFileW(
  3722. asrSifPath, // lpFileName
  3723. GENERIC_WRITE | GENERIC_READ, // dwDesiredAccess
  3724. FILE_SHARE_READ, // dwShareMode
  3725. &securityAttributes, // lpSecurityAttributes
  3726. CREATE_ALWAYS, // dwCreationFlags
  3727. FILE_FLAG_BACKUP_SEMANTICS, // dwFlagsAndAttributes
  3728. NULL // hTemplateFile
  3729. );
  3730. if (!sifhandle || INVALID_HANDLE_VALUE == sifhandle) {
  3731. //
  3732. // LastError is set by CreateFile
  3733. //
  3734. _AsrpErrExitCode(TRUE, status, GetLastError());
  3735. }
  3736. //
  3737. // File was successfully created. Add the unicode flag at the beginning
  3738. // of the file, followed by comments.
  3739. //
  3740. sprintf(UnicodeFlag, "%c%c", 0xFF, 0xFE);
  3741. result = WriteFile(sifhandle, UnicodeFlag,
  3742. strlen(UnicodeFlag)*sizeof(char), &size, NULL);
  3743. _AsrpErrExitCode(!result, status, GetLastError());
  3744. wcscpy(infstring,
  3745. L";\r\n; Microsoft Windows Automated System Recovery State Information File\r\n;\r\n");
  3746. result = WriteFile(sifhandle, infstring,
  3747. wcslen(infstring)*sizeof(WCHAR), &size, NULL);
  3748. _AsrpErrExitCode(!result, status, GetLastError());
  3749. //
  3750. // Beyond this point, we must zero out asr.sif on any failure. Also, if
  3751. // there's any failure, we must be careful not to make further system
  3752. // calls that could change the error returned by GetLastError().
  3753. //
  3754. //
  3755. // Since the function return values below are and-ed, if any of the calls
  3756. // fails, we won't execute the ones following it.
  3757. //
  3758. result = (
  3759. //
  3760. // Initialise the global structures
  3761. //
  3762. AsrpInitSystemInformation(&SystemInfo, bEnableAutoExtend)
  3763. && AsrpInitDiskInformation(&OriginalDiskList)
  3764. && AsrpInitLayoutInformation(&SystemInfo,
  3765. OriginalDiskList,
  3766. &maxDeviceNumber,
  3767. TRUE
  3768. )
  3769. && AsrpInitClusterSharedDisks(OriginalDiskList)
  3770. && AsrpFreeNonFixedMedia(&OriginalDiskList)
  3771. && AsrpMarkCriticalDisks(OriginalDiskList,
  3772. mszCriticalVolumes,
  3773. maxDeviceNumber
  3774. )
  3775. //
  3776. // Check if the system configuration is supported
  3777. //
  3778. && AsrpIsSupportedConfiguration(OriginalDiskList, &SystemInfo)
  3779. //
  3780. // Write the required sections to asr.sif
  3781. //
  3782. && AsrpWriteVersionSection(sifhandle, lpProviderName)
  3783. && AsrpWriteSystemsSection(sifhandle, &SystemInfo)
  3784. && AsrpWriteBusesSection(sifhandle, OriginalDiskList)
  3785. && AsrpWriteMbrDisksSection(sifhandle, OriginalDiskList)
  3786. && AsrpWriteGptDisksSection(sifhandle, OriginalDiskList)
  3787. && AsrpWriteMbrPartitionsSection(sifhandle,
  3788. OriginalDiskList,
  3789. &SystemInfo
  3790. )
  3791. && AsrpWriteGptPartitionsSection(sifhandle,
  3792. OriginalDiskList,
  3793. &SystemInfo
  3794. )
  3795. && FlushFileBuffers(sifhandle)
  3796. //
  3797. // Create asrpnp.sif, containing entries needed to recover the PnP
  3798. // entries in the registry
  3799. //
  3800. && AsrCreatePnpStateFileW(pnpSifPath)
  3801. );
  3802. if (result) {
  3803. // everything above succeeded
  3804. //
  3805. // Launch the apps registered to be run as part of ASR-backup. If any
  3806. // of these apps don't complete successfully, we'll fail the ASR-
  3807. // backup.
  3808. //
  3809. result = (
  3810. AsrpLaunchRegisteredCommands(sifhandle, mszCriticalVolumes)
  3811. && FlushFileBuffers(sifhandle)
  3812. );
  3813. }
  3814. if (!result) {
  3815. //
  3816. // One of the functions above failed--we'll make asr.sif zero-length
  3817. // and return the error. CreateFileW or CloseHandle might over-write
  3818. // the LastError, so we save our error now and set it at the end.
  3819. //
  3820. status = GetLastError();
  3821. #ifndef PRERELEASE
  3822. //
  3823. // On release versions, we wipe out the asr.sif if we hit an error,
  3824. // so that the user doesn't unknowingly end up with an incomplete
  3825. // asr.sif
  3826. //
  3827. // We don't want to delete the incomplete asr.sif during test cycles,
  3828. // though, since the sif may be useful for debugging.
  3829. //
  3830. _AsrpCloseHandle(sifhandle);
  3831. //
  3832. // Delete asr.sif and create it again, so that we have a zero-length
  3833. // asr.sif
  3834. //
  3835. DeleteFileW(asrSifPath);
  3836. /* sifhandle = CreateFileW(
  3837. asrSifPath, // lpFileName
  3838. GENERIC_WRITE, // dwDesiredAccess
  3839. 0, // dwShareMode
  3840. &securityAttributes, // lpSecurityAttributes
  3841. CREATE_ALWAYS, // dwCreationFlags
  3842. FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
  3843. NULL // hTemplateFile
  3844. );
  3845. _AsrpCloseHandle(sifhandle);
  3846. */
  3847. #endif
  3848. SetLastError(status);
  3849. }
  3850. EXIT:
  3851. //
  3852. // Clean up
  3853. //
  3854. _AsrpHeapFree(asrSifPath);
  3855. _AsrpHeapFree(pnpSifPath);
  3856. AsrpFreeStateInformation(&OriginalDiskList, &SystemInfo);
  3857. //
  3858. // Set the OUT parameters
  3859. //
  3860. *lpAsrContext = (DWORD_PTR)sifhandle;
  3861. if (ERROR_SUCCESS != status) {
  3862. SetLastError(status);
  3863. }
  3864. if (!result) {
  3865. if (ERROR_SUCCESS == GetLastError()) {
  3866. //
  3867. // We're going to return failure, but we haven't set the LastError to
  3868. // a failure code. This is bad, since we have no clue what went wrong.
  3869. //
  3870. // We shouldn't ever get here, because the function returning FALSE above
  3871. // should set the LastError as it sees fit.
  3872. //
  3873. // But I've added this in just to be safe. Let's set it to a generic
  3874. // error.
  3875. //
  3876. MYASSERT(0 && L"Returning failure, but LastError is not set");
  3877. SetLastError(ERROR_CAN_NOT_COMPLETE);
  3878. }
  3879. }
  3880. return ((result) && (ERROR_SUCCESS == status));
  3881. }
  3882. BOOL
  3883. AsrCreateStateFileA(
  3884. IN LPCSTR lpFilePath,
  3885. IN LPCSTR lpProviderName,
  3886. IN CONST BOOL bEnableAutoExtend,
  3887. IN LPCSTR mszCriticalVolumes,
  3888. OUT DWORD_PTR *lpAsrContext
  3889. )
  3890. /*++
  3891. Routine Description:
  3892. This is the ANSI wrapper for AsrCreateStateFile. Please see
  3893. AsrCreateStateFileW for a detailed description.
  3894. Arguments:
  3895. Return Value:
  3896. If the function succeeds, the return value is a nonzero value.
  3897. If the function fails, the return value is zero. To get extended error
  3898. information, call GetLastError().
  3899. --*/
  3900. {
  3901. PWSTR asrSifPath = NULL,
  3902. providerName = NULL,
  3903. lpwszCriticalVolumes = NULL;
  3904. DWORD cchString = 0,
  3905. status = ERROR_SUCCESS;
  3906. BOOL result = FALSE;
  3907. HANDLE heapHandle = GetProcessHeap();
  3908. if (AsrpIsRunningOnPersonalSKU()) {
  3909. //
  3910. // ASR is not supported on the Personal SKU
  3911. //
  3912. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  3913. return FALSE;
  3914. }
  3915. if (!AsrpCheckBackupPrivilege()) {
  3916. //
  3917. // The caller needs to first acquire SE_BACKUP_NAME
  3918. //
  3919. SetLastError(ERROR_PRIVILEGE_NOT_HELD);
  3920. return FALSE;
  3921. }
  3922. //
  3923. // Check the IN parameters
  3924. //
  3925. #ifdef PRERELEASE
  3926. //
  3927. // Don't enforce "CriticalVolumes must be non-NULL" for test
  3928. //
  3929. if (!(lpAsrContext)) {
  3930. #else
  3931. if (!(lpAsrContext && mszCriticalVolumes)) {
  3932. #endif
  3933. SetLastError(ERROR_INVALID_PARAMETER);
  3934. return FALSE;
  3935. }
  3936. //
  3937. // if lpFilePath is not NULL, allocate a big enough buffer to hold
  3938. // it, and convert it to wide char
  3939. //
  3940. if (lpFilePath) {
  3941. cchString = strlen(lpFilePath);
  3942. //
  3943. // Do a sanity check: we don't want to allow a file path
  3944. // more than 4096 characters long.
  3945. //
  3946. _AsrpErrExitCode(
  3947. (cchString > ASR_SIF_ENTRY_MAX_CHARS),
  3948. status,
  3949. ERROR_INVALID_PARAMETER
  3950. );
  3951. //
  3952. // Allocate a big enough buffer, and copy it over
  3953. //
  3954. asrSifPath = (PWSTR) HeapAlloc(
  3955. heapHandle,
  3956. HEAP_ZERO_MEMORY,
  3957. ((cchString + 1) * sizeof(WCHAR))
  3958. );
  3959. _AsrpErrExitCode(!asrSifPath, status, ERROR_NOT_ENOUGH_MEMORY);
  3960. result = MultiByteToWideChar(CP_ACP, // CodePage
  3961. 0, // dwFlags
  3962. lpFilePath, // lpMultiByteStr
  3963. -1, // cbMultiByte: -1 since lpMultiByteStr is null terminated
  3964. asrSifPath, // lpWideCharStr
  3965. (cchString + 1) // cchWideChar
  3966. );
  3967. _AsrpErrExitCode(!result, status, ERROR_INVALID_PARAMETER);
  3968. }
  3969. //
  3970. // if lpProviderName is not NULL, make sure it isn't insanely long,
  3971. // and convert it to wide char
  3972. //
  3973. if (lpProviderName) {
  3974. cchString = strlen(lpProviderName);
  3975. //
  3976. // Do a sanity check: we don't want to allow an entry
  3977. // more than 4096 characters long.
  3978. //
  3979. _AsrpErrExitCode(
  3980. (cchString > (ASR_SIF_ENTRY_MAX_CHARS - ASR_SIF_CCH_PROVIDER_STRING)),
  3981. status,
  3982. ERROR_INVALID_PARAMETER
  3983. );
  3984. //
  3985. // Allocate a big enough buffer, and copy it over
  3986. //
  3987. providerName = (PWSTR) HeapAlloc(
  3988. heapHandle,
  3989. HEAP_ZERO_MEMORY,
  3990. ((cchString + 1) * sizeof(WCHAR))
  3991. );
  3992. _AsrpErrExitCode(!providerName, status, ERROR_NOT_ENOUGH_MEMORY);
  3993. //
  3994. // Convert to wide string
  3995. //
  3996. result = MultiByteToWideChar(CP_ACP,
  3997. 0,
  3998. lpProviderName,
  3999. -1,
  4000. providerName,
  4001. cchString + 1
  4002. );
  4003. _AsrpErrExitCode(!result, status, ERROR_INVALID_PARAMETER);
  4004. }
  4005. if (mszCriticalVolumes) {
  4006. //
  4007. // Find the total length of mszCriticalVolumes
  4008. //
  4009. LPCSTR lpVolume = mszCriticalVolumes;
  4010. while (*lpVolume) {
  4011. lpVolume += (strlen(lpVolume) + 1);
  4012. }
  4013. //
  4014. // Convert the string to wide-chars
  4015. //
  4016. cchString = (DWORD) (lpVolume - mszCriticalVolumes + 1);
  4017. lpwszCriticalVolumes = (PWSTR) HeapAlloc(
  4018. heapHandle,
  4019. HEAP_ZERO_MEMORY,
  4020. cchString * sizeof(WCHAR)
  4021. );
  4022. _AsrpErrExitCode(!lpwszCriticalVolumes, status, ERROR_NOT_ENOUGH_MEMORY);
  4023. result = MultiByteToWideChar(CP_ACP,
  4024. 0,
  4025. mszCriticalVolumes,
  4026. cchString,
  4027. lpwszCriticalVolumes,
  4028. cchString * sizeof(WCHAR)
  4029. );
  4030. _AsrpErrExitCode(!result, status, ERROR_INVALID_PARAMETER);
  4031. }
  4032. result = AsrCreateStateFileW(
  4033. asrSifPath,
  4034. providerName,
  4035. bEnableAutoExtend,
  4036. lpwszCriticalVolumes,
  4037. lpAsrContext
  4038. );
  4039. EXIT:
  4040. _AsrpHeapFree(asrSifPath);
  4041. _AsrpHeapFree(providerName);
  4042. _AsrpHeapFree(lpwszCriticalVolumes);
  4043. return ((result) && (ERROR_SUCCESS == status));
  4044. }
  4045. //
  4046. // ---- AsrAddSifEntry
  4047. //
  4048. BOOL
  4049. AsrAddSifEntryW(
  4050. IN DWORD_PTR AsrContext,
  4051. IN PCWSTR lpSectionName,
  4052. IN PCWSTR lpSifEntry OPTIONAL
  4053. )
  4054. /*++
  4055. Routine Description:
  4056. The AsrSifEntry function adds entries to the ASR state file. It can be
  4057. used by applications that need to save application-specific information
  4058. in the ASR state file.
  4059. Arguments:
  4060. AsrContext - A valid ASR context. See the notes for more information
  4061. about this parameter.
  4062. lpSectionName - Pointer to a null-terminated string that specifies the
  4063. section name. This parameter cannot be NULL.
  4064. The section name has a string size limit of ASR_MAX_SIF_LINE
  4065. characters. This limit is related to how the AsrAddSifEntry
  4066. function parses entries in the ASR state file.
  4067. The section name is case-insensitive. It is converted to all-caps
  4068. before being added to the state file. The section name must not
  4069. contain spaces or non-printable characters. The valid character
  4070. set for section name is limited to letters (A-Z, a-z), numbers
  4071. (0-9), and the following special characters: underscore ("_")
  4072. and period ("."). If the state file does not contain a section
  4073. with the section name pointed to by lpSectionName, a new
  4074. section is created with this section name.
  4075. lpSifEntry - Pointer to a null-terminated string that is to be added to
  4076. the state file in the specified section. If *lpSifEntry is a
  4077. valid entry, there is a string size limit of
  4078. ASR_SIF_ENTRY_MAX_CHARS characters. This limit is related
  4079. to how the AsrAddSifEntry function parses entries in the
  4080. ASR state file.
  4081. If lpSifEntry parameter is NULL, an empty section with the
  4082. section name pointed to by lpSectionName is created if it
  4083. doesn't already exist.
  4084. Return Value:
  4085. If the function succeeds, the return value is a nonzero value.
  4086. If the function fails, the return value is zero. To get extended error
  4087. information, call GetLastError().
  4088. Notes:
  4089. The application calling AsrAddSifEntry obtains the ASR context by one of
  4090. two methods:
  4091. - If the application is the backup-and-restore application that creates
  4092. the ASR state file, it receives the context as a parameter returned by
  4093. AsrCreateStateFile.
  4094. - If the application is launched by AsrCreateStateFile as part of an ASR
  4095. backup, it receives the context to the state file as the /context
  4096. command-line parameter. The application is responsible for reading
  4097. this parameter to get the value of the context.
  4098. AsrAddSifEntry will fail if the section name is that of a reserved section
  4099. that applications are not allowed to add entries to. The following sections
  4100. in the ASR state file are reserved:
  4101. - Version, System, Disks.Mbr, Disk.Gpt, Partitions.Mbr and Partitions.Gpt
  4102. If the section name is recognised (Commands or InstallFiles), AsrAddSifEntry
  4103. will check the syntax of *lpSifEntry to ensure that it is in the proper
  4104. format. In addition, AsrAddSifEntry will check to ensure that there are no
  4105. filename collisions for the InstallFiles section. If a collision is
  4106. detected, the API returns ERROR_ALREADY_EXISTS. Applications must
  4107. use the following pre-defined values to access the recognised sections:
  4108. - ASR_COMMANDS_SECTION_NAME_W for the Commands section, and
  4109. - ASR_INSTALLFILES_SECTION_NAME for the InstallFiles section.
  4110. --*/
  4111. {
  4112. DWORD status = ERROR_SUCCESS,
  4113. nextKey = 0,
  4114. fileOffset = 0,
  4115. size = 0,
  4116. fileSize = 0,
  4117. bufferSize = 0,
  4118. destFilePos = 0;
  4119. HANDLE sifhandle = NULL;
  4120. WCHAR sifstring[ASR_SIF_ENTRY_MAX_CHARS *2 + 1],
  4121. ucaseSectionName[ASR_SIF_ENTRY_MAX_CHARS + 1]; // lpSectionName converted to upper case
  4122. PWSTR buffer = NULL,
  4123. sectionStart = NULL,
  4124. lastEqual = NULL,
  4125. nextSection = NULL,
  4126. nextChar = NULL,
  4127. sectionName = NULL;
  4128. BOOL commandsSection = FALSE,
  4129. installFilesSection = FALSE,
  4130. result = FALSE;
  4131. HANDLE heapHandle = NULL;
  4132. if (AsrpIsRunningOnPersonalSKU()) {
  4133. //
  4134. // ASR is not supported on the Personal SKU
  4135. //
  4136. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  4137. return FALSE;
  4138. }
  4139. if (!AsrpCheckBackupPrivilege()) {
  4140. //
  4141. // The caller needs to first acquire SE_BACKUP_NAME
  4142. //
  4143. SetLastError(ERROR_PRIVILEGE_NOT_HELD);
  4144. return FALSE;
  4145. }
  4146. heapHandle = GetProcessHeap();
  4147. MYASSERT(heapHandle);
  4148. //
  4149. // Zero out local structs
  4150. //
  4151. memset(sifstring, 0, (ASR_SIF_ENTRY_MAX_CHARS *2 + 1) * sizeof(WCHAR));
  4152. memset(ucaseSectionName, 0, (ASR_SIF_ENTRY_MAX_CHARS + 1) * (sizeof (WCHAR)));
  4153. //
  4154. // No OUT parameters
  4155. //
  4156. //
  4157. // Check the IN parameters: The SectionName should meet
  4158. // syntax requirements, SifEntry shouldn't be too long,
  4159. // and the sifhandle should be valid.
  4160. //
  4161. if ((!AsrpSifCheckSectionNameSyntax(lpSectionName)) ||
  4162. (ARGUMENT_PRESENT(lpSifEntry)
  4163. && (wcslen(lpSifEntry) > ASR_SIF_ENTRY_MAX_CHARS)) ||
  4164. ((!AsrContext) ||
  4165. (INVALID_HANDLE_VALUE == (HANDLE)AsrContext))
  4166. ) {
  4167. SetLastError(ERROR_INVALID_PARAMETER);
  4168. return FALSE;
  4169. }
  4170. while (lpSectionName[size]) {
  4171. if ((lpSectionName[size] >= L'a') && (lpSectionName[size] <= L'z')) {
  4172. ucaseSectionName[size] = lpSectionName[size] - L'a' + L'A';
  4173. }
  4174. else {
  4175. ucaseSectionName[size] = lpSectionName[size];
  4176. }
  4177. size++;
  4178. }
  4179. //
  4180. // If the section is a recognised section (COMMANDS or INSTALLFILES),
  4181. // we check the format of the sif entry.
  4182. //
  4183. if (!wcscmp(ucaseSectionName, ASR_SIF_SECTION_COMMANDS_W)) {
  4184. // COMMANDS section
  4185. if (!AsrpSifCheckCommandsEntrySyntax(lpSifEntry)) {
  4186. SetLastError(ERROR_INVALID_PARAMETER);
  4187. return FALSE;
  4188. }
  4189. commandsSection = TRUE;
  4190. }
  4191. else if(!wcscmp(ucaseSectionName, ASR_SIF_SECTION_INSTALLFILES_W)) {
  4192. // INSTALLFILES section
  4193. if (!AsrpSifCheckInstallFilesEntrySyntax(lpSifEntry, &destFilePos)) {
  4194. SetLastError(ERROR_INVALID_PARAMETER);
  4195. return FALSE;
  4196. }
  4197. installFilesSection = TRUE;
  4198. }
  4199. //
  4200. // We do not allow anyone to write to reserved sections:
  4201. // VERSION, SYSTEMS, DISKS.[MBR|GPT], PARTITIONS.[MBR|GPT]
  4202. //
  4203. else if (
  4204. !wcscmp(ucaseSectionName, ASR_SIF_VERSION_SECTION_NAME) ||
  4205. !wcscmp(ucaseSectionName, ASR_SIF_SYSTEM_SECTION_NAME) ||
  4206. !wcscmp(ucaseSectionName, ASR_SIF_MBR_DISKS_SECTION_NAME) ||
  4207. !wcscmp(ucaseSectionName, ASR_SIF_GPT_DISKS_SECTION_NAME) ||
  4208. !wcscmp(ucaseSectionName, ASR_SIF_MBR_PARTITIONS_SECTION_NAME) ||
  4209. !wcscmp(ucaseSectionName, ASR_SIF_GPT_PARTITIONS_SECTION_NAME)
  4210. ) {
  4211. SetLastError(ERROR_INVALID_PARAMETER);
  4212. return FALSE;
  4213. }
  4214. sectionName = (PWSTR) HeapAlloc(
  4215. heapHandle,
  4216. HEAP_ZERO_MEMORY,
  4217. (wcslen(ucaseSectionName) + 5) * sizeof (WCHAR)
  4218. );
  4219. _AsrpErrExitCode(!sectionName, status, ERROR_NOT_ENOUGH_MEMORY);
  4220. swprintf(sectionName, L"\r\n%ws\r\n", ucaseSectionName);
  4221. sifhandle = (HANDLE) AsrContext;
  4222. //
  4223. // The algorithm to add to the middle of asr.sif is rather ugly
  4224. // at the moment: we read the entire file into memory, make our
  4225. // necessary changes, and write back the changed portion of the
  4226. // file to disk. This is inefficient, but it's okay for now since
  4227. // we expect asr.sif to be about 5 or 6 KB at the most.
  4228. //
  4229. // We should revisit this if the performance is unacceptably poor.
  4230. //
  4231. //
  4232. // Allocate memory for the file
  4233. //
  4234. fileSize = GetFileSize(sifhandle, NULL);
  4235. GetLastError();
  4236. _AsrpErrExitCode((fileSize == 0xFFFFFFFF), status, ERROR_INVALID_DATA);
  4237. SetFilePointer(sifhandle, 0, NULL, FILE_BEGIN);
  4238. buffer = (PWSTR) HeapAlloc(
  4239. heapHandle,
  4240. HEAP_ZERO_MEMORY,
  4241. fileSize + 2
  4242. );
  4243. _AsrpErrExitCode(!buffer, status, ERROR_NOT_ENOUGH_MEMORY);
  4244. //
  4245. // And read file into memory.
  4246. //
  4247. result = ReadFile(sifhandle, buffer, fileSize, &size, NULL);
  4248. _AsrpErrExitCode(!result, status, GetLastError());
  4249. //
  4250. // Try to locate ucaseSectionName in the file
  4251. //
  4252. sectionStart = wcsstr(buffer, sectionName);
  4253. if (!sectionStart) {
  4254. //
  4255. // sectionName was not found, ie the section does not exist
  4256. // Add it at the end, and add the SifEntry right after it.
  4257. //
  4258. swprintf(sifstring,
  4259. L"\r\n%ws\r\n%ws%ws\r\n",
  4260. ucaseSectionName,
  4261. ((commandsSection || installFilesSection) ? L"1=" : L""),
  4262. (ARGUMENT_PRESENT(lpSifEntry) ? lpSifEntry : L"")
  4263. );
  4264. //
  4265. // File pointer already points to the end (because of ReadFile above)
  4266. //
  4267. if (!WriteFile(sifhandle, sifstring,
  4268. wcslen(sifstring)*sizeof (WCHAR), &size, NULL)) {
  4269. status = GetLastError();
  4270. }
  4271. // We're done
  4272. }
  4273. else {
  4274. //
  4275. // The section exists, if lpSifEntry is NULL, we're done
  4276. //
  4277. if (ARGUMENT_PRESENT(lpSifEntry)) {
  4278. //
  4279. // SifEntry is not NULL, we'll add it at the end of the section
  4280. //
  4281. nextChar = sectionStart + 4; // Move pointer from \r to . in \r\n[.
  4282. nextKey = 1;
  4283. //
  4284. // Find where this section ends--look either for the start
  4285. // of the next section, or for the end of the file
  4286. //
  4287. while(*nextChar && *nextChar != L'[') {
  4288. //
  4289. // If this is a recognised section, we need to generate
  4290. // the <key> to add the entry in a <key>=<entry> format.
  4291. // We go through each line, and find the last key that
  4292. // already exists. The new key will be last key + 1.
  4293. //
  4294. if (commandsSection || installFilesSection) {
  4295. UINT commaCount = 0;
  4296. BOOL tracking = FALSE;
  4297. UINT count = 0;
  4298. WCHAR c1, c2;
  4299. while (*nextChar && (*nextChar != L'[') && (*nextChar != L'\n')) {
  4300. if (installFilesSection) {
  4301. if (*nextChar == L',') {
  4302. commaCount++;
  4303. }
  4304. if ((commaCount > 2) && (L'"' == *nextChar)) {
  4305. if (tracking) {
  4306. // duplicate file name
  4307. _AsrpErrExitCode((L'"'== lpSifEntry[destFilePos + count]), status, ERROR_ALREADY_EXISTS);
  4308. }
  4309. else {
  4310. tracking = TRUE;
  4311. count = 0;
  4312. }
  4313. }
  4314. if (tracking) {
  4315. c1 = *nextChar;
  4316. if (c1 >= L'a' && c1 <= L'z') {
  4317. c1 = c1 - L'a' + L'A';
  4318. }
  4319. c2 = lpSifEntry[destFilePos + count];
  4320. if (c2 >= L'a' && c2 <= L'z') {
  4321. c2 = c2 - L'a' + L'A';
  4322. }
  4323. if (c1 == c2) {
  4324. count++;
  4325. }
  4326. else {
  4327. tracking = FALSE;
  4328. }
  4329. }
  4330. }
  4331. nextChar++;
  4332. }
  4333. if (*nextChar == L'\n') {
  4334. ++nextChar;
  4335. if (*nextChar >= L'0' && *nextChar <= L'9') {
  4336. nextKey = 0;
  4337. while (*nextChar >= L'0' && *nextChar <= L'9') {
  4338. nextKey = nextKey*10 + (*nextChar - L'0');
  4339. nextChar++;
  4340. }
  4341. nextKey++;
  4342. }
  4343. }
  4344. }
  4345. else {
  4346. nextChar++;
  4347. }
  4348. }
  4349. //
  4350. // We save a pointer to the next section in the sif, since we
  4351. // need to write it out to disk.
  4352. //
  4353. if (*nextChar) {
  4354. nextSection = nextChar;
  4355. }
  4356. else {
  4357. nextSection = NULL;
  4358. }
  4359. if (commandsSection || installFilesSection) {
  4360. //
  4361. // Form the <key>=<entry> string
  4362. //
  4363. swprintf(
  4364. sifstring,
  4365. L"%lu=%ws\r\n",
  4366. nextKey,
  4367. lpSifEntry
  4368. );
  4369. }
  4370. else {
  4371. //
  4372. // Not a recognised section: don't add the <key>=<entry>
  4373. // format, keep the string exactly as passed in
  4374. //
  4375. wcscpy(sifstring, lpSifEntry);
  4376. wcscat(sifstring, L"\r\n");
  4377. }
  4378. if (nextSection) {
  4379. //
  4380. // There are sections following the section we're adding to
  4381. // We need to mark the point where the new entry is added.
  4382. // While writing out to disk, we'll start from this point.
  4383. //
  4384. fileOffset = (DWORD) (((LPBYTE)nextSection) - ((LPBYTE)buffer) - sizeof(WCHAR)*2);
  4385. // section start - file start - "\r\n"
  4386. SetFilePointer(sifhandle, fileOffset, NULL, FILE_BEGIN);
  4387. }
  4388. //
  4389. // file pointer points to where the entry must be added
  4390. //
  4391. if (!WriteFile(sifhandle, sifstring, wcslen(sifstring)*sizeof(WCHAR), &size, NULL)) {
  4392. status = GetLastError();
  4393. }
  4394. else if (nextSection) {
  4395. //
  4396. // write out all sections following this entry
  4397. //
  4398. if (!WriteFile(
  4399. sifhandle,
  4400. ((LPBYTE)nextSection) - (sizeof(WCHAR)*2),
  4401. fileSize - fileOffset,
  4402. &size,
  4403. NULL
  4404. )) {
  4405. status = GetLastError();
  4406. }
  4407. }
  4408. }
  4409. }
  4410. EXIT:
  4411. _AsrpHeapFree(sectionName);
  4412. _AsrpHeapFree(buffer);
  4413. return (BOOL) (ERROR_SUCCESS == status);
  4414. }
  4415. BOOL
  4416. AsrAddSifEntryA(
  4417. IN DWORD_PTR AsrContext,
  4418. IN LPCSTR lpSectionName,
  4419. IN LPCSTR lpSifEntry OPTIONAL
  4420. )
  4421. /*++
  4422. This is the ANSI wrapper for AsrAddSifEntry.
  4423. See AsrAddSifEntryW for a full description.
  4424. --*/
  4425. {
  4426. WCHAR wszSectionName[ASR_SIF_ENTRY_MAX_CHARS + 1];
  4427. WCHAR wszSifEntry[ASR_SIF_ENTRY_MAX_CHARS + 1];
  4428. if (AsrpIsRunningOnPersonalSKU()) {
  4429. //
  4430. // ASR is not supported on the Personal SKU
  4431. //
  4432. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  4433. return FALSE;
  4434. }
  4435. if (!AsrpCheckBackupPrivilege()) {
  4436. //
  4437. // The caller needs to first acquire SE_BACKUP_NAME
  4438. //
  4439. SetLastError(ERROR_PRIVILEGE_NOT_HELD);
  4440. return FALSE;
  4441. }
  4442. memset(wszSectionName, 0L, ASR_SIF_ENTRY_MAX_CHARS + 1);
  4443. memset(wszSifEntry, 0L, ASR_SIF_ENTRY_MAX_CHARS + 1);
  4444. //
  4445. // lpSectionName must be non-NULL
  4446. //
  4447. if ((!lpSectionName) || !(MultiByteToWideChar(
  4448. CP_ACP,
  4449. 0,
  4450. lpSectionName,
  4451. -1,
  4452. wszSectionName,
  4453. ASR_SIF_ENTRY_MAX_CHARS + 1
  4454. ))) {
  4455. SetLastError(ERROR_INVALID_PARAMETER);
  4456. return FALSE;
  4457. }
  4458. //
  4459. // lpSifEntry is allowed to be NULL
  4460. //
  4461. if (ARGUMENT_PRESENT(lpSifEntry) && !(MultiByteToWideChar(
  4462. CP_ACP,
  4463. 0,
  4464. lpSifEntry,
  4465. -1,
  4466. wszSifEntry,
  4467. ASR_SIF_ENTRY_MAX_CHARS + 1
  4468. ))) {
  4469. SetLastError(ERROR_INVALID_PARAMETER);
  4470. return FALSE;
  4471. }
  4472. return AsrAddSifEntryW(
  4473. AsrContext,
  4474. wszSectionName,
  4475. wszSifEntry
  4476. );
  4477. }
  4478. //
  4479. // ---- AsrFreeContext
  4480. //
  4481. BOOL
  4482. AsrFreeContext(
  4483. IN OUT DWORD_PTR *lpAsrContext
  4484. )
  4485. /*++
  4486. Routine Description:
  4487. AsrFreeContext frees the Asr Context, and sets lpAsrContext
  4488. to NULL.
  4489. Arguments:
  4490. lpAsrContext This is the Asr context to be freed. This argument must
  4491. not be NULL.
  4492. AsrFreeContext will set this value to NULL after freeing
  4493. it, to prevent further unintended accesses to the freed
  4494. object.
  4495. Return Value:
  4496. If the function succeeds, the return value is a nonzero value.
  4497. If the function fails, the return value is zero. To get extended error
  4498. information, call GetLastError().
  4499. --*/
  4500. {
  4501. BOOL result = FALSE;
  4502. if (AsrpIsRunningOnPersonalSKU()) {
  4503. //
  4504. // ASR is not supported on the Personal SKU
  4505. //
  4506. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  4507. return FALSE;
  4508. }
  4509. //
  4510. // Essentially, the lpAsrContext is just a file handle, and all we need
  4511. // to do to free it is call CloseHandle.
  4512. //
  4513. if ((lpAsrContext) &&
  4514. (*lpAsrContext) &&
  4515. (INVALID_HANDLE_VALUE != (HANDLE)(*lpAsrContext))
  4516. ) {
  4517. result = CloseHandle((HANDLE)*lpAsrContext);
  4518. *lpAsrContext = 0;
  4519. }
  4520. else {
  4521. SetLastError(ERROR_INVALID_PARAMETER);
  4522. }
  4523. return result;
  4524. }