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.

622 lines
19 KiB

  1. /*++
  2. Copyright (c) 1991-2000 Microsoft Corporation
  3. Module Name:
  4. chkdsk.cxx
  5. Abstract:
  6. Chkdsk is a program that checks your disk for corruption and/or bad sectors.
  7. Author:
  8. Bill McJohn (billmc) 12-April-91
  9. Revision History:
  10. --*/
  11. #define _NTAPI_ULIB_
  12. #include "ulib.hxx"
  13. #include "arg.hxx"
  14. #include "chkmsg.hxx"
  15. #include "rtmsg.h"
  16. #include "wstring.hxx"
  17. #include "path.hxx"
  18. #include "system.hxx"
  19. #include "ifssys.hxx"
  20. #include "substrng.hxx"
  21. #include "ulibcl.hxx"
  22. #include "ifsentry.hxx"
  23. #include "keyboard.hxx"
  24. #include "supera.hxx" // for CHKDSK_EXIT_*
  25. int __cdecl
  26. main(
  27. )
  28. /*++
  29. Routine Description:
  30. Entry point for chkdsk.exe. This function parses the arguments,
  31. determines the appropriate file system (by querying the volume),
  32. and invokes the appropriate version of chkdsk.
  33. The arguments accepted by Chkdsk are:
  34. /f Fix errors on drive
  35. /v Verbose operation
  36. drive: drive to check
  37. file-name files to check for contiguity
  38. (Note that HPFS ignores file-name parameters).
  39. /x Force the volume to dismount first if necessary (implied /f)
  40. /i include index entries checking during index verification
  41. /c include cycles checking during index verification
  42. /r locate bad sectors and recover readable information
  43. /l[:size] change or display log file size
  44. --*/
  45. {
  46. DSTRING CurrentDirectory;
  47. DSTRING FsName;
  48. DSTRING FsNameAndVersion;
  49. DSTRING LibraryName;
  50. DSTRING DosDriveName;
  51. DSTRING CurrentDrive;
  52. DSTRING NtDriveName;
  53. PWSTRING p;
  54. HANDLE FsUtilityHandle;
  55. DSTRING ChkdskString;
  56. DSTRING Colon;
  57. CHKDSKEX_FN ChkdskEx = NULL;
  58. PWSTRING pwstring;
  59. BOOLEAN fix;
  60. BOOLEAN resize_logfile;
  61. ULONG logfile_size;
  62. FSTRING acolon, bcolon;
  63. ULONG exit_status;
  64. ARGUMENT_LEXEMIZER Lexemizer;
  65. ARRAY EmptyArray;
  66. ARRAY ArgumentArray;
  67. FLAG_ARGUMENT ArgumentHelp;
  68. FLAG_ARGUMENT ArgumentForce;
  69. FLAG_ARGUMENT ArgumentFix;
  70. FLAG_ARGUMENT ArgumentVerbose;
  71. FLAG_ARGUMENT ArgumentRecover;
  72. LONG_ARGUMENT ArgumentAlgorithm;
  73. FLAG_ARGUMENT ArgumentSkipIndexScan;
  74. FLAG_ARGUMENT ArgumentSkipCycleScan;
  75. FLAG_ARGUMENT ArgumentResize;
  76. LONG_ARGUMENT ArgumentResizeLong;
  77. STRING_ARGUMENT ArgumentProgramName;
  78. PATH_ARGUMENT ArgumentPath;
  79. CHKDSK_MESSAGE Message;
  80. NTSTATUS Status;
  81. DWORD oldErrorMode;
  82. PATH_ANALYZE_CODE rst;
  83. PPATH ppath;
  84. PATH fullpath;
  85. PATH drivepath;
  86. DSTRING drivename;
  87. DSTRING drive_path_string;
  88. BOOLEAN is_drivepath_invalid = TRUE;
  89. DSTRING ntfs_name;
  90. USHORT algorithm;
  91. CHKDSKEX_FN_PARAM param;
  92. if( !Message.Initialize( Get_Standard_Output_Stream(),
  93. Get_Standard_Input_Stream() ) ) {
  94. return CHKDSK_EXIT_COULD_NOT_CHK;
  95. }
  96. #if defined(PRE_RELEASE_NOTICE)
  97. Message.Set(MSG_CHK_PRE_RELEASE_NOTICE);
  98. Message.Display();
  99. #endif
  100. // Initialize the colon string in case we need it later:
  101. if( !Colon.Initialize( ":" ) ||
  102. !ntfs_name.Initialize( "NTFS" )) {
  103. Message.Set( MSG_CHK_NO_MEMORY );
  104. Message.Display( "" );
  105. return CHKDSK_EXIT_COULD_NOT_CHK;
  106. }
  107. // Parse the arguments. First, initialize all the
  108. // parsing machinery. Then put the potential arguments
  109. // into the argument array,
  110. if( !ArgumentArray.Initialize( 5, 1 ) ||
  111. !EmptyArray.Initialize( 5, 1 ) ||
  112. !Lexemizer.Initialize( &EmptyArray ) ||
  113. !ArgumentHelp.Initialize( "/?" ) ||
  114. !ArgumentForce.Initialize( "/X" ) ||
  115. !ArgumentFix.Initialize( "/F" ) ||
  116. !ArgumentVerbose.Initialize( "/V" ) ||
  117. !ArgumentRecover.Initialize( "/R" ) ||
  118. !ArgumentAlgorithm.Initialize( "/I:*" ) ||
  119. !ArgumentSkipIndexScan.Initialize( "/I" ) ||
  120. !ArgumentSkipCycleScan.Initialize( "/C" ) ||
  121. !ArgumentResize.Initialize( "/L" ) ||
  122. !ArgumentResizeLong.Initialize( "/L:*" ) ||
  123. !ArgumentProgramName.Initialize( "*" ) ||
  124. !ArgumentPath.Initialize( "*", FALSE ) ) {
  125. Message.Set( MSG_CHK_NO_MEMORY );
  126. Message.Display( "" );
  127. return CHKDSK_EXIT_COULD_NOT_CHK;
  128. }
  129. // CHKDSK is not case sensitive.
  130. Lexemizer.SetCaseSensitive( FALSE );
  131. if( !ArgumentArray.Put( &ArgumentProgramName ) ||
  132. !ArgumentArray.Put( &ArgumentHelp ) ||
  133. !ArgumentArray.Put( &ArgumentForce ) ||
  134. !ArgumentArray.Put( &ArgumentFix ) ||
  135. !ArgumentArray.Put( &ArgumentVerbose ) ||
  136. !ArgumentArray.Put( &ArgumentRecover ) ||
  137. !ArgumentArray.Put( &ArgumentAlgorithm ) ||
  138. !ArgumentArray.Put( &ArgumentSkipIndexScan )||
  139. !ArgumentArray.Put( &ArgumentSkipCycleScan )||
  140. !ArgumentArray.Put( &ArgumentResize ) ||
  141. !ArgumentArray.Put( &ArgumentResizeLong ) ||
  142. !ArgumentArray.Put( &ArgumentPath ) ) {
  143. Message.Set( MSG_CHK_NO_MEMORY );
  144. Message.Display( "" );
  145. return CHKDSK_EXIT_COULD_NOT_CHK;
  146. }
  147. // Parse. Note that PrepareToParse will, by default, pick
  148. // up the command line.
  149. if( !Lexemizer.PrepareToParse() ) {
  150. Message.Set( MSG_CHK_NO_MEMORY );
  151. Message.Display( "" );
  152. return CHKDSK_EXIT_COULD_NOT_CHK;
  153. }
  154. // If the parsing failed, display a helpful command line summary.
  155. if( !Lexemizer.DoParsing( &ArgumentArray ) ) {
  156. Message.Set(MSG_INVALID_PARAMETER);
  157. Message.Display("%W", pwstring = Lexemizer.QueryInvalidArgument());
  158. DELETE(pwstring);
  159. return CHKDSK_EXIT_COULD_NOT_CHK;
  160. }
  161. // If the user requested help, give it.
  162. if( ArgumentHelp.QueryFlag() ) {
  163. Message.Set( MSG_CHK_USAGE_HEADER );
  164. Message.Display( "" );
  165. Message.Set( MSG_BLANK_LINE );
  166. Message.Display( "" );
  167. Message.Set( MSG_CHK_COMMAND_LINE );
  168. Message.Display( "" );
  169. Message.Set( MSG_BLANK_LINE );
  170. Message.Display( "" );
  171. Message.Set( MSG_CHK_DRIVE );
  172. Message.Display( "" );
  173. Message.Set( MSG_CHK_USG_FILENAME );
  174. Message.Display( "" );
  175. Message.Set( MSG_CHK_F_SWITCH );
  176. Message.Display( "" );
  177. Message.Set( MSG_CHK_V_SWITCH );
  178. Message.Display( "" );
  179. return CHKDSK_EXIT_COULD_NOT_CHK;
  180. }
  181. if (!ArgumentPath.IsValueSet()) {
  182. if (!SYSTEM::QueryCurrentDosDriveName(&DosDriveName) ||
  183. !drivepath.Initialize(&DosDriveName)) {
  184. return CHKDSK_EXIT_COULD_NOT_CHK;
  185. }
  186. ppath = &drivepath;
  187. } else {
  188. ppath = ArgumentPath.GetPath();
  189. #if defined(RUN_ON_NT4)
  190. if (!DosDriveName.Initialize(ppath->GetPathString()))
  191. return CHKDSK_EXIT_COULD_NOT_CHK;
  192. #endif
  193. }
  194. #if !defined(RUN_ON_NT4)
  195. rst = ppath->AnalyzePath(&DosDriveName,
  196. &fullpath,
  197. &drive_path_string);
  198. switch (rst) {
  199. case PATH_OK:
  200. case PATH_COULD_BE_FLOPPY:
  201. is_drivepath_invalid = fullpath.IsDrive() ||
  202. (fullpath.GetPathString()->QueryChCount() == 0);
  203. if (ppath->IsGuidVolName()) {
  204. if (!drivename.Initialize(&DosDriveName))
  205. return CHKDSK_EXIT_COULD_NOT_CHK;
  206. } else {
  207. if (!drivename.Initialize(fullpath.GetPathString()))
  208. return CHKDSK_EXIT_COULD_NOT_CHK;
  209. }
  210. if (fullpath.GetPathString()->QueryChCount() == 2 &&
  211. fullpath.GetPathString()->QueryChAt(1) == (WCHAR)':') {
  212. // if there is a drive letter for this drive, use it
  213. // instead of the guid volume name
  214. if (!DosDriveName.Initialize(fullpath.GetPathString())) {
  215. return CHKDSK_EXIT_COULD_NOT_CHK;
  216. }
  217. }
  218. if (!fullpath.AppendString(&drive_path_string) ||
  219. !drivepath.Initialize(&drive_path_string))
  220. return CHKDSK_EXIT_COULD_NOT_CHK;
  221. break;
  222. case PATH_OUT_OF_MEMORY:
  223. DebugPrint("Out of memory.\n");
  224. return CHKDSK_EXIT_COULD_NOT_CHK;
  225. case PATH_NO_MOUNT_POINT_FOR_VOLUME_NAME_PATH:
  226. Message.Set(MSG_CHK_NO_MOUNT_POINT_FOR_GUID_VOLNAME_PATH);
  227. Message.Display();
  228. return CHKDSK_EXIT_COULD_NOT_CHK;
  229. default:
  230. Message.Set(MSG_CHK_BAD_DRIVE_PATH_FILENAME);
  231. Message.Display();
  232. return CHKDSK_EXIT_COULD_NOT_CHK;
  233. }
  234. #endif
  235. if (!DosDriveName.Strupr()) {
  236. return CHKDSK_EXIT_COULD_NOT_CHK;
  237. }
  238. // disable popups while we determine the drive type
  239. oldErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS );
  240. // Make sure that drive is of a correct type.
  241. switch (SYSTEM::QueryDriveType(&DosDriveName)) {
  242. case RemoteDrive:
  243. SetErrorMode( oldErrorMode );
  244. Message.Set(MSG_CHK_CANT_NETWORK);
  245. Message.Display();
  246. return CHKDSK_EXIT_COULD_NOT_CHK;
  247. #if 0
  248. case CdRomDrive:
  249. SetErrorMode( oldErrorMode );
  250. Message.Set(MSG_CHK_CANT_CDROM);
  251. Message.Display();
  252. return CHKDSK_EXIT_COULD_NOT_CHK;
  253. #endif
  254. default:
  255. break;
  256. }
  257. SetErrorMode( oldErrorMode );
  258. if (!SYSTEM::QueryCurrentDosDriveName(&CurrentDrive)) {
  259. return CHKDSK_EXIT_COULD_NOT_CHK;
  260. }
  261. // /R ==> /F
  262. // /X ==> /F
  263. fix = ArgumentFix.QueryFlag() ||
  264. ArgumentForce.QueryFlag() ||
  265. ArgumentRecover.QueryFlag();
  266. // From here on we want to deal with an NT drive name:
  267. if (!IFS_SYSTEM::DosDriveNameToNtDriveName(&DosDriveName, &NtDriveName)) {
  268. return CHKDSK_EXIT_COULD_NOT_CHK;
  269. }
  270. // disable popups while we determine the file system name and version
  271. oldErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS );
  272. // Determine the type of the file system.
  273. // Ask the volume what file system it has. The
  274. // IFS utilities for file system xxxx are in Uxxxx.DLL.
  275. //
  276. if (!IFS_SYSTEM::QueryFileSystemName(&NtDriveName,
  277. &FsName,
  278. &Status,
  279. &FsNameAndVersion )) {
  280. SetErrorMode( oldErrorMode );
  281. if( Status == STATUS_ACCESS_DENIED ) {
  282. Message.Set( MSG_DASD_ACCESS_DENIED );
  283. Message.Display( "" );
  284. } else if( Status != STATUS_SUCCESS ) {
  285. Message.Set( MSG_CANT_DASD );
  286. Message.Display( "" );
  287. } else {
  288. Message.Set( MSG_FS_NOT_DETERMINED );
  289. Message.Display( "%W", &drivename );
  290. }
  291. return CHKDSK_EXIT_COULD_NOT_CHK;
  292. }
  293. // re-enable hardware popups
  294. SetErrorMode( oldErrorMode );
  295. if (FsName == ntfs_name && drive_path_string.QueryChCount()) {
  296. Message.Set(MSG_CHK_BAD_DRIVE_PATH_FILENAME);
  297. Message.Display();
  298. return CHKDSK_EXIT_COULD_NOT_CHK;
  299. }
  300. Message.SetLoggingEnabled();
  301. Message.Set( MSG_CHK_RUNNING );
  302. Message.Log( "%W", &drivename );
  303. Message.Set( MSG_FILE_SYSTEM_TYPE );
  304. Message.Display( "%W", &FsName );
  305. if ( !FsName.Strupr() ) {
  306. return CHKDSK_EXIT_COULD_NOT_CHK;
  307. }
  308. DSTRING fat32_name;
  309. if ( !fat32_name.Initialize("FAT32") ) {
  310. return CHKDSK_EXIT_COULD_NOT_CHK;
  311. }
  312. if ( FsName == fat32_name ) {
  313. FsName.Initialize("FAT");
  314. }
  315. if ( !LibraryName.Initialize( "U" ) ||
  316. !LibraryName.Strcat( &FsName ) ||
  317. !ChkdskString.Initialize( "ChkdskEx" ) ) {
  318. Message.Set( MSG_CHK_NO_MEMORY );
  319. Message.Display( "" );
  320. return CHKDSK_EXIT_COULD_NOT_CHK;
  321. }
  322. if (fix && (CurrentDrive == DosDriveName)) {
  323. Message.Set(MSG_CANT_LOCK_CURRENT_DRIVE);
  324. Message.Display();
  325. if (IsNEC_98) {
  326. DP_DRIVE dpdrive;
  327. dpdrive.Initialize(&NtDriveName, &Message);
  328. if (dpdrive.IsFloppy()) {
  329. return CHKDSK_EXIT_COULD_NOT_CHK;
  330. }
  331. } else {
  332. acolon.Initialize((PWSTR) L"A:");
  333. bcolon.Initialize((PWSTR) L"B:");
  334. if (!DosDriveName.Stricmp(&acolon) ||
  335. !DosDriveName.Stricmp(&bcolon)) {
  336. return CHKDSK_EXIT_COULD_NOT_CHK;
  337. }
  338. }
  339. // Fall through so that the lock fails and then the
  340. // run autochk on reboot logic kicks in.
  341. //
  342. }
  343. if (ArgumentAlgorithm.IsValueSet() && ArgumentSkipIndexScan.QueryFlag()) {
  344. Message.Set(MSG_CHK_ALGORITHM_AND_SKIP_INDEX_SPECIFIED);
  345. Message.Display();
  346. return CHKDSK_EXIT_COULD_NOT_CHK;
  347. }
  348. if (ArgumentAlgorithm.IsValueSet()) {
  349. if (ArgumentAlgorithm.QueryLong() < 0 ||
  350. ArgumentAlgorithm.QueryLong() > CHKDSK_MAX_ALGORITHM_VALUE) {
  351. Message.Set(MSG_CHK_INCORRECT_ALGORITHM_VALUE);
  352. Message.Display();
  353. return CHKDSK_EXIT_COULD_NOT_CHK;
  354. } else
  355. algorithm = (USHORT)ArgumentAlgorithm.QueryLong();
  356. } else
  357. algorithm = 0;
  358. if (ArgumentSkipIndexScan.QueryFlag() || ArgumentAlgorithm.IsValueSet()) {
  359. if (0 != FsName.Stricmp( &ntfs_name )) {
  360. Message.Set(MSG_CHK_SKIP_INDEX_NOT_NTFS);
  361. Message.Display();
  362. return CHKDSK_EXIT_COULD_NOT_CHK;
  363. }
  364. }
  365. if (ArgumentSkipCycleScan.QueryFlag()) {
  366. if (0 != FsName.Stricmp( &ntfs_name )) {
  367. Message.Set(MSG_CHK_SKIP_CYCLE_NOT_NTFS);
  368. Message.Display();
  369. return CHKDSK_EXIT_COULD_NOT_CHK;
  370. }
  371. }
  372. // Does the user want to resize the logfile? This is only sensible
  373. // for NTFS. If she specified a size of zero, print an error message
  374. // because that's a poor choice and will confuse the untfs code,
  375. // which assumes that zero means resize to the default size.
  376. //
  377. resize_logfile = ArgumentResize.IsValueSet() || ArgumentResizeLong.IsValueSet();
  378. if (resize_logfile) {
  379. if (0 != FsName.Stricmp( &ntfs_name )) {
  380. Message.Set(MSG_CHK_LOGFILE_NOT_NTFS);
  381. Message.Display();
  382. return CHKDSK_EXIT_COULD_NOT_CHK;
  383. }
  384. if (ArgumentResizeLong.IsValueSet()) {
  385. if (ArgumentResizeLong.QueryLong() <= 0) {
  386. Message.Set(MSG_CHK_WONT_ZERO_LOGFILE);
  387. Message.Display();
  388. return CHKDSK_EXIT_COULD_NOT_CHK;
  389. }
  390. if (ArgumentResizeLong.QueryLong() > MAXULONG/1024) {
  391. Message.Set(MSG_CHK_NTFS_SPECIFIED_LOGFILE_SIZE_TOO_BIG);
  392. Message.Display();
  393. return CHKDSK_EXIT_COULD_NOT_CHK;
  394. }
  395. logfile_size = ArgumentResizeLong.QueryLong() * 1024;
  396. } else {
  397. logfile_size = 0;
  398. }
  399. }
  400. if ((ChkdskEx =
  401. (CHKDSKEX_FN)SYSTEM::QueryLibraryEntryPoint( &LibraryName,
  402. &ChkdskString,
  403. &FsUtilityHandle )) !=
  404. NULL ) {
  405. if (fix &&
  406. !KEYBOARD::EnableBreakHandling()) {
  407. return CHKDSK_EXIT_COULD_NOT_CHK;
  408. }
  409. //
  410. // setup parameter block v1.0 to be passed to ChkdskEx
  411. //
  412. param.Major = 1;
  413. param.Minor = 1;
  414. param.Flags = (ArgumentVerbose.QueryFlag() ? CHKDSK_VERBOSE : 0);
  415. param.Flags |= (ArgumentRecover.QueryFlag() ? CHKDSK_RECOVER : 0);
  416. param.Flags |= (ArgumentForce.QueryFlag() ? CHKDSK_FORCE : 0);
  417. param.Flags |= (resize_logfile ? CHKDSK_RESIZE_LOGFILE : 0);
  418. param.Flags |= (ArgumentSkipIndexScan.QueryFlag() ? CHKDSK_SKIP_INDEX_SCAN : 0);
  419. param.Flags |= (ArgumentSkipCycleScan.QueryFlag() ? CHKDSK_SKIP_CYCLE_SCAN : 0);
  420. param.Flags |= (ArgumentAlgorithm.IsValueSet() ? CHKDSK_ALGORITHM_SPECIFIED : 0);
  421. param.LogFileSize = logfile_size;
  422. param.PathToCheck = &fullpath;
  423. param.RootPath = (is_drivepath_invalid) ? NULL : &drivepath;
  424. param.Algorithm = algorithm;
  425. if (fix) {
  426. ChkdskEx( &NtDriveName,
  427. &Message,
  428. fix,
  429. &param,
  430. &exit_status );
  431. } else {
  432. //disable C4509 warning about nonstandard ext: SEH + destructor
  433. #pragma warning(disable:4509)
  434. __try {
  435. ChkdskEx( &NtDriveName,
  436. &Message,
  437. fix,
  438. &param,
  439. &exit_status );
  440. } __except (EXCEPTION_EXECUTE_HANDLER) {
  441. // If we get an access violation during read-only mode
  442. // CHKDSK then it's because the file system is partying
  443. // on the volume while we are.
  444. Message.Set(MSG_CHK_NTFS_ERRORS_FOUND);
  445. Message.Display();
  446. exit_status = CHKDSK_EXIT_ERRS_NOT_FIXED;
  447. }
  448. }
  449. if (CHKDSK_EXIT_ERRS_FIXED == exit_status && !fix) {
  450. exit_status = CHKDSK_EXIT_ERRS_NOT_FIXED;
  451. }
  452. SYSTEM::FreeLibraryHandle( FsUtilityHandle );
  453. if (fix &&
  454. !KEYBOARD::DisableBreakHandling()) {
  455. return 1;
  456. }
  457. } else {
  458. Message.Set( MSG_FS_NOT_SUPPORTED );
  459. Message.Display( "%s%W", "CHKDSK", &FsName );
  460. Message.Set( MSG_BLANK_LINE );
  461. Message.Display( "" );
  462. return CHKDSK_EXIT_COULD_NOT_CHK;
  463. }
  464. // Message.Set(MSG_CHK_NTFS_MESSAGE);
  465. // Message.Display("%s%d", "Exit Status ", exit_status);
  466. return exit_status;
  467. }