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.

374 lines
9.2 KiB

  1. //-----------------------------------------------------------------------
  2. //
  3. // File: chkdsk.cxx
  4. //
  5. // Contents: Sanity checking and recovery mechanism for multistream files
  6. //
  7. // Argument:
  8. //
  9. // History: 9-July-92 t-chrisy Created.
  10. //------------------------------------------------------------------------
  11. #include "chkdsk.hxx"
  12. // Global variables, declared as so for convenience.
  13. CMSFHeader *pheader;
  14. CFat *pFat;
  15. CFat *pMiniFat;
  16. CDirectory *pDir;
  17. CDIFat *pDIFat;
  18. BOOL fixdf;
  19. CFatVector *pfvFat, *pfvMiniFat;
  20. wchar_t pwcsDocfile[_MAX_PATH];
  21. DFLAGS df = DF_READWRITE | DF_DENYWRITE;
  22. extern SCODE DllMultiStreamFromCorruptedStream(CMStream MSTREAM_NEAR **ppms,
  23. ILockBytes **pplstStream,
  24. DWORD dwFlags);
  25. // Function Prototypes
  26. void BuildFatTables();
  27. void MarkFatTables();
  28. void TreeWalk(CDirEntry *pde, SID sid);
  29. BOOL GetOption(int argc, char *argv[]);
  30. void Usage(char *pszProgName);
  31. void DIFTable();
  32. void main(int argc, char *argv[])
  33. {
  34. CFileStream *pfilestr;
  35. CMStream MSTREAM_NEAR *pms;
  36. SCODE scRc;
  37. ILockBytes *pilb;
  38. // if fixdf returns yes, open docfile without a copy;
  39. // otherwise, open docfile with a copy and operate on the copy.
  40. fixdf = GetOption(argc,argv);
  41. pfilestr = new CFileStream;
  42. // creating ILockBytes implementation for the given file
  43. // Note: When a docfile is corrupted, the chkdsk utility
  44. // calls the original CFileStream::Init. If any objects
  45. // fail to instantiate, the approach is to call an
  46. // alternative Init routine, which can force the instantiation
  47. // of Directory and MiniFat objects.
  48. if (fixdf==TRUE) // -f specified, write allowed.
  49. {
  50. df &= ~0x03;
  51. df |= DF_WRITE;
  52. printf("Trying to open file...\n");
  53. scRc = pfilestr->Init(pwcsDocfile,RSF_OPEN,df);
  54. if (FAILED(scRc))
  55. {
  56. printf("Error creating ILockBytes.\n");
  57. exit(FAIL_CREATE_ILB);
  58. }
  59. }
  60. else // open a read-only copy of filestream
  61. {
  62. df &= ~0x300; // clear access bits
  63. df |= DF_DENYWRITE;
  64. printf("Trying to open file...\n");
  65. scRc = pfilestr->Init(pwcsDocfile,RSF_OPEN,df);
  66. if (FAILED(scRc))
  67. {
  68. printf("Error creating ILockBytes.\n");
  69. exit(FAIL_CREATE_ILB);
  70. }
  71. else printf("Successfully created ILockBytes.\n");
  72. }
  73. scRc = pfilestr->Validate();
  74. if (scRc == STG_E_INVALIDHANDLE)
  75. {
  76. printf("Filestream signature is not valid.\n");
  77. exit(INVALID_DOCFILE);
  78. }
  79. // CFileStream is essentially equivalent to ILockBytes.
  80. pilb = (ILockBytes *) pfilestr;
  81. scRc = DllMultiStreamFromStream(&pms,&pilb,0);
  82. if (FAILED(scRc))
  83. if (FAILED(scRc = DllMultiStreamFromCorruptedStream
  84. (&pms,&pilb,0)))
  85. {
  86. exit(FAIL_CREATE_MULTISTREAM);
  87. printf("Error creating a multistream.\n");
  88. }
  89. // When an multi-stream is instantiated, the following control structures
  90. // are automatically instantiated.
  91. pheader = pms->GetHeader();
  92. pDir = pms->GetDir();
  93. pFat = pms->GetFat();
  94. pMiniFat = pms->GetMiniFat();
  95. pDIFat = pms->GetDIFat();
  96. printf("\tBuilding fat tables...\n");
  97. BuildFatTables();
  98. printf("\tExamining the DIFat...\n");
  99. DIFTable();
  100. printf("\tExamining Fat and MiniFat chains...\n");
  101. MarkFatTables();
  102. printf("\tChecking completed.\n");
  103. delete(pfvFat);
  104. delete(pfvMiniFat);
  105. pfilestr->Release();
  106. printf("Memory blocks freed.\n");
  107. }
  108. void BuildFatTables()
  109. {
  110. // Build two tables: one for Fat sectors, the other for Minifat sectors.
  111. FSINDEX FatLen,MiniFatLen;
  112. FatLen = pheader->GetFatLength();
  113. MiniFatLen = pheader->GetMiniFatLength();
  114. pfvFat = new CFatVector(TABLE_SIZE);
  115. pfvFat->Init(FatLen);
  116. if (MiniFatLen == 0)
  117. printf("No MiniFat to be checked.\n");
  118. else
  119. {
  120. pfvMiniFat = new CFatVector(TABLE_SIZE);
  121. pfvMiniFat->Init(MiniFatLen);
  122. }
  123. }
  124. void MarkFatTables()
  125. {
  126. CDirEntry *pde;
  127. // Walk through all the fat chains and mark the new table with the
  128. // first SID number encountered.
  129. pDir->SidToEntry(SIDROOT,&pde);
  130. TreeWalk(pde,SIDROOT); // pde points to the root entry now
  131. }
  132. void TreeWalk(CDirEntry *pde, SID sid)
  133. {
  134. CDirEntry *pchild, *pnext;
  135. SID childsid, nextsid;
  136. SCODE scRc,scRcM;
  137. FSINDEX fitable,fioffset;
  138. SECT sectentry, sect;
  139. CFatSect *pfsec;
  140. CFatVector *pfv;
  141. CFat *pf;
  142. ULONG uldesize;
  143. pDir->GetStart(sid,&sect);
  144. uldesize = pde->GetSize();
  145. if (uldesize >= MINISTREAMSIZE) // storage is in FAT
  146. {
  147. pfv = pfvFat;
  148. pf = pFat;
  149. }
  150. else
  151. {
  152. pfv = pfvMiniFat;
  153. pf = pMiniFat;
  154. }
  155. // Check if LUID exceeds MaxLUID. If so, report the error.
  156. if (pde->GetLuid() > pheader->GetLuid())
  157. printf("LUID for dir entry #%lu exceeds MAXLuid.\n",sid);
  158. while (sect < MAXREGSECT)
  159. {
  160. if (sid == SIDROOT)
  161. break; // nothing should be in root stream
  162. // Use fitable and fioffset to index into the fat (or minifat)
  163. // table and mark the field with visited.
  164. // at the same time, check for loops or crosslinks.
  165. //Note: 3 cases
  166. fitable = sect / (TABLE_SIZE);
  167. fioffset = sect % (TABLE_SIZE);
  168. pfv->GetTable(fitable,&pfsec); // pfsec = ptr to CFatSect
  169. sectentry = pfsec->GetSect(fioffset);
  170. // printf("\tsect = %lu \t \t sectentry = %lu \t stream_size = %lu\n",
  171. // sect,sectentry, uldesize);
  172. // Mark the FatTables as well as fixing the multistream.
  173. // Right now, the routine only marks the FatTables.
  174. //Note: 3 cases...but the last two cases may not
  175. // be handled the same.
  176. if (sectentry > MAXREGSECT)
  177. pfsec->SetSect(fioffset,sid);
  178. else if (sectentry == sid)
  179. {
  180. // discontinue the current stream chain by marking
  181. // current SECT as ENDOFCHAIN.
  182. pf->SetNext(sect,ENDOFCHAIN);
  183. pfsec->SetSect(fioffset,ENDOFCHAIN);
  184. printf("Loop detected at fat SECT %ul\n",sectentry);
  185. }
  186. else
  187. {
  188. pf->SetNext(sect,ENDOFCHAIN);
  189. pfsec->SetSect(fioffset,ENDOFCHAIN);
  190. printf("Crosslink detected at Fat SECT %lu with stream #%lu\n",
  191. sect,sid);
  192. }
  193. // get the next sector to be examined
  194. // !!!!! Need to use the Fat object to track down next sector
  195. pf->GetNext(sect,&sect);
  196. }
  197. // Recursively go down the tree
  198. // pchild and pnext must point to the original tree for
  199. // efficiency purposes.
  200. childsid = pde->GetChild();
  201. if (childsid != NOSTREAM)
  202. {
  203. pDir->SidToEntry(childsid,&pchild);
  204. TreeWalk(pchild,childsid);
  205. }
  206. nextsid = pde->GetNext();
  207. if (nextsid != NOSTREAM)
  208. {
  209. pDir->SidToEntry(nextsid,&pnext);
  210. TreeWalk(pnext,nextsid);
  211. }
  212. if (fixdf==TRUE)
  213. {
  214. scRc = pFat->Flush();
  215. scRcM = pMiniFat->Flush();
  216. if (FAILED(scRc) || FAILED(scRcM))
  217. printf("Failed to write all modified FatSects out to stream.\n");
  218. }
  219. }
  220. BOOL GetOption(int argc, char *argv[])
  221. {
  222. char *pszArg, *pszProgName;
  223. BOOL ArgsOK = FALSE, Fix = FALSE;
  224. pszProgName = *argv++;
  225. while ((pszArg = *argv++) != NULL)
  226. {
  227. if (*pszArg == '-' || *pszArg == '/')
  228. {
  229. switch (tolower(*(++pszArg)))
  230. {
  231. case 'f': // fix the errors.
  232. Fix = TRUE; // open file with read-only without a copy.
  233. break;
  234. case 'n': // name of the docfile to be opened.
  235. // path of the filename.
  236. mbstowcs(pwcsDocfile,++pszArg,_MAX_PATH);
  237. Fix = FALSE;
  238. ArgsOK = TRUE;
  239. break;
  240. default:
  241. break;
  242. }
  243. }
  244. else ArgsOK = FALSE;
  245. }
  246. if (ArgsOK == FALSE)
  247. {
  248. printf("0 argument or invalid command line argument.\n");
  249. Usage(pszProgName);
  250. exit(INVALID_ARG);
  251. }
  252. return Fix;
  253. }
  254. void Usage(char *pszProgName)
  255. {
  256. printf("Usage: %s\n", pszProgName);
  257. printf(" -f fix requested by user.\n");
  258. printf(" -n <name of docfile>\n");
  259. printf("The -n option must be specified.\n");
  260. }
  261. void DIFTable() // August 11, 1992
  262. {
  263. // Walk through each DIF sector array to detect loops and
  264. // crosslinks.
  265. SCODE scRc;
  266. BOOL FatOK = TRUE;
  267. SECT sect, sectentry;
  268. FSINDEX diflen, fatlen, fitable, fioffset, index, minifatlen,uldif,ulr;
  269. CFatSect *pfsec;
  270. diflen = pheader->GetDifLength();
  271. fatlen = pheader->GetFatLength();
  272. minifatlen = pheader->GetMiniFatLength();
  273. // testing the validity of pheader->GetDifLength
  274. if (fatlen > CSECTFAT) // diflen > 0
  275. {
  276. ulr = ( ((fatlen - CSECTFAT)%TABLE_SIZE) > 0 )? 1: 0;
  277. uldif = CSECTFAT + (fatlen-CSECTFAT)/TABLE_SIZE + ulr;
  278. }
  279. else uldif = 0;
  280. if (diflen!=uldif)
  281. printf("DIFLEN in header is inconsistent with FatLEN.\n");
  282. for (index=0; index<fatlen; index++)
  283. {
  284. pDIFat->GetFatSect(index,&sect);
  285. if (sect < MAXREGSECT)
  286. {
  287. fitable = sect / TABLE_SIZE;
  288. fioffset = sect % TABLE_SIZE;
  289. pfvFat->GetTable(fitable,&pfsec); // pfsec = ptr to CFatSect
  290. sectentry = pfsec->GetSect(fioffset);
  291. if (sectentry > MAXREGSECT)
  292. pfsec->SetSect(fioffset,SIDFAT);
  293. else
  294. {
  295. printf("Crosslink! DIF index #%u points\n",index);
  296. printf(" to the same location %u.\n", sect);
  297. FatOK = FALSE;
  298. }
  299. }
  300. pDIFat->GetFatSect(index+1,&sect);
  301. }
  302. if (FatOK == TRUE)
  303. printf("No errors found in DIFat.\n");
  304. // Walk through the terminating cells in each sector array to check
  305. // the correctness of chaining.
  306. printf("\tWalking through DIFTable chain.\n");
  307. for (index = 0; index<diflen; index++)
  308. {
  309. pDIFat->GetSect(index,&sect);
  310. fitable = sect/TABLE_SIZE;
  311. fioffset = sect%TABLE_SIZE;
  312. pfvFat->GetTable(fitable,&pfsec); // pfsec = ptr to CFatSect
  313. sectentry = pfsec->GetSect(fioffset);
  314. if ((sectentry!=ENDOFCHAIN) && (index == diflen-1))
  315. printf("ERROR! ENDOFCHAIN expected at the end of DIFat.\n.");
  316. pDIFat->SetFatSect(fioffset,SIDDIF);
  317. pfsec->SetSect(fioffset,SIDDIF);
  318. }
  319. if (fixdf==TRUE)
  320. {
  321. scRc = pDIFat->FlushAll();
  322. if (FAILED(scRc))
  323. printf("Failed to write all modified FatSects out to stream.\n");
  324. }
  325. }