Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

429 lines
11 KiB

  1. /*++
  2. Copyright (C) 2001 Microsoft Corporation
  3. Module Name:
  4. ftszchk.c
  5. Abstract:
  6. This utility checks the disks on a Windows NT 4.0 system looking for those
  7. that cannot be converted to dynamic after upgrading the system to Windows
  8. 2000. The main reason why a disk cannot be converted to dynamic is the lack
  9. of 1MB free space at the end of the disk.
  10. Author:
  11. Cristian G. Teodorescu (cristiat) 29-Jan-2002
  12. Environment:
  13. User mode.
  14. Notes:
  15. Revision History:
  16. --*/
  17. #include <windows.h>
  18. #include <stdio.h>
  19. #include <winioctl.h>
  20. #include <ntddvol.h>
  21. #define PRIVATE_REGION_SIZE 0x100000 // 1MB
  22. #define SECTOR 0x200
  23. #define MESSAGE "The ftszchk.exe utility wrote this sector"
  24. void
  25. PrintUsage(
  26. IN char* ProgramName
  27. )
  28. {
  29. printf("usage: %s [disk_number]\n", ProgramName);
  30. }
  31. BOOLEAN
  32. AccessSector(
  33. IN HANDLE Handle,
  34. IN LONGLONG Offset
  35. )
  36. /*++
  37. Routine Description:
  38. This routine tries to access the given disk sector.
  39. Arguments:
  40. Handle - Supplies the disk handle.
  41. Offset - Supplies the offset of the sector.
  42. Return Value:
  43. TRUE if the sector is accessible.
  44. --*/
  45. {
  46. LARGE_INTEGER pointer;
  47. DWORD err;
  48. CHAR saveBuffer[SECTOR];
  49. CHAR buffer[SECTOR];
  50. BOOL b;
  51. DWORD bytes;
  52. //
  53. // Save the old content of the sector.
  54. //
  55. pointer.QuadPart = Offset;
  56. pointer.LowPart = SetFilePointer(Handle, pointer.LowPart,
  57. &pointer.HighPart, FILE_BEGIN);
  58. err = GetLastError();
  59. if (pointer.LowPart == INVALID_SET_FILE_POINTER && err != NO_ERROR) {
  60. return FALSE;
  61. }
  62. memset(saveBuffer, 0, SECTOR);
  63. b = ReadFile(Handle, saveBuffer, SECTOR, &bytes, NULL);
  64. if (!b || bytes != SECTOR) {
  65. return FALSE;
  66. }
  67. //
  68. // Write the sector.
  69. //
  70. pointer.QuadPart = Offset;
  71. pointer.LowPart = SetFilePointer(Handle, pointer.LowPart,
  72. &pointer.HighPart, FILE_BEGIN);
  73. err = GetLastError();
  74. if (pointer.LowPart == INVALID_SET_FILE_POINTER && err != NO_ERROR) {
  75. return FALSE;
  76. }
  77. memset(buffer, 0, SECTOR);
  78. sprintf(buffer, MESSAGE);
  79. b = WriteFile(Handle, buffer, SECTOR, &bytes, NULL);
  80. if (!b || bytes != SECTOR) {
  81. return FALSE;
  82. }
  83. //
  84. // Read the sector and compare the result with what we've wrriten.
  85. //
  86. pointer.QuadPart = Offset;
  87. pointer.LowPart = SetFilePointer(Handle, pointer.LowPart,
  88. &pointer.HighPart, FILE_BEGIN);
  89. err = GetLastError();
  90. if (pointer.LowPart == INVALID_SET_FILE_POINTER && err != NO_ERROR) {
  91. return FALSE;
  92. }
  93. memset(buffer, 0, SECTOR);
  94. b = ReadFile(Handle, buffer, SECTOR, &bytes, NULL);
  95. if (!b || bytes != SECTOR) {
  96. return FALSE;
  97. }
  98. if (memcmp(buffer, MESSAGE, strlen(MESSAGE))) {
  99. return FALSE;
  100. }
  101. //
  102. // Try to restore the old content of the sector.
  103. //
  104. pointer.QuadPart = Offset;
  105. pointer.LowPart = SetFilePointer(Handle, pointer.LowPart,
  106. &pointer.HighPart, FILE_BEGIN);
  107. err = GetLastError();
  108. if (pointer.LowPart == INVALID_SET_FILE_POINTER && err != NO_ERROR) {
  109. return TRUE;
  110. }
  111. WriteFile(Handle, saveBuffer, SECTOR, &bytes, NULL);
  112. return TRUE;
  113. }
  114. BOOLEAN
  115. ProcessDisk(
  116. IN ULONG DiskNumber,
  117. OUT PBOOLEAN ContainsFT
  118. )
  119. /*++
  120. Routine Description:
  121. This routine checks whether the given disk can be converted to dynamic or
  122. not.
  123. Arguments:
  124. DiskNumber - Supplies the disk NT device number.
  125. ContainsFT - Returns TRUE if the disk contains FT partitions.
  126. Return Value:
  127. FALSE if opening the disk failed with ERROR_FILE_NOT_FOUND. TRUE otherwise.
  128. --*/
  129. {
  130. WCHAR diskDevice[64];
  131. HANDLE h;
  132. DISK_GEOMETRY geometry;
  133. ULONG layoutSize;
  134. PDRIVE_LAYOUT_INFORMATION layout;
  135. PPARTITION_INFORMATION partition;
  136. ULONG i;
  137. BOOL b;
  138. DWORD bytes;
  139. DWORD err;
  140. UCHAR type;
  141. BOOLEAN ft = FALSE, dynamic = FALSE;
  142. LONGLONG lastEnd, end, privateEnd, geoSize, ioOffset;
  143. *ContainsFT = FALSE;
  144. //
  145. // Get a handle to the disk
  146. //
  147. swprintf(diskDevice, L"\\\\.\\PHYSICALDRIVE%lu", DiskNumber);
  148. h = CreateFileW(diskDevice, GENERIC_READ | GENERIC_WRITE,
  149. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  150. NULL, OPEN_EXISTING, 0, NULL);
  151. if (h == INVALID_HANDLE_VALUE) {
  152. err = GetLastError();
  153. if (err == ERROR_FILE_NOT_FOUND) {
  154. return FALSE;
  155. }
  156. printf("Disk%3lu: The disk was inaccessible due to error %lu.\n",
  157. DiskNumber, err);
  158. return TRUE;
  159. }
  160. printf("Disk%3lu: ", DiskNumber);
  161. //
  162. // Get the drive geometry
  163. //
  164. b = DeviceIoControl(h, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geometry,
  165. sizeof(geometry), &bytes, NULL);
  166. if (!b) {
  167. printf("The disk geometry was inaccessible due to error %lu.\n",
  168. GetLastError());
  169. CloseHandle(h);
  170. return TRUE;
  171. }
  172. //
  173. // Get the drive layout
  174. //
  175. layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) +
  176. 128 * sizeof(PARTITION_INFORMATION);
  177. layout = (PDRIVE_LAYOUT_INFORMATION) LocalAlloc(LMEM_ZEROINIT, layoutSize);
  178. if (!layout) {
  179. printf("The disk was inaccessible due to memory allocation error.\n");
  180. CloseHandle(h);
  181. return TRUE;
  182. }
  183. while (TRUE) {
  184. b = DeviceIoControl(h, IOCTL_DISK_GET_DRIVE_LAYOUT, NULL, 0, layout,
  185. layoutSize, &bytes, NULL);
  186. if (b) {
  187. break;
  188. }
  189. err = GetLastError();
  190. LocalFree(layout);
  191. if (err != ERROR_INSUFFICIENT_BUFFER) {
  192. printf("The disk partition table was inaccessible due to error %lu.\n",
  193. err);
  194. CloseHandle(h);
  195. return TRUE;
  196. }
  197. layoutSize += 64 * sizeof(PARTITION_INFORMATION);
  198. layout = (PDRIVE_LAYOUT_INFORMATION) LocalAlloc(LMEM_ZEROINIT,
  199. layoutSize);
  200. if (!layout) {
  201. printf("The disk was inaccessible due to memory allocation error.\n");
  202. CloseHandle(h);
  203. return TRUE;
  204. }
  205. }
  206. //
  207. // Scan the partition table. Look for FT partitions or dynamic partitions.
  208. // Find the last partititon end offset.
  209. //
  210. lastEnd = 0;
  211. for (i = 0; i < layout->PartitionCount; i++) {
  212. partition = &layout->PartitionEntry[i];
  213. type = partition->PartitionType;
  214. if (type == 0 || IsContainerPartition(type)) {
  215. continue;
  216. }
  217. if (type == PARTITION_LDM) {
  218. dynamic = TRUE;
  219. }
  220. if (IsFTPartition(type)) {
  221. ft = TRUE;
  222. }
  223. end = partition->StartingOffset.QuadPart +
  224. partition->PartitionLength.QuadPart;
  225. if (end > lastEnd) {
  226. lastEnd = end;
  227. }
  228. }
  229. LocalFree(layout);
  230. if (ft) {
  231. printf("NT 4.0 basic volumes present. ");
  232. }
  233. *ContainsFT = ft;
  234. //
  235. // Dell ships LDM on Windows NT 4.0. Make sure we don't touch dynamic disks.
  236. //
  237. if (dynamic) {
  238. printf("The disk is a dynamic disk.\n");
  239. CloseHandle(h);
  240. return TRUE;
  241. }
  242. //
  243. // Only disks with 512 bytes/sector can be converted to dynamic
  244. //
  245. if (geometry.BytesPerSector != SECTOR) {
  246. printf("The disk does not have a 512 sector size and cannot be converted to a dynamic disk.\n");
  247. CloseHandle(h);
  248. return TRUE;
  249. }
  250. //
  251. // Check whether there is enough space left at the end of the disk to
  252. // convert it to dynamic.
  253. //
  254. // Given there is no way to get the real size of the disk in Windows NT 4.0
  255. // we use the following algorithm:
  256. //
  257. // 1. Get the geometric size.
  258. // 2. Add one MB (the size of the LDM private region) to the last
  259. // partition end offset.
  260. // 3. If the result is within the geometric size STOP. The disk is large
  261. // enough to be converted.
  262. // 4. Try to access the sector that ends at the offset calculated at step 2.
  263. // If the access succeeds STOP. The disk is large enough to be converted.
  264. // 5. Try to access some more sectors at higher offsets. If one of them
  265. // succeeds STOP. The disk is large enough to be converted.
  266. // 6. If all attempts failed the disk is probably not large enough to be
  267. // converted.
  268. //
  269. geoSize = geometry.Cylinders.QuadPart * geometry.TracksPerCylinder *
  270. geometry.SectorsPerTrack * geometry.BytesPerSector;
  271. privateEnd = lastEnd + PRIVATE_REGION_SIZE;
  272. if (privateEnd > geoSize) {
  273. ioOffset = privateEnd - SECTOR;
  274. for (i = 0; i < 4; i++) {
  275. if (AccessSector(h, ioOffset)) {
  276. break;
  277. }
  278. ioOffset += 0x1000;
  279. }
  280. if (i == 4) {
  281. printf("The disk does not have enough free space to be converted to a dynamic disk.\n");
  282. CloseHandle(h);
  283. return TRUE;
  284. }
  285. }
  286. printf("The disk may be converted to a dynamic disk.\n");
  287. CloseHandle(h);
  288. return TRUE;
  289. }
  290. void __cdecl
  291. main(
  292. int argc,
  293. char** argv
  294. )
  295. {
  296. ULONG diskNumber;
  297. BOOLEAN found;
  298. BOOLEAN ft, ftall = FALSE;
  299. UINT i;
  300. if (argc > 2) {
  301. PrintUsage(argv[0]);
  302. return;
  303. }
  304. if (argc == 2) {
  305. if (sscanf(argv[1], "%lu", &diskNumber) != 1) {
  306. PrintUsage(argv[0]);
  307. return;
  308. }
  309. found = ProcessDisk(diskNumber, &ftall);
  310. if (!found) {
  311. printf("Disk%3lu: The system cannot find the disk specified.\n",
  312. diskNumber);
  313. }
  314. return;
  315. }
  316. //
  317. // Scan all disks starting with disk 0. Stop the search after 20
  318. // consecutive FILE_NOT_FOUND failures to open the disks.
  319. //
  320. for (diskNumber = 0, i = 0; i < 20; diskNumber++) {
  321. found = ProcessDisk(diskNumber, &ft);
  322. if (!found) {
  323. i++;
  324. } else {
  325. i = 0;
  326. if (ft) {
  327. ftall = TRUE;
  328. }
  329. }
  330. }
  331. if (ftall) {
  332. printf("\nIMPORTANT: The utility detected NT 4.0 basic volumes in your system. These volumes might include volume sets, stripe sets, mirror sets and stripe sets with parity. Make sure these volumes are in healthy status before upgrading the operating system to Windows 2000.\n");
  333. }
  334. }