Advertisement
Guest User

Untitled

a guest
Sep 18th, 2021
26
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 49.35 KB | None | 0 0
  1. // MICROSOFT PUBLIC LICENSE (Ms-PL)
  2. //
  3. // This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
  4. //
  5. // 1. Definitions
  6. //
  7. // The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
  8. // A "contribution" is the original software, or any additions or changes to the software.
  9. // A "contributor" is any person that distributes its contribution under this license.
  10. // "Licensed patents" are a contributor's patent claims that read directly on its contribution.
  11. //
  12. // 2. Grant of Rights
  13. //
  14. // (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
  15. // (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
  16. //
  17. // 3. Conditions and Limitations
  18. //
  19. // (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
  20. // (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
  21. // (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
  22. // (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
  23. // (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
  24.  
  25.  
  26. /*++
  27.  
  28. Module Name:
  29.  
  30. VhdTool.cpp
  31.  
  32. Abstract:
  33.  
  34. This file implements the VhdTool functionality
  35.  
  36. Author:
  37.  
  38. Christopher Eck - 23-Mar-2009
  39.  
  40. --*/
  41.  
  42. #include "stdafx.h"
  43.  
  44. //
  45. // VHD definitions.
  46. // Taken from the public VHD spec, current link is
  47. // http://technet.microsoft.com/en-us/virtualserver/bb676673.aspx
  48. //
  49.  
  50. #define VHD_CURRENT_FILE_FORMAT_VERSION 0x00010000
  51. #define VHD_HOST_OS_WINDOWS 'k2iW'
  52. #define VHD_TYPE_BIG_FIXED 0x02000000
  53. #define VHD_TYPE_BIG_SPARSE 0x03000000
  54. #define VHD_TYPE_BIG_DIFF 0x04000000
  55. #define VHD_SECTOR_SIZE 0x00000200UI64
  56.  
  57. struct VHD_DISK_FOOTER
  58. {
  59. UCHAR Cookie[8];
  60. ULONG Features;
  61. ULONG FileFormatVersion;
  62. ULONG64 DataOffset;
  63. ULONG TimeStamp;
  64. UCHAR CreatorApplication[4];
  65. ULONG CreatorVersion;
  66. ULONG CreatorHostOS;
  67. ULONG64 OriginalSize;
  68. ULONG64 CurrentSize;
  69.  
  70. struct
  71. {
  72. USHORT Cylinders;
  73. UCHAR Heads;
  74. UCHAR SectorsPerTrack;
  75. } Geometry;
  76.  
  77. ULONG DiskType;
  78. ULONG Checksum;
  79. UCHAR UniqueId[16];
  80. UCHAR SavedState;
  81. UCHAR Reserved[427];
  82. };
  83.  
  84.  
  85. struct PARENT_DRIVE_LOCATOR
  86. {
  87. ULONG PlatformCode;
  88. ULONG PlatformDataAllocatedSpace;
  89. ULONG PlatformDataLength;
  90. ULONG Reserved;
  91. ULONGLONG PlatformDataOffset;
  92. };
  93.  
  94.  
  95. struct VHD_SPARSE_HEADER
  96. {
  97. ULONGLONG Signature;
  98. ULONGLONG Reserved1;
  99. ULONGLONG TableOffset;
  100. ULONG HeaderVersion;
  101. ULONG MaxTableEntries;
  102. ULONG BlockSize;
  103. ULONG CheckSum;
  104.  
  105. UCHAR ParentUniqueId[16];
  106. ULONG ParentTimeStamp;
  107. ULONG Reserved2;
  108. USHORT ParentName[256];
  109. PARENT_DRIVE_LOCATOR ParentLocatorTable[8];
  110. UCHAR Reserved3[256];
  111. };
  112.  
  113.  
  114. //
  115. // Tool definitions
  116. //
  117.  
  118. const LARGE_INTEGER SecondsToStartOf2000 = {0xee7dd480, 0x00000002};
  119. const int VHD_CREATE_TOOL_VERSION = 0x00020000;
  120.  
  121. enum PROGRAM_MODE
  122. {
  123. ProgramModeUnspecified = 0,
  124. ProgramModeCreate = 1,
  125. ProgramModeConvert = 2,
  126. ProgramModeExtend = 3,
  127. ProgramModeRepair = 4
  128. };
  129.  
  130.  
  131. //
  132. // Global state
  133. //
  134. BOOL g_QuietMode = FALSE;
  135.  
  136.  
  137. //
  138. // Input/Output handlers
  139. //
  140.  
  141.  
  142. VOID
  143. PrintUsage()
  144. /*++
  145.  
  146. Routine Description:
  147.  
  148. Prints the command line usage to the console.
  149.  
  150. Arguments:
  151.  
  152. None.
  153.  
  154. Return Value:
  155.  
  156. None.
  157.  
  158. --*/
  159. {
  160. if (g_QuietMode)
  161. {
  162. goto Cleanup;
  163. }
  164.  
  165. printf("\n");
  166. printf("VhdTool.exe /create <FileName> <Size> [/quiet]\n");
  167. printf("VhdTool.exe /convert <FileName> [/quiet]\n");
  168. printf("VhdTool.exe /extend <FileName> <NewSize> [/quiet]\n");
  169. printf("VhdTool.exe /repair <BaseVhdFileName> <FirstSnapshotAVhdFileName> [/quiet]\n");
  170. printf("\n");
  171. printf("Create: Creates a new fixed format VHD of size <Size>.\n");
  172. printf(" WARNING - this function is admin only and bypasses\n");
  173. printf(" file system security. The resulting VHD file will\n");
  174. printf(" contain data which currently exists on the physical disk.\n");
  175. printf("\n");
  176. printf("Convert: Converts an existing file to a fixed-format VHD.\n");
  177. printf(" The existing file length, rounded up, will contain block data\n");
  178. printf(" A VHD footer is appended to the current end of file.\n");
  179. printf("\n");
  180. printf("Extend: Extends an existing fixed format VHD to a larger size <Size>.\n");
  181. printf(" WARNING - this function is admin only and bypasses\n");
  182. printf(" file system security. The resulting VHD file will\n");
  183. printf(" contain data which currently exists on the physical disk.\n");
  184. printf("\n");
  185. printf("Repair: Repairs a broken Hyper-V snapshot chain where an administrator\n");
  186. printf(" has expanded the size of the root VHD. The base VHD will be\n");
  187. printf(" returned to its original size. THIS MAY CAUSE DATA LOSS if the\n");
  188. printf(" contents of the base VHD were changed after expansion.\n");
  189. printf("\n");
  190.  
  191. Cleanup:
  192. return;
  193. }
  194.  
  195.  
  196. BOOL
  197. ParseCommandLine(
  198. __in int ArgCount,
  199. __in _TCHAR* ArgArray[],
  200. __out PROGRAM_MODE* ProgramMode,
  201. __out LPTSTR* Filename,
  202. __out UINT64* Filesize,
  203. __out LPTSTR* ChildFilename
  204. )
  205. /*++
  206.  
  207. Routine Description:
  208.  
  209. Parses the command line
  210.  
  211. Arguments:
  212.  
  213. ArgCount - Number of command line arguments
  214.  
  215. ArgArray - Array of command line arguments
  216.  
  217. ProgramMode - Mode the program should run in.
  218.  
  219. Filename - Name of the file to take action upon.
  220.  
  221. Filesize - Desired size of the file.
  222.  
  223. ChildFilename - Name of the child VHD if present.
  224.  
  225. Return Value:
  226.  
  227. BOOL - TRUE on success, FALSE on failure
  228.  
  229. --*/
  230. {
  231. LPTSTR tempString = NULL;
  232. size_t filenameLength = 0;
  233. BOOL status = FALSE;
  234. int currentArg = 1;
  235.  
  236. //
  237. // Initialize out params
  238. //
  239. *ProgramMode = ProgramModeUnspecified;
  240. *Filename = NULL;
  241. *Filesize = 0;
  242.  
  243. //
  244. // Validate the command line arguments
  245. //
  246. if (ArgCount < 3)
  247. {
  248. PrintUsage();
  249. goto Cleanup;
  250. }
  251.  
  252. //
  253. // Retrieve mode
  254. //
  255. tempString = ArgArray[currentArg];
  256. currentArg++;
  257.  
  258. if (_tcsicmp(tempString, TEXT("/create")) == 0)
  259. {
  260. *ProgramMode = ProgramModeCreate;
  261. }
  262. else if (_tcsicmp(tempString, TEXT("/convert")) == 0)
  263. {
  264. *ProgramMode = ProgramModeConvert;
  265. }
  266. else if (_tcsicmp(tempString, TEXT("/extend")) == 0)
  267. {
  268. *ProgramMode = ProgramModeExtend;
  269. }
  270. else if (_tcsicmp(tempString, TEXT("/repair")) == 0)
  271. {
  272. *ProgramMode = ProgramModeRepair;
  273. }
  274. else
  275. {
  276. PrintUsage();
  277. goto Cleanup;
  278. }
  279.  
  280. //
  281. // Retrieve file name
  282. //
  283. tempString = ArgArray[currentArg];
  284. currentArg++;
  285.  
  286. // Add space for the null-terminating character
  287. filenameLength = _tcslen(tempString) + 1;
  288.  
  289. *Filename = (LPTSTR)malloc(filenameLength * sizeof(TCHAR));
  290. _tcscpy_s(*Filename, filenameLength, tempString);
  291.  
  292. //
  293. // Retrieve file size if in create or extend mode.
  294. //
  295. if ((*ProgramMode == ProgramModeCreate) ||
  296. (*ProgramMode == ProgramModeExtend))
  297. {
  298. if ((currentArg + 1) > ArgCount)
  299. {
  300. PrintUsage();
  301. goto Cleanup;
  302. }
  303.  
  304. tempString = ArgArray[currentArg];
  305. currentArg++;
  306.  
  307. *Filesize = _ttoi64(tempString);
  308. if (*Filesize == 0)
  309. {
  310. PrintUsage();
  311. goto Cleanup;
  312. }
  313. }
  314.  
  315. //
  316. // Retieve child VHD name if in repair mode.
  317. //
  318. if (*ProgramMode == ProgramModeRepair)
  319. {
  320. if ((currentArg + 1) > ArgCount)
  321. {
  322. PrintUsage();
  323. goto Cleanup;
  324. }
  325.  
  326. tempString = ArgArray[currentArg];
  327. currentArg++;
  328.  
  329. filenameLength = _tcslen(tempString) + 1;
  330.  
  331. *ChildFilename = (LPTSTR)malloc(filenameLength * sizeof(TCHAR));
  332. _tcscpy_s(*ChildFilename, filenameLength, tempString);
  333. }
  334.  
  335.  
  336. //
  337. // Retrieve quiet mode if present
  338. //
  339. if ((currentArg + 1) <= ArgCount)
  340. {
  341. tempString = ArgArray[currentArg];
  342. currentArg++;
  343.  
  344. if (_tcsicmp(tempString, TEXT("/quiet")) == 0)
  345. {
  346. g_QuietMode = TRUE;
  347. }
  348. else
  349. {
  350. PrintUsage();
  351. goto Cleanup;
  352. }
  353. }
  354.  
  355. status = TRUE;
  356.  
  357. Cleanup:
  358. return status;
  359. }
  360.  
  361.  
  362. VOID
  363. PrintError(
  364. DWORD LastErrorValue,
  365. __in_z const char* FormatString,
  366. ...
  367. )
  368. /*++
  369.  
  370. Routine Description:
  371.  
  372. Prints tool errors.
  373.  
  374. Arguments:
  375.  
  376. LastErrorValue - error code returned from a system call.
  377.  
  378. FormatString - the printf format string.
  379.  
  380. ... - VarArgs passed to printf.
  381.  
  382. Return Value:
  383.  
  384. None.
  385.  
  386. --*/
  387. {
  388. va_list argumentPointer;
  389. LPTSTR errorMessage = NULL;
  390. int status = 0;
  391.  
  392. if (g_QuietMode)
  393. {
  394. goto Cleanup;
  395. }
  396.  
  397. va_start(argumentPointer, FormatString);
  398.  
  399. printf("\tError: ");
  400. vprintf(FormatString, argumentPointer);
  401.  
  402. status = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  403. NULL,
  404. LastErrorValue,
  405. 0,
  406. (LPTSTR)&errorMessage,
  407. 255,
  408. NULL);
  409. if (status == 0)
  410. {
  411. printf(" with error: %d", LastErrorValue);
  412. }
  413. else
  414. {
  415. printf(" with error: %S", errorMessage);
  416. LocalFree(errorMessage);
  417. }
  418.  
  419. printf("\n");
  420.  
  421. Cleanup:
  422. return;
  423. }
  424.  
  425.  
  426. VOID
  427. PrintStatus(
  428. __in_z const char* FormatString,
  429. ...
  430. )
  431. /*++
  432.  
  433. Routine Description:
  434.  
  435. Prints tool status.
  436.  
  437. Arguments:
  438.  
  439. FormatString - the printf format string.
  440.  
  441. ... - VarArgs passed to printf.
  442.  
  443. Return Value:
  444.  
  445. None.
  446.  
  447. --*/
  448. {
  449. va_list argumentPointer;
  450.  
  451. if (g_QuietMode)
  452. {
  453. goto Cleanup;
  454. }
  455.  
  456. va_start(argumentPointer, FormatString);
  457.  
  458. printf("\tStatus: ");
  459. vprintf(FormatString, argumentPointer);
  460. printf("\n");
  461.  
  462. Cleanup:
  463. return;
  464. }
  465.  
  466.  
  467. VOID
  468. PrintVhdIdentifier(
  469. __in GUID* Identifier,
  470. __in_z const char* FormatString
  471. )
  472. /*++
  473.  
  474. Routine Description:
  475.  
  476. Prints a VHD identifier as status
  477.  
  478. Arguments:
  479.  
  480. FormatString - the printf format string.
  481.  
  482. ... - VarArgs passed to printf.
  483.  
  484. Return Value:
  485.  
  486. None.
  487.  
  488. --*/
  489. {
  490. LPTSTR parentIdString;
  491.  
  492. if (g_QuietMode)
  493. {
  494. goto Cleanup;
  495. }
  496.  
  497. UuidToString(Identifier, (RPC_WSTR*)&parentIdString);
  498.  
  499. printf("\tStatus: ");
  500. printf(FormatString, parentIdString);
  501. printf("\n");
  502.  
  503. RpcStringFree((RPC_WSTR*)&parentIdString);
  504.  
  505. Cleanup:
  506. return;
  507. }
  508.  
  509.  
  510. //
  511. // Utility functions
  512. //
  513.  
  514.  
  515. ULONG64
  516. RoundUpUlong64(
  517. __in ULONG64 Value,
  518. __in ULONG64 PowerOf2
  519. )
  520. /*++
  521.  
  522. Routine Description:
  523.  
  524. Rounds a 64-bit Value up to the nearest given PowerOf2.
  525.  
  526. Arguments:
  527.  
  528. Value - Supplies the value to round.
  529.  
  530. PowerOf2 - Supplies the value to round to.
  531.  
  532. Return Value:
  533.  
  534. The rounded value.
  535.  
  536. --*/
  537. {
  538. return (Value + (PowerOf2 - 1)) & ~(PowerOf2 - 1);
  539. }
  540.  
  541.  
  542. ULONG
  543. CalculateChecksum(
  544. __in_bcount(Length) PVOID Buffer,
  545. __in ULONG Length
  546. )
  547. /*++
  548.  
  549. Routine Description:
  550.  
  551. This routine calculates the one's complement of the checksum of all the
  552. bytes with the given range, excluding the address to ignore.
  553.  
  554. Arguments:
  555.  
  556. Buffer - Pointer to the buffer to calculate the checksum for.
  557.  
  558. Length - Length of the buffer in bytes.
  559.  
  560. Return Value:
  561.  
  562. Checksum.
  563.  
  564. --*/
  565. {
  566. PUCHAR address = NULL;
  567. ULONG checksum = 0;
  568.  
  569. checksum = 0;
  570. address = (PUCHAR)Buffer;
  571.  
  572. while (Length != 0)
  573. {
  574. checksum += *address;
  575. Length -= 1;
  576. address += 1;
  577. }
  578.  
  579. return ~checksum;
  580. }
  581.  
  582.  
  583. BOOL
  584. ZeroFileContents(
  585. __in HANDLE FileHandle,
  586. __in LARGE_INTEGER FileOffset,
  587. __in DWORD BytesToClear
  588. )
  589. /*++
  590.  
  591. Routine Description:
  592.  
  593. Zeros out a section of a file.
  594.  
  595. Arguments:
  596.  
  597. FileHandle - Handle to the open file.
  598.  
  599. FileOffset - Offset in the file to begin clearing.
  600.  
  601. BytesToClear - Number of bytes of space to clear.
  602.  
  603. Return Value:
  604.  
  605. BOOL - TRUE on success, FALSE on failure.
  606.  
  607. --*/
  608. {
  609. LPVOID buffer = NULL;
  610. DWORD bytesWritten = 0;
  611. DWORD bufferSize = 1024 * 1024;
  612. DWORD bytesToWrite = 0;
  613. BOOL success = FALSE;
  614.  
  615. // Validate input
  616. if (FileHandle == INVALID_HANDLE_VALUE)
  617. {
  618. goto Cleanup;
  619. }
  620.  
  621. //
  622. // Allocate and clear the 1 MB zeroing buffer
  623. //
  624. buffer = malloc(bufferSize);
  625. if (buffer == NULL)
  626. {
  627. PrintError(ERROR_OUTOFMEMORY, "Unable to allocate zeroing buffer");
  628. goto Cleanup;
  629. }
  630. RtlZeroMemory(buffer, bufferSize);
  631.  
  632. //
  633. // Seek to the file offset
  634. //
  635. if (!SetFilePointerEx(FileHandle, FileOffset, NULL, FILE_BEGIN))
  636. {
  637. PrintError(GetLastError(), "Unable to seek to file offset %I64d", FileOffset);
  638. goto Cleanup;
  639. }
  640.  
  641. //
  642. // Clear file contents.
  643. // This isn't particularly fast, but maintainability is more important here.
  644. //
  645. while (BytesToClear > 0)
  646. {
  647. if (BytesToClear > bufferSize)
  648. {
  649. bytesToWrite = bufferSize;
  650. }
  651. else
  652. {
  653. bytesToWrite = BytesToClear;
  654. }
  655. BytesToClear -= bytesToWrite;
  656.  
  657. if (!WriteFile(FileHandle, buffer, bytesToWrite, &bytesWritten, NULL))
  658. {
  659. PrintError(GetLastError(), "Unable to write data to file");
  660. goto Cleanup;
  661. }
  662.  
  663. if (bytesWritten != bytesToWrite)
  664. {
  665. PrintError(ERROR_WRITE_FAULT, "Bytes written count unexpected");
  666. goto Cleanup;
  667. }
  668. }
  669.  
  670. success = TRUE;
  671.  
  672. Cleanup:
  673.  
  674. if (buffer != NULL)
  675. {
  676. free(buffer);
  677. buffer = NULL;
  678. }
  679.  
  680. return success;
  681. }
  682.  
  683.  
  684. BOOL
  685. EnablePrivilege(
  686. __in LPCTSTR SePrivilege
  687. )
  688. /*++
  689.  
  690. Routine Description:
  691.  
  692. Enables a system privelege on the current token
  693.  
  694. Arguments:
  695.  
  696. SePrivilege - the privilege to enable
  697.  
  698. Return Value:
  699.  
  700. BOOL - TRUE on success, FALSE on failure
  701.  
  702. --*/
  703. {
  704. PTOKEN_PRIVILEGES newPrivileges = NULL;
  705. HANDLE token = INVALID_HANDLE_VALUE;
  706. BOOL success = FALSE;
  707. LUID luidPrivilege;
  708.  
  709. //
  710. // Make sure we have access to adjust and to get the old
  711. // token privileges
  712. //
  713. if (!OpenProcessToken(GetCurrentProcess(),
  714. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  715. &token))
  716. {
  717. PrintError(GetLastError(), "Failed to open process token");
  718. goto Cleanup;
  719. }
  720.  
  721. //
  722. // Initialize the privilege adjustment structure
  723. //
  724. if (!LookupPrivilegeValue(NULL, SePrivilege, &luidPrivilege))
  725. {
  726. PrintError(GetLastError(), "Failed to lookup privilege value");
  727. goto Cleanup;
  728. }
  729.  
  730. newPrivileges = (PTOKEN_PRIVILEGES)calloc(1, sizeof(TOKEN_PRIVILEGES) + (1 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES));
  731. if (newPrivileges == NULL)
  732. {
  733. PrintError(ERROR_NOT_ENOUGH_MEMORY, "Failed to allocate new privileges");
  734. goto Cleanup;
  735. }
  736.  
  737. newPrivileges->PrivilegeCount = 1;
  738. newPrivileges->Privileges[0].Luid = luidPrivilege;
  739. newPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  740.  
  741. //
  742. // Enable the privilege
  743. //
  744. if (!AdjustTokenPrivileges(token,
  745. FALSE,
  746. newPrivileges,
  747. 0,
  748. NULL,
  749. NULL))
  750. {
  751. PrintError(GetLastError(), "Failed to enable required privilege");
  752. goto Cleanup;
  753. }
  754.  
  755. //
  756. // According to the API description, caller needs to call GetLastError()
  757. // to determine if the call was completely successful even though the
  758. // return value is non-zero (success).
  759. //
  760. if ((GetLastError() != ERROR_SUCCESS))
  761. {
  762. PrintError(GetLastError(), "Failed to enable required privilege");
  763. goto Cleanup;
  764. }
  765.  
  766. success = TRUE;
  767.  
  768. Cleanup:
  769.  
  770. if (token != INVALID_HANDLE_VALUE)
  771. {
  772. CloseHandle(token);
  773. }
  774.  
  775. if (newPrivileges != NULL)
  776. {
  777. free(newPrivileges);
  778. }
  779.  
  780. return success;
  781. }
  782.  
  783.  
  784. //
  785. // VHD format functions
  786. //
  787.  
  788. VOID
  789. GetVirtualDiskGeometry(
  790. __in ULONGLONG TotalSectors,
  791. __out PUSHORT DiskCylinders,
  792. __out PUCHAR DiskHeads,
  793. __out PUCHAR DiskSectorsPerTrack
  794. )
  795. /*++
  796.  
  797. Routine Description:
  798.  
  799. This routine returns the disk geometry values given the total number of
  800. sectors on the disk.
  801.  
  802. Arguments:
  803.  
  804. TotalSectors - Supplies the total number of sectors on the disk.
  805.  
  806. DiskCylinders - Supplies a pointer to a value that will hold the number of
  807. cylinders on the disk.
  808.  
  809. DiskHeads - Supplies a pointer to a value that will hold the number of
  810. heads on the disk.
  811.  
  812. DiskSectorsPerTrack - Supplies a pointer to a value that will hold the
  813. number sectors per track on the disk.
  814.  
  815. Return Value:
  816.  
  817. None.
  818.  
  819. --*/
  820. {
  821. USHORT cylinders = 0;
  822. ULONG cylinderTimesHeads = 0;
  823. UCHAR heads = 0;
  824. UCHAR sectorsPerTrack = 0;
  825.  
  826. // Upper bound the total sector count.
  827. if (TotalSectors > (65535 * 16 * 255))
  828. {
  829. TotalSectors = 65535 * 16 * 255;
  830. }
  831.  
  832. if (TotalSectors >= (65535 * 16 * 63))
  833. {
  834. sectorsPerTrack = 255;
  835. heads = 16;
  836. cylinderTimesHeads = (ULONG)(TotalSectors / sectorsPerTrack);
  837. }
  838. else
  839. {
  840. sectorsPerTrack = 17;
  841. cylinderTimesHeads = (ULONG)(TotalSectors / sectorsPerTrack);
  842. heads = (UCHAR)((cylinderTimesHeads + 1023) / 1024);
  843.  
  844. if (heads < 4)
  845. {
  846. heads = 4;
  847. }
  848.  
  849. if ((cylinderTimesHeads >= ((ULONG)heads * 1024)) || (heads > 16))
  850. {
  851. sectorsPerTrack = 31;
  852. heads = 16;
  853. cylinderTimesHeads = (ULONG)(TotalSectors / sectorsPerTrack);
  854. }
  855.  
  856. if (cylinderTimesHeads >= ((ULONG)heads * 1024))
  857. {
  858. sectorsPerTrack = 63;
  859. heads = 16;
  860. cylinderTimesHeads = (ULONG)(TotalSectors / sectorsPerTrack);
  861. }
  862. }
  863.  
  864. cylinders = (USHORT)(cylinderTimesHeads / heads);
  865.  
  866. *DiskCylinders = cylinders;
  867. *DiskHeads = heads;
  868. *DiskSectorsPerTrack = sectorsPerTrack;
  869. }
  870.  
  871.  
  872. BOOL
  873. GetTimestamp(
  874. __in ULONG* Timestamp
  875. )
  876. /*++
  877.  
  878. Routine Description:
  879.  
  880. This routine calculates the VHD timestamp, based on the number
  881. of seconds since 1/1/2000.
  882.  
  883. Arguments:
  884.  
  885. Timestamp - returns the VHD timestamp value, based on the current time.
  886.  
  887. Return Value:
  888.  
  889. BOOL - TRUE upon success, FALSE upon failure.
  890.  
  891. --*/
  892. {
  893. ULARGE_INTEGER time;
  894. LARGE_INTEGER seconds;
  895. SYSTEMTIME systemTime;
  896. FILETIME fileTime;
  897.  
  898. // Get current time, 1601 timescale based.
  899. GetSystemTime(&systemTime);
  900.  
  901. // Translate into usable variable
  902. SystemTimeToFileTime(&systemTime, &fileTime);
  903. time.LowPart = fileTime.dwLowDateTime;
  904. time.HighPart = fileTime.dwHighDateTime;
  905.  
  906. // Convert from nanoseconds to seconds.
  907. seconds.QuadPart = time.QuadPart / 10000000;
  908.  
  909. // Subtract number of seconds from 1601 to 2000
  910. seconds.QuadPart = seconds.QuadPart - SecondsToStartOf2000.QuadPart;
  911.  
  912. //
  913. // If the result is negative then the date was before 2000 or if
  914. // the result is greater than a ULONG then it's too far in the
  915. // future so we return FALSE.
  916. //
  917. if (seconds.HighPart != 0)
  918. {
  919. return FALSE;
  920. }
  921.  
  922. *Timestamp = seconds.LowPart;
  923.  
  924. return TRUE;
  925. }
  926.  
  927.  
  928. BOOL
  929. CreateVhdFooter(
  930. __in ULONG64 VirtualSize,
  931. __in GUID* UniqueId,
  932. __in BOOLEAN FixedType,
  933. __in UINT64 SparseHeaderOffset,
  934. __out VHD_DISK_FOOTER* Footer
  935. )
  936. /*++
  937.  
  938. Routine Description:
  939.  
  940. This routine creates a valid fixed VHD footer given a virtual disk size.
  941.  
  942. Arguments:
  943.  
  944. VirtualSize - Size of the virtual storage device in bytes.
  945.  
  946. UniqueId - the unique identifier to place in the footer.
  947.  
  948. FixedType - TRUE if a fixed VHD footer should be generated,
  949. FALSE for a sparse VHD footer.
  950.  
  951. SparseHeaderOffset - for sparse VHD footers, the offset to the
  952. sparse header.
  953.  
  954. Footer - Upon return, filled with a valid VHD 1.0 footer for the
  955. virtual storage device.
  956.  
  957. Return Value:
  958.  
  959. BOOL - TRUE upon success, FALSE upon failure.
  960.  
  961. --*/
  962. {
  963. ULONG64 totalSectors = 0;
  964. USHORT cylinders = 0;
  965. ULONG timestamp = 0;
  966. UCHAR heads = 0;
  967. UCHAR sectorsPerTrack = 0;
  968. BOOL success = FALSE;
  969.  
  970. RtlZeroMemory(Footer, sizeof(VHD_DISK_FOOTER));
  971.  
  972. //
  973. // Gather data
  974. //
  975.  
  976. assert((VirtualSize % VHD_SECTOR_SIZE) == 0);
  977. totalSectors = VirtualSize / VHD_SECTOR_SIZE;
  978.  
  979. GetVirtualDiskGeometry(totalSectors, &cylinders, &heads, &sectorsPerTrack);
  980.  
  981. if (!GetTimestamp(&timestamp))
  982. {
  983. PrintError(ERROR_OUTOFMEMORY, "Unable to determine timestamp");
  984. goto Cleanup;
  985. }
  986.  
  987. //
  988. // Initialize the VHD 1.0 Footer
  989. //
  990.  
  991. Footer->Cookie[0] = 'c';
  992. Footer->Cookie[1] = 'o';
  993. Footer->Cookie[2] = 'n';
  994. Footer->Cookie[3] = 'e';
  995. Footer->Cookie[4] = 'c';
  996. Footer->Cookie[5] = 't';
  997. Footer->Cookie[6] = 'i';
  998. Footer->Cookie[7] = 'x';
  999.  
  1000. Footer->Features = _byteswap_ulong(0x00000002);
  1001. Footer->FileFormatVersion = _byteswap_ulong(VHD_CURRENT_FILE_FORMAT_VERSION);
  1002.  
  1003. if (FixedType)
  1004. {
  1005. Footer->DiskType = VHD_TYPE_BIG_FIXED;
  1006. Footer->DataOffset = 0xFFFFFFFFFFFFFFFF;
  1007. }
  1008. else
  1009. {
  1010. Footer->DiskType = VHD_TYPE_BIG_SPARSE;
  1011. Footer->DataOffset = SparseHeaderOffset;
  1012. }
  1013.  
  1014. Footer->TimeStamp = _byteswap_ulong(timestamp);
  1015.  
  1016. Footer->CreatorApplication[0] = 'h';
  1017. Footer->CreatorApplication[1] = 'a';
  1018. Footer->CreatorApplication[2] = 'c';
  1019. Footer->CreatorApplication[3] = 'k';
  1020.  
  1021. Footer->CreatorVersion = _byteswap_ulong(VHD_CREATE_TOOL_VERSION);
  1022.  
  1023. Footer->CreatorHostOS = VHD_HOST_OS_WINDOWS;
  1024.  
  1025. Footer->OriginalSize = _byteswap_uint64(VirtualSize);
  1026. Footer->CurrentSize = _byteswap_uint64(VirtualSize);
  1027.  
  1028. Footer->Geometry.Cylinders = _byteswap_ushort(cylinders);
  1029. Footer->Geometry.Heads = heads;
  1030. Footer->Geometry.SectorsPerTrack = sectorsPerTrack;
  1031.  
  1032. RtlCopyMemory(&Footer->UniqueId, UniqueId, 16);
  1033.  
  1034. Footer->SavedState = 0;
  1035.  
  1036. Footer->Checksum = _byteswap_ulong(CalculateChecksum((PVOID)Footer, sizeof(VHD_DISK_FOOTER)));
  1037.  
  1038. PrintStatus("VHD footer generated");
  1039.  
  1040. success = TRUE;
  1041.  
  1042. Cleanup:
  1043. return success;
  1044. }
  1045.  
  1046.  
  1047. BOOL
  1048. OpenVhdFile(
  1049. __in LPTSTR Filename,
  1050. __out HANDLE* FileHandle,
  1051. __out LARGE_INTEGER* FileSize
  1052. )
  1053. /*++
  1054.  
  1055. Routine Description:
  1056.  
  1057. Opens a file for IO & retrieves the physical file size
  1058.  
  1059. Arguments:
  1060.  
  1061. Filename - name of the file to open.
  1062.  
  1063. FileHandle - Upon successful return, contains the handle to the file.
  1064.  
  1065. FileSize - Upon successful return, contains the physical size of the file.
  1066.  
  1067. Return Value:
  1068.  
  1069. BOOL - TRUE on success, FALSE on failure
  1070.  
  1071. --*/
  1072. {
  1073. BOOL status = FALSE;
  1074.  
  1075. //
  1076. // Initialize out parameters
  1077. //
  1078. *FileHandle = INVALID_HANDLE_VALUE;
  1079. FileSize->QuadPart = 0;
  1080.  
  1081. //
  1082. // Open the file
  1083. //
  1084. PrintStatus("Attempting to open file \"%S\"", Filename);
  1085. *FileHandle = CreateFile(Filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  1086.  
  1087. if (*FileHandle == INVALID_HANDLE_VALUE)
  1088. {
  1089. PrintError(GetLastError(), "Unable to open file \"%S\"", Filename);
  1090. goto Cleanup;
  1091. }
  1092.  
  1093. //
  1094. // Determine the file length
  1095. //
  1096. if (!GetFileSizeEx(*FileHandle, FileSize))
  1097. {
  1098. PrintError(GetLastError(), "Unable to retrieve file size");
  1099. goto Cleanup;
  1100. }
  1101. PrintStatus("File opened, current size is %I64d", FileSize->QuadPart);
  1102.  
  1103. status = TRUE;
  1104.  
  1105. Cleanup:
  1106.  
  1107. if (!status)
  1108. {
  1109. CloseHandle(*FileHandle);
  1110. *FileHandle = INVALID_HANDLE_VALUE;
  1111. }
  1112.  
  1113. return status;
  1114. }
  1115.  
  1116.  
  1117. VOID
  1118. RoundUpToSectorSize(
  1119. __inout LARGE_INTEGER* FileSize
  1120. )
  1121. /*++
  1122.  
  1123. Routine Description:
  1124.  
  1125. Rounds the input file size up to the nearest sector
  1126.  
  1127. Arguments:
  1128.  
  1129. FileSize - Size to round up.
  1130.  
  1131. Return Value:
  1132.  
  1133. None.
  1134.  
  1135. --*/
  1136. {
  1137. ULONG64 size = FileSize->QuadPart;
  1138. size = RoundUpUlong64(size, VHD_SECTOR_SIZE);
  1139.  
  1140. if (size != FileSize->QuadPart)
  1141. {
  1142. PrintStatus("Warning: Rounding file size up to nearest sector boundary.");
  1143. }
  1144.  
  1145. FileSize->QuadPart = size;
  1146. }
  1147.  
  1148.  
  1149. BOOL
  1150. ReadFooter(
  1151. __in HANDLE FileHandle,
  1152. __in UINT64 FooterOffset,
  1153. __out VHD_DISK_FOOTER* Footer
  1154. )
  1155. /*++
  1156.  
  1157. Routine Description:
  1158.  
  1159. Reads the VHD footer from an open VHD file
  1160.  
  1161. Arguments:
  1162.  
  1163. FileHandle - handle to the open VHD file.
  1164.  
  1165. FooterOffset - file offset of the footer object within the file.
  1166.  
  1167. Footer - Upon successful return, contains the footer object in big-endian format.
  1168.  
  1169. Return Value:
  1170.  
  1171. BOOL - TRUE on success, FALSE on failure
  1172.  
  1173. --*/
  1174. {
  1175. LARGE_INTEGER savedPosition;
  1176. LARGE_INTEGER footerPosition;
  1177. DWORD bytesRead;
  1178. BOOL savedCurrentPosition = FALSE;
  1179. BOOL status = FALSE;
  1180.  
  1181. memset(Footer, 0, sizeof(VHD_DISK_FOOTER));
  1182.  
  1183. //
  1184. // Save the current file pointer
  1185. //
  1186. savedPosition.QuadPart = 0;
  1187. if (!SetFilePointerEx(FileHandle, savedPosition, &savedPosition, FILE_CURRENT))
  1188. {
  1189. PrintError(GetLastError(), "Unable to get current file pointer");
  1190. goto Cleanup;
  1191. }
  1192.  
  1193. savedCurrentPosition = TRUE;
  1194.  
  1195. //
  1196. // Read the existing footer.
  1197. //
  1198. footerPosition.QuadPart = FooterOffset;
  1199. if (!SetFilePointerEx(FileHandle, footerPosition, NULL, FILE_BEGIN))
  1200. {
  1201. PrintError(GetLastError(), "Unable to set file pointer to footer offset");
  1202. goto Cleanup;
  1203. }
  1204.  
  1205. if (!ReadFile(FileHandle, Footer, sizeof(VHD_DISK_FOOTER), &bytesRead, NULL))
  1206. {
  1207. PrintError(GetLastError(), "Cannot read existing footer");
  1208. goto Cleanup;
  1209. }
  1210.  
  1211. if (bytesRead != sizeof(VHD_DISK_FOOTER))
  1212. {
  1213. PrintError(ERROR_HANDLE_EOF, "Cannot read existing footer");
  1214. goto Cleanup;
  1215. }
  1216.  
  1217. status = TRUE;
  1218.  
  1219. Cleanup:
  1220.  
  1221. if (savedCurrentPosition)
  1222. {
  1223. SetFilePointerEx(FileHandle, savedPosition, NULL, FILE_BEGIN);
  1224. }
  1225.  
  1226. return status;
  1227. }
  1228.  
  1229.  
  1230. BOOL
  1231. ReadSparseHeader(
  1232. __in HANDLE FileHandle,
  1233. __in VHD_DISK_FOOTER* Footer,
  1234. __out VHD_SPARSE_HEADER* SparseHeader
  1235. )
  1236. /*++
  1237.  
  1238. Routine Description:
  1239.  
  1240. Reads the VHD sparse header from a VHD file.
  1241.  
  1242. Arguments:
  1243.  
  1244. FileHandle - handle to the open VHD file.
  1245.  
  1246. Footer - Contains the footer object in big-endian format.
  1247.  
  1248. SparseHeader - Upon successful return, contains the sparse header object in big-endian format.
  1249.  
  1250. Return Value:
  1251.  
  1252. BOOL - TRUE on success, FALSE on failure
  1253.  
  1254. --*/
  1255. {
  1256. LARGE_INTEGER savedPosition;
  1257. LARGE_INTEGER headerPosition;
  1258. DWORD bytesRead;
  1259. BOOL savedCurrentPosition = FALSE;
  1260. BOOL status = FALSE;
  1261.  
  1262. memset(SparseHeader, 0, sizeof(VHD_SPARSE_HEADER));
  1263.  
  1264. //
  1265. // Save the current file pointer
  1266. //
  1267. savedPosition.QuadPart = 0;
  1268. if (!SetFilePointerEx(FileHandle, savedPosition, &savedPosition, FILE_CURRENT))
  1269. {
  1270. PrintError(GetLastError(), "Unable to get current file pointer");
  1271. goto Cleanup;
  1272. }
  1273. savedCurrentPosition = TRUE;
  1274.  
  1275. //
  1276. // Move the file pointer to the start of the sparse header.
  1277. //
  1278. headerPosition.QuadPart = _byteswap_uint64(Footer->DataOffset);
  1279. if (!SetFilePointerEx(FileHandle, headerPosition, NULL, FILE_BEGIN))
  1280. {
  1281. PrintError(GetLastError(), "Unable to set file pointer");
  1282. goto Cleanup;
  1283. }
  1284.  
  1285. //
  1286. // Read the sparse header.
  1287. //
  1288. if (!ReadFile(FileHandle, SparseHeader, sizeof(VHD_SPARSE_HEADER), &bytesRead, NULL))
  1289. {
  1290. PrintError(GetLastError(), "Cannot read existing sparse header");
  1291. goto Cleanup;
  1292. }
  1293.  
  1294. if (bytesRead != sizeof(VHD_SPARSE_HEADER))
  1295. {
  1296. PrintError(ERROR_HANDLE_EOF, "Cannot read existing sparse header");
  1297. goto Cleanup;
  1298. }
  1299. status = TRUE;
  1300.  
  1301. Cleanup:
  1302.  
  1303. if (savedCurrentPosition)
  1304. {
  1305. SetFilePointerEx(FileHandle, savedPosition, NULL, FILE_BEGIN);
  1306. }
  1307.  
  1308. return status;
  1309. }
  1310.  
  1311.  
  1312. BOOL
  1313. WriteFooter(
  1314. __in HANDLE FileHandle,
  1315. __in UINT64 FooterOffset,
  1316. __in VHD_DISK_FOOTER* Footer
  1317. )
  1318. /*++
  1319.  
  1320. Routine Description:
  1321.  
  1322. Writes a VHD footer to an open VHD file
  1323.  
  1324. Arguments:
  1325.  
  1326. FileHandle - handle to the open VHD file.
  1327.  
  1328. FooterOffset - file offset to write the footer object within the file.
  1329.  
  1330. Footer - Footer object in big-endian format to write to the file.
  1331.  
  1332. Return Value:
  1333.  
  1334. BOOL - TRUE on success, FALSE on failure
  1335.  
  1336. --*/
  1337. {
  1338. LARGE_INTEGER savedPosition;
  1339. LARGE_INTEGER footerPosition;
  1340. DWORD bytesWritten;
  1341. BOOL savedCurrentPosition = FALSE;
  1342. BOOL status = FALSE;
  1343.  
  1344. //
  1345. // Save the current file pointer
  1346. //
  1347. savedPosition.QuadPart = 0;
  1348. if (!SetFilePointerEx(FileHandle, savedPosition, &savedPosition, FILE_CURRENT))
  1349. {
  1350. PrintError(GetLastError(), "Unable to get current file pointer");
  1351. goto Cleanup;
  1352. }
  1353. savedCurrentPosition = TRUE;
  1354.  
  1355.  
  1356. //
  1357. // Seek to the offset in the file
  1358. //
  1359. footerPosition.QuadPart = FooterOffset;
  1360. if (!SetFilePointerEx(FileHandle, footerPosition, NULL, FILE_BEGIN))
  1361. {
  1362. PrintError(GetLastError(), "Unable to set file pointer to footer offset");
  1363. goto Cleanup;
  1364. }
  1365.  
  1366. //
  1367. // Write the footer
  1368. //
  1369. if (!WriteFile(FileHandle, (PVOID)Footer, sizeof(VHD_DISK_FOOTER), &bytesWritten, NULL))
  1370. {
  1371. PrintError(GetLastError(), "Unable to write VHD footer");
  1372. goto Cleanup;
  1373. }
  1374.  
  1375. if (bytesWritten != sizeof(VHD_DISK_FOOTER))
  1376. {
  1377. PrintError(ERROR_WRITE_FAULT, "Bytes written count unexpected");
  1378. goto Cleanup;
  1379. }
  1380.  
  1381. PrintStatus("VHD footer written to file.");
  1382.  
  1383. status = TRUE;
  1384.  
  1385. Cleanup:
  1386.  
  1387. if (savedCurrentPosition)
  1388. {
  1389. SetFilePointerEx(FileHandle, savedPosition, NULL, FILE_BEGIN);
  1390. }
  1391.  
  1392. return status;
  1393. }
  1394.  
  1395.  
  1396. BOOL
  1397. SetFileLength(
  1398. __in HANDLE FileHandle,
  1399. __in UINT64 FileLength
  1400. )
  1401. /*++
  1402.  
  1403. Routine Description:
  1404.  
  1405. Sets the file length of the given file, potentially
  1406. truncating it. It does not set the valid data length.
  1407.  
  1408. Arguments:
  1409.  
  1410. FileHandle - handle to the open VHD file.
  1411.  
  1412. FileLength - file length to set.
  1413.  
  1414. Return Value:
  1415.  
  1416. BOOL - TRUE on success, FALSE on failure
  1417.  
  1418. --*/
  1419. {
  1420. LARGE_INTEGER savedPosition;
  1421. LARGE_INTEGER fileSize;
  1422. BOOL savedCurrentPosition = FALSE;
  1423. BOOL status = FALSE;
  1424.  
  1425. //
  1426. // Save the current file pointer
  1427. //
  1428. savedPosition.QuadPart = 0;
  1429. if (!SetFilePointerEx(FileHandle, savedPosition, &savedPosition, FILE_CURRENT))
  1430. {
  1431. PrintError(GetLastError(), "Unable to get current file pointer");
  1432. goto Cleanup;
  1433. }
  1434. savedCurrentPosition = TRUE;
  1435.  
  1436. //
  1437. // If the saved file pointer is beyond the new end-of-file, don't
  1438. // restore the saved position on exit. Instead, leave the file
  1439. // pointer at the new EOF.
  1440. //
  1441. if ((UINT64)savedPosition.QuadPart > FileLength)
  1442. {
  1443. savedCurrentPosition = FALSE;
  1444. }
  1445.  
  1446. //
  1447. // Move the file pointer to the new end-of-file.
  1448. //
  1449. fileSize.QuadPart = FileLength;
  1450.  
  1451. if (!SetFilePointerEx(FileHandle, fileSize, NULL, FILE_BEGIN))
  1452. {
  1453. PrintError(GetLastError(), "Unable to set file pointer to new end of file");
  1454. goto Cleanup;
  1455. }
  1456.  
  1457. //
  1458. // Set EOF
  1459. //
  1460. if (!SetEndOfFile(FileHandle))
  1461. {
  1462. PrintError(GetLastError(), "Unable to set EOF");
  1463. goto Cleanup;
  1464. }
  1465.  
  1466. PrintStatus("Set the file length");
  1467.  
  1468. status = TRUE;
  1469.  
  1470. Cleanup:
  1471.  
  1472. if (savedCurrentPosition)
  1473. {
  1474. SetFilePointerEx(FileHandle, savedPosition, NULL, FILE_BEGIN);
  1475. }
  1476.  
  1477. return status;
  1478. }
  1479.  
  1480.  
  1481. //
  1482. // Main handler functions
  1483. //
  1484.  
  1485.  
  1486. BOOL
  1487. ConvertToVhd(
  1488. __in LPTSTR Filename
  1489. )
  1490. /*++
  1491.  
  1492. Routine Description:
  1493.  
  1494. Adds a VHD footer and clears the first few MB of an existing file.
  1495.  
  1496. Arguments:
  1497.  
  1498. Filename - name of the file to convert to a VHD.
  1499.  
  1500. Return Value:
  1501.  
  1502. BOOL - TRUE on success, FALSE on failure
  1503.  
  1504. --*/
  1505. {
  1506. VHD_DISK_FOOTER footer;
  1507. LARGE_INTEGER fileSize;
  1508. HANDLE fileHandle = INVALID_HANDLE_VALUE;
  1509. GUID uniqueId;
  1510. BOOL status = FALSE;
  1511.  
  1512. PrintStatus("Converting \"%S\" to a fixed format VHD.", Filename);
  1513.  
  1514. //
  1515. // Open the file
  1516. //
  1517. if (!OpenVhdFile(Filename, &fileHandle, &fileSize))
  1518. {
  1519. goto Cleanup;
  1520. }
  1521.  
  1522. //
  1523. // Error if file is too small
  1524. //
  1525. if (fileSize.QuadPart < (1024 * 1024 * 12))
  1526. {
  1527. PrintError(ERROR_HANDLE_EOF, "File is smaller than 12 MB.");
  1528. goto Cleanup;
  1529. }
  1530.  
  1531. //
  1532. // Round up to the nearest sector size
  1533. //
  1534. RoundUpToSectorSize(&fileSize);
  1535.  
  1536. //
  1537. // Create the footer
  1538. //
  1539. UuidCreate(&uniqueId);
  1540.  
  1541. if (!CreateVhdFooter(fileSize.QuadPart, &uniqueId, TRUE, 0, &footer))
  1542. {
  1543. goto Cleanup;
  1544. }
  1545.  
  1546. //
  1547. // Append the footer
  1548. //
  1549. if (!WriteFooter(fileHandle, fileSize.QuadPart, &footer))
  1550. {
  1551. goto Cleanup;
  1552. }
  1553.  
  1554. status = TRUE;
  1555.  
  1556. Cleanup:
  1557.  
  1558. if (fileHandle != INVALID_HANDLE_VALUE)
  1559. {
  1560. CloseHandle(fileHandle);
  1561. fileHandle = INVALID_HANDLE_VALUE;
  1562. }
  1563.  
  1564. return status;
  1565. }
  1566.  
  1567.  
  1568. BOOL
  1569. CreateNewVhd(
  1570. __in LPTSTR Filename,
  1571. __in UINT64 Filesize
  1572. )
  1573. /*++
  1574.  
  1575. Routine Description:
  1576.  
  1577. Adds a VHD footer and clears the first few MB of an existing file.
  1578.  
  1579. Arguments:
  1580.  
  1581. Filename - name of the file to create.
  1582.  
  1583. Filesize - size of the resulting data section.
  1584.  
  1585. Return Value:
  1586.  
  1587. BOOL - TRUE on success, FALSE on failures
  1588.  
  1589. --*/
  1590. {
  1591. VHD_DISK_FOOTER footer;
  1592. LARGE_INTEGER filePointer;
  1593. HANDLE fileHandle = INVALID_HANDLE_VALUE;
  1594. GUID uniqueId;
  1595. BOOL status = FALSE;
  1596.  
  1597. PrintStatus("Creating new fixed format VHD with name \"%S\"", Filename);
  1598.  
  1599. //
  1600. // Error if file is too small
  1601. //
  1602. if (Filesize < (1024 * 1024 * 12))
  1603. {
  1604. PrintError(ERROR_HANDLE_EOF, "File is smaller than 12 MB.");
  1605. goto Cleanup;
  1606. }
  1607.  
  1608. //
  1609. // Enable SE_MANAGE_VOLUME privilege.
  1610. //
  1611. if (!EnablePrivilege(SE_MANAGE_VOLUME_NAME))
  1612. {
  1613. PrintStatus("Must be run as an administrator or a user with SE_MANAGE_VOLUME privilege. Try running from an elevated command prompt.");
  1614. goto Cleanup;
  1615. }
  1616.  
  1617. //
  1618. // Create the file
  1619. //
  1620. PrintStatus("Attempting to create file \"%S\"", Filename);
  1621.  
  1622. fileHandle = CreateFile(Filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
  1623. if (fileHandle == INVALID_HANDLE_VALUE)
  1624. {
  1625. PrintError(GetLastError(), "Unable to create file \"%S\"", Filename);
  1626. goto Cleanup;
  1627. }
  1628.  
  1629. PrintStatus("Created file \"%S\"", Filename);
  1630.  
  1631. //
  1632. // Round up to the nearest sector size
  1633. //
  1634. filePointer.QuadPart = Filesize;
  1635. RoundUpToSectorSize(&filePointer);
  1636.  
  1637. //
  1638. // Set the file length
  1639. //
  1640. if (!SetFileLength(fileHandle, filePointer.QuadPart))
  1641. {
  1642. goto Cleanup;
  1643. }
  1644.  
  1645. //
  1646. // Set the valid data length
  1647. //
  1648. if (!SetFileValidData(fileHandle, filePointer.QuadPart))
  1649. {
  1650. PrintError(GetLastError(), "Unable to set valid data length");
  1651. goto Cleanup;
  1652. }
  1653.  
  1654. PrintStatus("Set the valid data length");
  1655.  
  1656. //
  1657. // Create the footer
  1658. //
  1659. UuidCreate(&uniqueId);
  1660.  
  1661. if (!CreateVhdFooter(filePointer.QuadPart, &uniqueId, TRUE, 0, &footer))
  1662. {
  1663. goto Cleanup;
  1664. }
  1665.  
  1666. //
  1667. // Append the footer
  1668. //
  1669. if (!WriteFooter(fileHandle, filePointer.QuadPart, &footer))
  1670. {
  1671. goto Cleanup;
  1672. }
  1673.  
  1674. //
  1675. // Clear out first 12 MB of file.
  1676. //
  1677. filePointer.QuadPart = 0;
  1678. if (!ZeroFileContents(fileHandle, filePointer, 12 * 1024 * 1024))
  1679. {
  1680. goto Cleanup;
  1681. }
  1682.  
  1683. PrintStatus("VHD header area cleared.");
  1684.  
  1685. status = TRUE;
  1686.  
  1687. Cleanup:
  1688.  
  1689. if (fileHandle != INVALID_HANDLE_VALUE)
  1690. {
  1691. CloseHandle(fileHandle);
  1692. fileHandle = INVALID_HANDLE_VALUE;
  1693. }
  1694.  
  1695. return status;
  1696. }
  1697.  
  1698.  
  1699. BOOL
  1700. ExtendVhd(
  1701. __in LPTSTR Filename,
  1702. __in UINT64 Filesize
  1703. )
  1704. /*++
  1705.  
  1706. Routine Description:
  1707.  
  1708. Adds a VHD footer and clears the first few MB of an existing file.
  1709.  
  1710. Arguments:
  1711.  
  1712. Filename - name of the VHD file to extend.
  1713.  
  1714. Filesize - new virtual size of the VHD.
  1715.  
  1716. Return Value:
  1717.  
  1718. BOOL - TRUE on success, FALSE on failure
  1719.  
  1720. --*/
  1721. {
  1722. VHD_DISK_FOOTER footer;
  1723. LARGE_INTEGER originalFileSize;
  1724. LARGE_INTEGER newFileSize;
  1725. HANDLE fileHandle = INVALID_HANDLE_VALUE;
  1726. GUID uniqueId;
  1727. BOOL status = FALSE;
  1728.  
  1729. PrintStatus("Extending \"%S\" to a larger size.", Filename);
  1730.  
  1731. //
  1732. // Enable SE_MANAGE_VOLUME privilege.
  1733. //
  1734. if (!EnablePrivilege(SE_MANAGE_VOLUME_NAME))
  1735. {
  1736. PrintStatus("Must be run as an administrator or a user with SE_MANAGE_VOLUME privilege. Try running from an elevated command prompt.");
  1737. goto Cleanup;
  1738. }
  1739.  
  1740. //
  1741. // Open the file
  1742. //
  1743. if (!OpenVhdFile(Filename, &fileHandle, &originalFileSize))
  1744. {
  1745. goto Cleanup;
  1746. }
  1747.  
  1748. //
  1749. // Error if file is too small to operate on.
  1750. //
  1751. if (originalFileSize.QuadPart < (1024 * 1024 * 12))
  1752. {
  1753. PrintError(ERROR_HANDLE_EOF, "File is smaller than 12 MB.");
  1754. goto Cleanup;
  1755. }
  1756.  
  1757. //
  1758. // Error if a shrink operation is attempted
  1759. //
  1760. if ((UINT64)originalFileSize.QuadPart > Filesize)
  1761. {
  1762. PrintError(ERROR_HANDLE_EOF, "Cannot extend a VHD smaller than its current size");
  1763. goto Cleanup;
  1764. }
  1765.  
  1766. //
  1767. // Read the existing footer.
  1768. //
  1769. originalFileSize.QuadPart -= VHD_SECTOR_SIZE;
  1770. if (!ReadFooter(fileHandle, originalFileSize.QuadPart, &footer))
  1771. {
  1772. goto Cleanup;
  1773. }
  1774.  
  1775. RtlCopyMemory(&uniqueId, footer.UniqueId, sizeof(GUID));
  1776.  
  1777. //
  1778. // Round the extension up to the nearest sector size
  1779. //
  1780. newFileSize.QuadPart = Filesize;
  1781. RoundUpToSectorSize(&newFileSize);
  1782.  
  1783. //
  1784. // Set the file length
  1785. //
  1786. if (!SetFileLength(fileHandle, newFileSize.QuadPart))
  1787. {
  1788. goto Cleanup;
  1789. }
  1790.  
  1791. //
  1792. // Set the valid data length
  1793. //
  1794. if (!SetFileValidData(fileHandle, newFileSize.QuadPart))
  1795. {
  1796. PrintError(GetLastError(), "Unable to set valid data length");
  1797. goto Cleanup;
  1798. }
  1799.  
  1800. PrintStatus("Set the valid data length");
  1801.  
  1802. //
  1803. // Create the footer
  1804. //
  1805. if (!CreateVhdFooter(newFileSize.QuadPart, &uniqueId, TRUE, 0, &footer))
  1806. {
  1807. goto Cleanup;
  1808. }
  1809.  
  1810. //
  1811. // Append the footer
  1812. //
  1813. if (!WriteFooter(fileHandle, newFileSize.QuadPart, &footer))
  1814. {
  1815. goto Cleanup;
  1816. }
  1817.  
  1818. //
  1819. // Clear out original footer.
  1820. //
  1821. if (!ZeroFileContents(fileHandle, originalFileSize, VHD_SECTOR_SIZE))
  1822. {
  1823. goto Cleanup;
  1824. }
  1825.  
  1826. PrintStatus("Existing footer cleared");
  1827.  
  1828. status = TRUE;
  1829.  
  1830. Cleanup:
  1831.  
  1832. if (fileHandle != INVALID_HANDLE_VALUE)
  1833. {
  1834. CloseHandle(fileHandle);
  1835. fileHandle = INVALID_HANDLE_VALUE;
  1836. }
  1837.  
  1838. return status;
  1839. }
  1840.  
  1841.  
  1842. BOOL
  1843. RepairVhd(
  1844. __in LPTSTR Filename,
  1845. __in LPTSTR ChildFilename
  1846. )
  1847. /*++
  1848.  
  1849. Routine Description:
  1850.  
  1851. Repairs a VHD chain broken by an extend operation on the base VHD.
  1852. Resets the base VHD size to match the child.
  1853.  
  1854. Arguments:
  1855.  
  1856. Filename - name of the base VHD file to repair.
  1857.  
  1858. ChildFilename - name of the next child VHD in the disk chain.
  1859.  
  1860. Return Value:
  1861.  
  1862. BOOL - TRUE on success, FALSE on failure
  1863.  
  1864. --*/
  1865. {
  1866. VHD_SPARSE_HEADER childSparseHeader;
  1867. VHD_DISK_FOOTER footer;
  1868. VHD_DISK_FOOTER childFooter;
  1869. LARGE_INTEGER fileSize;
  1870. LARGE_INTEGER childFileSize;
  1871. HANDLE fileHandle = INVALID_HANDLE_VALUE;
  1872. HANDLE childFileHandle = INVALID_HANDLE_VALUE;
  1873. DWORD bytesWritten = 0;
  1874. BOOL status = FALSE;
  1875.  
  1876. PrintStatus("Resizing base VHD \"%S\" to match the size indicated in child VHD \"%S\".", Filename, ChildFilename);
  1877.  
  1878. //
  1879. // Open the base file
  1880. //
  1881. if (!OpenVhdFile(Filename, &fileHandle, &fileSize))
  1882. {
  1883. goto Cleanup;
  1884. }
  1885.  
  1886. //
  1887. // Open the child file
  1888. //
  1889. if (!OpenVhdFile(ChildFilename, &childFileHandle, &childFileSize))
  1890. {
  1891. goto Cleanup;
  1892. }
  1893.  
  1894. //
  1895. // Read the base VHD footer.
  1896. //
  1897. fileSize.QuadPart -= VHD_SECTOR_SIZE;
  1898. if (!ReadFooter(fileHandle, fileSize.QuadPart, &footer))
  1899. {
  1900. goto Cleanup;
  1901. }
  1902.  
  1903. //
  1904. // Verify we have a fixed or dynamic VHD as the base.
  1905. //
  1906. switch (footer.DiskType)
  1907. {
  1908. case VHD_TYPE_BIG_FIXED:
  1909. PrintStatus("Opened \"%S\" as base VHD file, type is fixed-sized.", Filename);
  1910. break;
  1911.  
  1912. case VHD_TYPE_BIG_SPARSE:
  1913. PrintStatus("Opened \"%S\" as base VHD file, type is dynamic-sized.", Filename);
  1914. break;
  1915.  
  1916. case VHD_TYPE_BIG_DIFF:
  1917. PrintError(ERROR_BAD_FILE_TYPE, "VHD \"%S\" is a differencing disk. You must enter the base VHD for this task.", Filename);
  1918. goto Cleanup;
  1919.  
  1920. default:
  1921. PrintError(ERROR_BAD_FILE_TYPE, "VHD \"%S\" is an unsupported type.", Filename);
  1922. goto Cleanup;
  1923. }
  1924.  
  1925. GUID* baseIdentifier = (GUID*)&footer.UniqueId[0];
  1926. PrintVhdIdentifier(baseIdentifier, "Base VHD's identifier is \"%S\"");
  1927.  
  1928. //
  1929. // Read the child VHD's footer.
  1930. //
  1931. childFileSize.QuadPart -= VHD_SECTOR_SIZE;
  1932. if (!ReadFooter(childFileHandle, childFileSize.QuadPart, &childFooter))
  1933. {
  1934. goto Cleanup;
  1935. }
  1936.  
  1937. //
  1938. // Verify we have a differencing disk as the child.
  1939. //
  1940. if (childFooter.DiskType != VHD_TYPE_BIG_DIFF)
  1941. {
  1942. PrintError(ERROR_BAD_FILE_TYPE, "Child VHD \"%S\" is not a differencing VHD.", ChildFilename);
  1943. goto Cleanup;
  1944. }
  1945.  
  1946. PrintStatus("Opened \"%S\" as child VHD file.", ChildFilename);
  1947.  
  1948. //
  1949. // Retrieve the child's sparse header and parent pointer information
  1950. //
  1951. if (!ReadSparseHeader(childFileHandle, &childFooter, &childSparseHeader))
  1952. {
  1953. goto Cleanup;
  1954. }
  1955.  
  1956. GUID* parentIdentifier = (GUID*)&childSparseHeader.ParentUniqueId[0];
  1957. PrintVhdIdentifier(parentIdentifier, "Child VHD's parent identifier is \"%S\"");
  1958.  
  1959. //
  1960. // Verify that the child's parent pointer matches the parent id.
  1961. //
  1962.  
  1963. if (0 != memcmp(parentIdentifier, &footer.UniqueId[0], sizeof(GUID)))
  1964. {
  1965. PrintError(ERROR_HANDLE_EOF, "Child's parent identifier doesn't match the base VHD's identifier.");
  1966. goto Cleanup;
  1967. }
  1968.  
  1969. //
  1970. // Determine the correct size for the base VHD.
  1971. //
  1972.  
  1973. PrintStatus("Resizing base VHD to match child size of %I64u bytes", _byteswap_uint64(childFooter.CurrentSize));
  1974.  
  1975. //
  1976. // Resize the parent VHD to match the expected size.
  1977. //
  1978.  
  1979. if (footer.DiskType == VHD_TYPE_BIG_FIXED)
  1980. {
  1981. //
  1982. // Fixed VHDs only contain a footer. Truncate the file
  1983. // and write the new footer.
  1984. //
  1985.  
  1986. LARGE_INTEGER newFileSize;
  1987. newFileSize.QuadPart = _byteswap_uint64(childFooter.CurrentSize);
  1988.  
  1989. //
  1990. // Set the file length
  1991. //
  1992. if (!SetFileLength(fileHandle, newFileSize.QuadPart))
  1993. {
  1994. goto Cleanup;
  1995. }
  1996.  
  1997. PrintStatus("Base VHD truncated to new size %I64u.", newFileSize.QuadPart);
  1998.  
  1999. //
  2000. // Create the footer
  2001. //
  2002. if (!CreateVhdFooter(newFileSize.QuadPart, parentIdentifier, TRUE, 0, &footer))
  2003. {
  2004. goto Cleanup;
  2005. }
  2006.  
  2007. //
  2008. // Write the footer
  2009. //
  2010. if (!WriteFooter(fileHandle, newFileSize.QuadPart, &footer))
  2011. {
  2012. goto Cleanup;
  2013. }
  2014. }
  2015. else
  2016. {
  2017. //
  2018. // Sparse VHDs contain a footer and a duplicate header.
  2019. // Update both of these, but leave the file size intact.
  2020. //
  2021.  
  2022. //
  2023. // Create the footer
  2024. //
  2025. VHD_DISK_FOOTER newFooter;
  2026. if (!CreateVhdFooter(_byteswap_uint64(childFooter.CurrentSize), parentIdentifier, FALSE, footer.DataOffset, &newFooter))
  2027. {
  2028. goto Cleanup;
  2029. }
  2030.  
  2031. //
  2032. // Write the footer to the beginning and end of the VHD.
  2033. //
  2034. if (!GetFileSizeEx(fileHandle, &fileSize))
  2035. {
  2036. PrintError(GetLastError(), "Unable to retrieve file size");
  2037. goto Cleanup;
  2038. }
  2039.  
  2040. if (!WriteFooter(fileHandle, fileSize.QuadPart - VHD_SECTOR_SIZE, &newFooter))
  2041. {
  2042. goto Cleanup;
  2043. }
  2044.  
  2045. if (!WriteFooter(fileHandle, 0, &newFooter))
  2046. {
  2047. goto Cleanup;
  2048. }
  2049. }
  2050.  
  2051. PrintStatus("Operation complete.");
  2052. status = TRUE;
  2053.  
  2054. Cleanup:
  2055.  
  2056. if (fileHandle != INVALID_HANDLE_VALUE)
  2057. {
  2058. CloseHandle(fileHandle);
  2059. fileHandle = INVALID_HANDLE_VALUE;
  2060. }
  2061.  
  2062. if (childFileHandle != INVALID_HANDLE_VALUE)
  2063. {
  2064. CloseHandle(childFileHandle);
  2065. childFileHandle = INVALID_HANDLE_VALUE;
  2066. }
  2067.  
  2068. return status;
  2069. }
  2070.  
  2071.  
  2072. int
  2073. _tmain(
  2074. __in int argc,
  2075. __in _TCHAR* argv[]
  2076. )
  2077. /*++
  2078.  
  2079. Routine Description:
  2080.  
  2081. Entrypoint for the tool
  2082.  
  2083. Arguments:
  2084.  
  2085. argc - Number of command line arguments
  2086.  
  2087. argv - Array of command line arguments
  2088.  
  2089. Return Value:
  2090.  
  2091. int - 0 on success, >0 on failure
  2092.  
  2093. --*/
  2094. {
  2095. PROGRAM_MODE programMode;
  2096. LPTSTR filename = NULL;
  2097. LPTSTR childFilename = NULL;
  2098. UINT64 filesize = 0;
  2099. int status = 1;
  2100.  
  2101. //
  2102. // Parse command line
  2103. //
  2104. if (!ParseCommandLine(argc, argv, &programMode, &filename, &filesize, &childFilename))
  2105. {
  2106. goto Cleanup;
  2107. }
  2108.  
  2109. switch(programMode)
  2110. {
  2111. case ProgramModeCreate:
  2112. if (!CreateNewVhd(filename, filesize))
  2113. {
  2114. goto Cleanup;
  2115. }
  2116. break;
  2117.  
  2118. case ProgramModeConvert:
  2119. if (!ConvertToVhd(filename))
  2120. {
  2121. goto Cleanup;
  2122. }
  2123. break;
  2124.  
  2125. case ProgramModeExtend:
  2126. if (!ExtendVhd(filename, filesize))
  2127. {
  2128. goto Cleanup;
  2129. }
  2130. break;
  2131.  
  2132. case ProgramModeRepair:
  2133. if (!RepairVhd(filename, childFilename))
  2134. {
  2135. goto Cleanup;
  2136. }
  2137. break;
  2138.  
  2139. default:
  2140. PrintStatus("Unknown Command");
  2141. goto Cleanup;
  2142. }
  2143.  
  2144. PrintStatus("Complete");
  2145.  
  2146. status = 0;
  2147.  
  2148. Cleanup:
  2149.  
  2150. if (filename != NULL)
  2151. {
  2152. free(filename);
  2153. filename = NULL;
  2154. }
  2155.  
  2156. if (childFilename != NULL)
  2157. {
  2158. free(childFilename);
  2159. childFilename = NULL;
  2160. }
  2161.  
  2162. return status;
  2163. }
  2164.  
  2165.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement