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.

705 lines
20 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. chkntfs.cxx
  5. Abstract:
  6. This utility allows the users to find the state of the dirty bit
  7. on NTFS volumes, to schedule autochk for specific drives, and to
  8. mofidy the default autochk action for a drive.
  9. SYNTAX:
  10. chkntfs drive: [...] -- tells if a drive is dirty or chkdsk has been scheduled
  11. chkntfs /d -- restore default autochk behavior
  12. chkntfs /x drive: [...] -- exclude drives from default autochk
  13. chkntfs /c drive: [...] -- schedule autochk to run on drives
  14. chkntfs /e drive: [...] -- enable automatic volume upgrades on drives
  15. chkntfs /t[:countdowntime] -- display or set autochk countdown time
  16. EXIT:
  17. 0 -- OK, dirty bit not set on drive or bit not checked
  18. 1 -- OK, and dirty bit set on at least one drive
  19. 2 -- Error
  20. Author:
  21. Matthew Bradburn (MattBr) 19-Aug-1996
  22. --*/
  23. #include "ulib.hxx"
  24. #include "arg.hxx"
  25. #include "array.hxx"
  26. #include "path.hxx"
  27. #include "wstring.hxx"
  28. #include "ifssys.hxx"
  29. #include "system.hxx"
  30. #include "arrayit.hxx"
  31. #include "autoreg.hxx"
  32. #include "chkntfs.hxx"
  33. #include "mpmap.hxx"
  34. #include "volume.hxx"
  35. DEFINE_CONSTRUCTOR(CHKNTFS, PROGRAM);
  36. BOOLEAN
  37. CHKNTFS::Initialize(
  38. )
  39. /*++
  40. Routine Description:
  41. Initializes an object of class CHKNTFS. Called once when the program
  42. starts.
  43. Arguments:
  44. None.
  45. Return Value:
  46. BOOLEAN - Indicates whether the initialization succeeded.
  47. --*/
  48. {
  49. ARGUMENT_LEXEMIZER arg_lex;
  50. ARRAY lex_array;
  51. ARRAY argument_array;
  52. STRING_ARGUMENT program_name_argument;
  53. FLAG_ARGUMENT flag_restore_default;
  54. FLAG_ARGUMENT flag_exclude;
  55. FLAG_ARGUMENT flag_schedule_check;
  56. FLAG_ARGUMENT flag_invalid;
  57. FLAG_ARGUMENT flag_display_help;
  58. FLAG_ARGUMENT flag_count_down_time;
  59. LONG_ARGUMENT arg_count_down_time;
  60. ExitStatus = 0;
  61. PROGRAM::Initialize();
  62. if (!argument_array.Initialize()) {
  63. return FALSE;
  64. }
  65. if (!program_name_argument.Initialize("*") ||
  66. !flag_restore_default.Initialize("/D") ||
  67. !flag_exclude.Initialize("/X") ||
  68. !flag_schedule_check.Initialize("/C") ||
  69. !flag_count_down_time.Initialize("/T") ||
  70. !arg_count_down_time.Initialize("/T:*") ||
  71. !flag_display_help.Initialize("/?") ||
  72. !flag_invalid.Initialize("/*") || // close comment */
  73. !_drive_arguments.Initialize("*", FALSE, TRUE)) {
  74. return FALSE;
  75. }
  76. if (!argument_array.Put(&program_name_argument) ||
  77. !argument_array.Put(&flag_display_help) ||
  78. !argument_array.Put(&flag_restore_default) ||
  79. !argument_array.Put(&flag_exclude) ||
  80. !argument_array.Put(&flag_schedule_check) ||
  81. !argument_array.Put(&flag_count_down_time) ||
  82. !argument_array.Put(&arg_count_down_time) ||
  83. !argument_array.Put(&flag_invalid) ||
  84. !argument_array.Put(&_drive_arguments)) {
  85. return FALSE;
  86. }
  87. if (!lex_array.Initialize() ||
  88. !arg_lex.Initialize(&lex_array)) {
  89. return FALSE;
  90. }
  91. arg_lex.PutSwitches("/");
  92. arg_lex.PutStartQuotes("\"");
  93. arg_lex.PutEndQuotes("\"");
  94. arg_lex.PutSeparators(" \"\t");
  95. arg_lex.SetCaseSensitive(FALSE);
  96. if (!arg_lex.PrepareToParse()) {
  97. DisplayMessage(MSG_CHKNTFS_INVALID_FORMAT);
  98. return FALSE;
  99. }
  100. if (!arg_lex.DoParsing(&argument_array)) {
  101. if (flag_invalid.QueryFlag()) {
  102. DisplayMessage(MSG_CHKNTFS_INVALID_SWITCH, NORMAL_MESSAGE,
  103. "%W", flag_invalid.GetLexeme());
  104. } else {
  105. DisplayMessage(MSG_CHKNTFS_INVALID_FORMAT);
  106. }
  107. return FALSE;
  108. } else if (_drive_arguments.WildCardExpansionFailed()) {
  109. DisplayMessage(MSG_CHKNTFS_NO_WILDCARDS);
  110. return FALSE;
  111. }
  112. if (flag_invalid.QueryFlag()) {
  113. DisplayMessage(MSG_CHKNTFS_INVALID_SWITCH);
  114. return FALSE;
  115. }
  116. if (flag_display_help.QueryFlag()) {
  117. DisplayMessage(MSG_CHKNTFS_USAGE);
  118. return FALSE;
  119. }
  120. _restore_default = flag_restore_default.QueryFlag();
  121. _exclude = flag_exclude.QueryFlag();
  122. _schedule_check = flag_schedule_check.QueryFlag();
  123. _display_count_down_time = flag_count_down_time.IsValueSet();
  124. _set_count_down_time = arg_count_down_time.IsValueSet() ?
  125. arg_count_down_time.QueryLong() : -1;
  126. _count_down_time = _display_count_down_time || arg_count_down_time.IsValueSet();
  127. if (_restore_default + _exclude + _schedule_check + _count_down_time > 1) {
  128. DisplayMessage(MSG_CHKNTFS_ARGS_CONFLICT);
  129. return FALSE;
  130. }
  131. if (0 == _drive_arguments.QueryPathCount() &&
  132. !(_restore_default || _count_down_time)) {
  133. DisplayMessage(MSG_CHKNTFS_REQUIRES_DRIVE);
  134. return FALSE;
  135. }
  136. if ((_restore_default || _count_down_time)
  137. && _drive_arguments.QueryPathCount() > 0) {
  138. DisplayMessage(MSG_CHKNTFS_INVALID_FORMAT);
  139. return FALSE;
  140. }
  141. if (_count_down_time && arg_count_down_time.IsValueSet() &&
  142. (arg_count_down_time.QueryLong() > MAX_AUTOCHK_TIMEOUT_VALUE ||
  143. arg_count_down_time.QueryLong() < 0)) {
  144. DisplayMessage(MSG_CHKNTFS_INVALID_AUTOCHK_COUNT_DOWN_TIME,
  145. NORMAL_MESSAGE, "%d%d",
  146. MAX_AUTOCHK_TIMEOUT_VALUE, AUTOCHK_TIMEOUT);
  147. return FALSE;
  148. }
  149. return TRUE;
  150. }
  151. BOOLEAN
  152. CHKNTFS::CheckNtfs(
  153. )
  154. /*++
  155. Routine Description:
  156. Look at the arguments specified by the user and do what
  157. he wants.
  158. Arguments:
  159. None.
  160. Return Value:
  161. BOOLEAN -- success or failure.
  162. --*/
  163. {
  164. DSTRING volume_name;
  165. PATH fullpath;
  166. DSTRING drive_path_string;
  167. PATH_ANALYZE_CODE rst;
  168. MOUNT_POINT_MAP drive_array_map;
  169. PMOUNT_POINT_TUPLE mptuple;
  170. PARRAY drive_array;
  171. PARRAY_ITERATOR iterator;
  172. PPATH current_drive;
  173. PCWSTRING display_drive_string;
  174. PCWSTRING dos_volume_name;
  175. PCWSTRING dos_drive_name;
  176. DSTRING nt_drive_name;
  177. DSTRING ntfs_str;
  178. DSTRING fat_str;
  179. DSTRING fat32_str;
  180. DSTRING fs_name;
  181. DSTRING fs_name_and_version;
  182. BOOLEAN is_dirty = 0;
  183. DSTRING cmd_line;
  184. ULONG old_error_mode;
  185. DRIVE_TYPE drive_type;
  186. DP_DRIVE dpdrive;
  187. if (_restore_default) {
  188. // Remove all autochk commands and insert the
  189. // default entry into the registry.
  190. if (!cmd_line.Initialize("autocheck autochk") ||
  191. !AUTOREG::DeleteEntry(&cmd_line, TRUE)) {
  192. return FALSE;
  193. }
  194. if (!cmd_line.Initialize("autocheck autochk *") ||
  195. !AUTOREG::AddEntry(&cmd_line)) {
  196. return FALSE;
  197. }
  198. return TRUE;
  199. }
  200. if (_count_down_time) {
  201. ULONG timeout;
  202. if (_display_count_down_time) {
  203. if (!VOL_LIODPDRV::QueryAutochkTimeOut(&timeout)) {
  204. timeout = AUTOCHK_TIMEOUT;
  205. }
  206. DisplayMessage(MSG_CHKNTFS_AUTOCHK_COUNT_DOWN_TIME,
  207. NORMAL_MESSAGE, "%d", timeout);
  208. return TRUE;
  209. } else if (_set_count_down_time >= 0) {
  210. if (!VOL_LIODPDRV::SetAutochkTimeOut(_set_count_down_time)) {
  211. DisplayMessage(MSG_CHKNTFS_AUTOCHK_SET_COUNT_DOWN_TIME_FAILED,
  212. NORMAL_MESSAGE, "%d", _set_count_down_time);
  213. }
  214. return TRUE;
  215. }
  216. DebugAssert(FALSE);
  217. return FALSE;
  218. }
  219. if (!ntfs_str.Initialize("NTFS") ||
  220. !fat_str.Initialize("FAT") ||
  221. !fat32_str.Initialize("FAT32"))
  222. return FALSE;
  223. if (!drive_array_map.Initialize()) {
  224. DebugPrint("Unable to initialize drive_array_map.\n");
  225. return FALSE;
  226. }
  227. drive_array = _drive_arguments.GetPathArray();
  228. iterator = (PARRAY_ITERATOR)drive_array->QueryIterator();
  229. iterator->Reset();
  230. //
  231. // Run through the arguments here and translate each of
  232. // them into their ultimate volume names, and mount points.
  233. // Build an array of tuple consists of the mount point,
  234. // volume name, and original drive specification tuple.
  235. while (NULL != (current_drive = (PPATH)iterator->GetNext())) {
  236. display_drive_string = current_drive->GetPathString();
  237. rst = current_drive->AnalyzePath(&volume_name,
  238. &fullpath,
  239. &drive_path_string);
  240. switch (rst) {
  241. case PATH_OK:
  242. case PATH_COULD_BE_FLOPPY:
  243. if (drive_path_string.QueryChCount() != 0) {
  244. DisplayMessage(MSG_CHKNTFS_BAD_ARG, NORMAL_MESSAGE,
  245. "%W", display_drive_string);
  246. DELETE(iterator);
  247. return FALSE;
  248. }
  249. mptuple = (PMOUNT_POINT_TUPLE)NEW MOUNT_POINT_TUPLE;
  250. if (mptuple == NULL ||
  251. !mptuple->_DeviceName.Initialize(display_drive_string) ||
  252. !mptuple->_VolumeName.Initialize(&volume_name) ||
  253. !mptuple->_DriveName.Initialize(fullpath.GetPathString())) {
  254. DebugPrint("Out of memory.\n");
  255. DELETE(mptuple);
  256. DELETE(iterator);
  257. return FALSE;
  258. }
  259. if (!drive_array_map.Put(mptuple)) {
  260. DebugPrint("Unable to put away an object.\n");
  261. DELETE(mptuple);
  262. DELETE(iterator);
  263. return FALSE;
  264. }
  265. break;
  266. case PATH_OUT_OF_MEMORY:
  267. DebugPrint("Out of memory.\n");
  268. DELETE(iterator);
  269. return FALSE;
  270. case PATH_NO_MOUNT_POINT_FOR_VOLUME_NAME_PATH:
  271. DisplayMessage(MSG_CHKNTFS_NO_MOUNT_POINT_FOR_GUID_VOLNAME_PATH,
  272. NORMAL_MESSAGE, "%W", display_drive_string);
  273. DELETE(iterator);
  274. return FALSE;
  275. default:
  276. DisplayMessage(MSG_CHKNTFS_BAD_ARG, NORMAL_MESSAGE,
  277. "%W", display_drive_string);
  278. DELETE(iterator);
  279. return FALSE;
  280. }
  281. }
  282. DELETE(iterator);
  283. iterator = (PARRAY_ITERATOR)drive_array_map.QueryIterator();
  284. iterator->Reset();
  285. //
  286. // Run through the tuples here and make sure they're all
  287. // valid drive names.
  288. //
  289. while (NULL != (mptuple = (PMOUNT_POINT_TUPLE)iterator->GetNext())) {
  290. display_drive_string = &(mptuple->_DeviceName);
  291. dos_volume_name = &(mptuple->_VolumeName);
  292. old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
  293. drive_type = SYSTEM::QueryDriveType(dos_volume_name);
  294. SetErrorMode(old_error_mode);
  295. switch (drive_type) {
  296. case UnknownDrive:
  297. DisplayMessage(MSG_CHKNTFS_NONEXISTENT_DRIVE, NORMAL_MESSAGE,
  298. "%W", display_drive_string);
  299. DELETE(iterator);
  300. return FALSE;
  301. case RemoteDrive:
  302. DisplayMessage(MSG_CHKNTFS_NO_NETWORK, NORMAL_MESSAGE,
  303. "%W", display_drive_string);
  304. DELETE(iterator);
  305. return FALSE;
  306. case RamDiskDrive:
  307. DisplayMessage(MSG_CHKNTFS_NO_RAMDISK, NORMAL_MESSAGE,
  308. "%W", display_drive_string);
  309. DELETE(iterator);
  310. return FALSE;
  311. case RemovableDrive:
  312. if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_volume_name,
  313. &nt_drive_name)) {
  314. DELETE(iterator);
  315. return FALSE;
  316. }
  317. old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
  318. if (!dpdrive.Initialize(&nt_drive_name)) {
  319. SetErrorMode(old_error_mode);
  320. DisplayMessage(MSG_CHKNTFS_CANNOT_CHECK, NORMAL_MESSAGE,
  321. "%W", display_drive_string);
  322. DELETE(iterator);
  323. return FALSE;
  324. }
  325. SetErrorMode(old_error_mode);
  326. if (dpdrive.IsFloppy()) {
  327. DisplayMessage(MSG_CHKNTFS_FLOPPY_DRIVE, NORMAL_MESSAGE, "%W",
  328. display_drive_string);
  329. DELETE(iterator);
  330. return FALSE;
  331. }
  332. default:
  333. break;
  334. }
  335. }
  336. iterator->Reset();
  337. if (_exclude) {
  338. DSTRING cmd_line2;
  339. DSTRING option;
  340. // Remove all previous autochk commands, if any.
  341. if (!cmd_line.Initialize("autocheck autochk *") ||
  342. !AUTOREG::DeleteEntry(&cmd_line, TRUE)) {
  343. DELETE(iterator);
  344. return FALSE;
  345. }
  346. if (!cmd_line.Initialize("autocheck autochk /k:") ||
  347. !AUTOREG::DeleteEntry(&cmd_line, TRUE)) {
  348. DELETE(iterator);
  349. return FALSE;
  350. }
  351. if (!cmd_line.Initialize("autocheck autochk") ||
  352. !option.Initialize(" /k:") ||
  353. !cmd_line2.Initialize("autocheck autochk")) {
  354. DELETE(iterator);
  355. return FALSE;
  356. }
  357. //
  358. // Collect a list of drives to be excluded and add them to the
  359. // command line
  360. //
  361. while (NULL != (mptuple = (PMOUNT_POINT_TUPLE)iterator->GetNext())) {
  362. display_drive_string = &(mptuple->_DeviceName);
  363. dos_volume_name = &(mptuple->_VolumeName);
  364. dos_drive_name = &(mptuple->_DriveName);
  365. // Warn the user if the filesystem is not NTFS.
  366. if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_volume_name,
  367. &nt_drive_name)) {
  368. DELETE(iterator);
  369. return FALSE;
  370. }
  371. if (IFS_SYSTEM::QueryFileSystemName(&nt_drive_name, &fs_name,
  372. NULL, &fs_name_and_version)) {
  373. DisplayMessage(MSG_FILE_SYSTEM_TYPE, NORMAL_MESSAGE,
  374. "%W", &fs_name);
  375. }
  376. if (!AUTOREG::DeleteEntry(&cmd_line2, &nt_drive_name)) {
  377. DELETE(iterator);
  378. return FALSE;
  379. }
  380. if (dos_drive_name->QueryChCount() == 2) {
  381. if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_drive_name,
  382. &nt_drive_name)) {
  383. DELETE(iterator);
  384. return FALSE;
  385. }
  386. if (!AUTOREG::DeleteEntry(&cmd_line2, &nt_drive_name)) {
  387. DELETE(iterator);
  388. return FALSE;
  389. }
  390. if (!cmd_line.Strcat(&option) ||
  391. !cmd_line.Strcat(dos_drive_name->QueryString(0, 1))) {
  392. DELETE(iterator);
  393. return FALSE;
  394. }
  395. } else {
  396. if (!cmd_line.Strcat(&option) ||
  397. !cmd_line.Strcat(dos_volume_name)) {
  398. DELETE(iterator);
  399. return FALSE;
  400. }
  401. }
  402. }
  403. DELETE(iterator);
  404. DSTRING star;
  405. if (!star.Initialize(" *") ||
  406. !cmd_line.Strcat(&star)) {
  407. return FALSE;
  408. }
  409. // Add the new command line.
  410. return AUTOREG::AddEntry(&cmd_line);
  411. }
  412. //
  413. // This loop handles the "schedule check" and default actions.
  414. //
  415. while (NULL != (mptuple = (PMOUNT_POINT_TUPLE)iterator->GetNext())) {
  416. display_drive_string = &(mptuple->_DeviceName);
  417. dos_volume_name = &(mptuple->_VolumeName);
  418. dos_drive_name = &(mptuple->_DriveName);
  419. if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_volume_name,
  420. &nt_drive_name)) {
  421. DELETE(iterator);
  422. return FALSE;
  423. }
  424. //
  425. // Schedule check: Put a line in the registry like
  426. // "autocheck autochk \??\X:" for each command-line argument.
  427. //
  428. if (_schedule_check) {
  429. if (!IFS_SYSTEM::QueryFileSystemName(&nt_drive_name, &fs_name, NULL)) {
  430. DisplayMessage(MSG_CHKNTFS_CANNOT_CHECK, NORMAL_MESSAGE,
  431. "%W", display_drive_string);
  432. DELETE(iterator);
  433. return FALSE;
  434. }
  435. if (fs_name == ntfs_str ||
  436. fs_name == fat_str ||
  437. fs_name == fat32_str) {
  438. if (!cmd_line.Initialize("autocheck autochk") ||
  439. !AUTOREG::DeleteEntry(&cmd_line, &nt_drive_name)) {
  440. DELETE(iterator);
  441. return FALSE;
  442. }
  443. if (dos_drive_name->QueryChCount() == 2) {
  444. if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_drive_name,
  445. &nt_drive_name)) {
  446. DELETE(iterator);
  447. return FALSE;
  448. }
  449. if (!cmd_line.Initialize("autocheck autochk") ||
  450. !AUTOREG::DeleteEntry(&cmd_line, &nt_drive_name)) {
  451. DELETE(iterator);
  452. return FALSE;
  453. }
  454. }
  455. if (!cmd_line.Initialize("autocheck autochk /m ") ||
  456. !cmd_line.Strcat(&nt_drive_name) ||
  457. !AUTOREG::PushEntry(&cmd_line)) {
  458. DELETE(iterator);
  459. return FALSE;
  460. }
  461. } else {
  462. DisplayMessage(MSG_CHKNTFS_SKIP_DRIVE_RAW, NORMAL_MESSAGE,
  463. "%W", display_drive_string);
  464. }
  465. continue;
  466. }
  467. //
  468. // Default: check to see if the volume is dirty.
  469. //
  470. if (IFS_SYSTEM::QueryFileSystemName(&nt_drive_name, &fs_name,
  471. NULL, &fs_name_and_version)) {
  472. DisplayMessage(MSG_FILE_SYSTEM_TYPE, NORMAL_MESSAGE,
  473. "%W", &fs_name);
  474. }
  475. if (!IFS_SYSTEM::IsVolumeDirty(&nt_drive_name, &is_dirty)) {
  476. DisplayMessage(MSG_CHKNTFS_CANNOT_CHECK, NORMAL_MESSAGE,
  477. "%W", display_drive_string);
  478. DELETE(iterator);
  479. return FALSE;
  480. }
  481. if (is_dirty) {
  482. DisplayMessage(MSG_CHKNTFS_DIRTY, NORMAL_MESSAGE,
  483. "%W", display_drive_string);
  484. ExitStatus = 1;
  485. } else {
  486. DSTRING cmd_line2;
  487. if (!cmd_line.Initialize("autocheck autochk") ||
  488. !cmd_line2.Initialize(" ") ||
  489. !cmd_line2.Strcat(&nt_drive_name)) {
  490. DELETE(iterator);
  491. return FALSE;
  492. }
  493. if (AUTOREG::IsFrontEndPresent(&cmd_line, &nt_drive_name)) {
  494. DisplayMessage(MSG_CHKNTFS_CHKDSK_WILL_RUN,
  495. NORMAL_MESSAGE,
  496. "%W", display_drive_string);
  497. continue;
  498. } else if (dos_drive_name->QueryChCount() == 2) {
  499. if (!IFS_SYSTEM::DosDriveNameToNtDriveName(dos_drive_name,
  500. &nt_drive_name)) {
  501. DELETE(iterator);
  502. return FALSE;
  503. }
  504. if (AUTOREG::IsFrontEndPresent(&cmd_line, &nt_drive_name)) {
  505. DisplayMessage(MSG_CHKNTFS_CHKDSK_WILL_RUN,
  506. NORMAL_MESSAGE,
  507. "%W", display_drive_string);
  508. continue;
  509. }
  510. }
  511. DisplayMessage(MSG_CHKNTFS_CLEAN, NORMAL_MESSAGE,
  512. "%W", display_drive_string);
  513. }
  514. }
  515. DELETE(iterator);
  516. return TRUE;
  517. }
  518. VOID __cdecl
  519. main()
  520. {
  521. DEFINE_CLASS_DESCRIPTOR(CHKNTFS);
  522. {
  523. CHKNTFS ChkNtfs;
  524. if (!ChkNtfs.Initialize() ||
  525. !ChkNtfs.CheckNtfs()) {
  526. exit(2);
  527. }
  528. exit(ChkNtfs.ExitStatus);
  529. }
  530. }