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.

919 lines
34 KiB

  1. /*****************************************************************************************************************
  2. FILENAME: FastFat2.cpp
  3. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4. This file contains the routines that traverse the FAT directory structure returning file names so our
  5. engine can process every file on the disk. See other write-ups for the format of the FAT file system.
  6. We traverse the directory tree beginning in the root directory. All files in that directory are
  7. processed first, then we move down into the first subdirectory and process every file in it. Then we
  8. move down again into its first subdirectory and process all the files in it, etc. Once we reach the
  9. end of a directory chain, then we move back up one level and immediately move down again into the next
  10. undone directory from that level. We keep the FAT directories in memory for our current directory and
  11. each directory in the chain above us.
  12. We process all the files before moving into subdirs to save space. As soon as all the files are
  13. processed, we delete all the file entries from our current FAT directory and that consolidates all
  14. the directories. We can then step through each directory.
  15. */
  16. #include "stdafx.h"
  17. extern "C"{
  18. #include <stdio.h>
  19. }
  20. #ifdef BOOTIME
  21. #include "Offline.h"
  22. #else
  23. #include <Windows.h>
  24. #endif
  25. extern "C" {
  26. #include "SysStruc.h"
  27. }
  28. #include "ErrMacro.h"
  29. #include "DfrgCmn.h"
  30. #include "DfrgEngn.h"
  31. #include "DfrgRes.h"
  32. #include "DfrgFat.h"
  33. #include "DasdRead.h"
  34. #include "Devio.h"
  35. #include "Extents.h"
  36. #include "FatSubs.h"
  37. #include "FastFat2.h"
  38. #include "Alloc.h"
  39. #include "IntFuncs.h"
  40. #ifdef OFFLINEDK
  41. #include "OffLog.h"
  42. #else
  43. #include "Logging.h"
  44. #endif
  45. #include "ErrMsg.h"
  46. #include "Event.h"
  47. #include "GetDfrgRes.h"
  48. static TREE_DATA TreeData;
  49. /*****************************************************************************************************************
  50. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  51. ROUTINE DESCRIPTION:
  52. This routine gets basic data about the FAT volume that the FastFat2 routines need to carry out their work.
  53. Most notably, this routine loads in the root dir and initializes a pass for NextFatFile() which traverses
  54. the disk. NextFatFile() can be re-initialized to begin again at the beginning of the disk simply by calling
  55. this routine again.
  56. INPUT + OUTPUT:
  57. None.
  58. GLOBALS:
  59. OUT TreeData - Initialized by this function. Holds all the current data necessary for
  60. NextFatFile() to traverse the directory tree of a disk.
  61. IN OUT Multiple VolData fields. Pointless to enumerate here.
  62. Of note:
  63. OUT VolData.FirstDataOffset - The byte offset from the beginning of the disk to the data portion of the disk.
  64. OUT VolData.FatOffset - The byte offset of the FAT from the beginning of the disk.
  65. RETURN:
  66. TRUE - Success
  67. FALSE - Failure
  68. */
  69. BOOLEAN
  70. GetFatBootSector(
  71. )
  72. {
  73. HANDLE hBoot = NULL;
  74. BYTE* pBoot = NULL;
  75. DWORD ReservedSectors = 0;
  76. DWORD NumberOfFats = 0;
  77. DWORD SectorsPerFat = 0;
  78. DWORD NumberOfRootEntries = 0;
  79. DWORD dMirroring = 0;
  80. BOOL bMirroring = FALSE;
  81. DWORD ActiveFat = 0;
  82. EXTENT_LIST* pExtentList = 0;
  83. LONGLONG Cluster = 0;
  84. DWORD Extent = 0;
  85. STREAM_EXTENT_HEADER* pStreamExtentHeader = NULL;
  86. __try{
  87. //#Dk172_066 Divide by zero check goes here...
  88. EF(VolData.BytesPerSector!=0);
  89. //0.0E00 Load the boot sector
  90. EF((hBoot = DasdLoadSectors(VolData.hVolume, 0, 1, VolData.BytesPerSector)) != NULL);
  91. EF((pBoot = (BYTE*)GlobalLock(hBoot)) != NULL);
  92. //1.0E00 Get data from the boot sector for this volume about where the on disk structures are.
  93. // bug #187823 change from casting to memcpy to avoid memory allignment errors
  94. ReservedSectors = (DWORD)*((WORD*)(pBoot+0x0E)); //Total number of reserved sectors from the
  95. //beginning of the disk including
  96. // memcpy(&ReservedSectors,(pBoot+0x0E),sizeof(DWORD)); //Total number of reserved sectors from the
  97. // //beginning of the disk including
  98. // bug #187823 change from casting to memcpy to avoid memory allignment errors
  99. //the boot records. 2 bytes long.
  100. memcpy(&NumberOfFats,(pBoot+0x10),sizeof(UCHAR)); //The number of FATs on the drive. 1 byte long.
  101. memcpy(&SectorsPerFat,(pBoot+0x16),sizeof(UCHAR)*2); //The number of sectors per FAT, or 0 if this is
  102. memcpy(&VolData.FatVersion,(pBoot+0x2a),sizeof(UCHAR)*2); //The version number of the FAT or FAT32 volume.
  103. //1.0E00 If this is a FAT volume...
  104. if(SectorsPerFat != 0){
  105. // bug #187823 change from casting to memcpy to avoid memory allignment errors
  106. memcpy(&NumberOfRootEntries,(pBoot+0x11),sizeof(UCHAR)*2);
  107. }
  108. //1.0E00 Otherwise this is a FAT32 volume.
  109. else{
  110. memcpy(&SectorsPerFat,(pBoot+0x24),sizeof(UCHAR)*4); //The number of sectors per fat on a FAT32 volume.
  111. memcpy(&dMirroring,(pBoot+0x28),sizeof(UCHAR)*2); //Extract the bit that says whether mirroring
  112. bMirroring = dMirroring & 0x0080 ? FALSE : TRUE; //of the FATs is enabled. 1=Mirroring disabled.
  113. memcpy(&ActiveFat,(pBoot+0x28),sizeof(UCHAR)*2); //Extract the number of the active FAT. Only valid
  114. ActiveFat = ActiveFat & 0x0007;
  115. }
  116. //1.0E00 On FAT and FAT32 volumes, FirstDataOffset points to the "zeroth" byte. That is, if you do a
  117. //1.0E00 dasd read on a FAT or FAT32 volume for byte 0, you will get the actual byte number of
  118. //1.0E00 FirstDataOffset on the volume. That is - byte 0 is byte 0 of the data portion of a FAT volume.
  119. //1.0E00 If FatSectorsPerFat is zero that indicates this is a FAT32 volume.
  120. if(VolData.FileSystem == FS_FAT){
  121. VolData.FirstDataOffset =
  122. (DWORD) ((ReservedSectors + //The number of reserved sectors at the beginning of the disk.
  123. NumberOfFats * SectorsPerFat + //The number of sectors in the FAT at the beginning of the disk.
  124. sizeof(DIRSTRUC) * NumberOfRootEntries / VolData.BytesPerSector) * //The number of sectors in the root directory.
  125. VolData.BytesPerSector); //Gives us a byte offset instead of a sector offset.
  126. }
  127. //1.0E00 This is a FAT32 volume.
  128. else if(VolData.FileSystem == FS_FAT32){
  129. VolData.FirstDataOffset =
  130. (ReservedSectors + //The number of reserved sectors at the beginning of the disk.
  131. (NumberOfFats * SectorsPerFat)) * //The number of sectors in the FAT at the beginning of the disk.
  132. (DWORD)VolData.BytesPerSector; //Gives us a byte offset instead of a sector offset.
  133. }
  134. else{
  135. EF(FALSE);
  136. }
  137. //1.0E00 We also need the offset of the FAT we will be reading from. This is so GetExtentListManuallyFat can
  138. //1.0E00 load in sections of the FAT at it's discretion.
  139. if(VolData.FileSystem == FS_FAT){
  140. //1.0E00 On FAT, we'll just use the FAT -- all the data is mirrored.
  141. VolData.FatOffset = (LONGLONG)(ReservedSectors * (DWORD)VolData.BytesPerSector);
  142. VolData.FatMirrOffset = (LONGLONG)((ReservedSectors+SectorsPerFat) * (DWORD)VolData.BytesPerSector);
  143. }
  144. else if(VolData.FileSystem == FS_FAT32){
  145. //1.0E00 On FAT32, if mirroring is enabled, then we use the first FAT since all FAT's are identical.
  146. if(bMirroring){
  147. //1.0E00 The first FAT comes right after the reserved sectors.
  148. VolData.FatOffset = (LONGLONG)(ReservedSectors * (DWORD)VolData.BytesPerSector);
  149. VolData.FatMirrOffset = (LONGLONG)((ReservedSectors+SectorsPerFat) * (DWORD)VolData.BytesPerSector);
  150. }
  151. //1.0E00 If mirroring is disabled, then we have to use whichever FAT is the active FAT.
  152. else{
  153. //1.0E00 Use the active FAT as specified by the boot sector.
  154. VolData.FatOffset = (LONGLONG)((ReservedSectors + (ActiveFat * SectorsPerFat)) * VolData.BytesPerSector);
  155. VolData.FatMirrOffset = 0;
  156. }
  157. }
  158. //Initialize the TreeData structure
  159. ZeroMemory(&TreeData, sizeof(TREE_DATA));
  160. TreeData.bProcessingFiles = TRUE;
  161. //0.0E00 alloc mem for and read the root dir's clusters
  162. EF(VolData.BytesPerSector != 0);
  163. if(VolData.FileSystem == FS_FAT){
  164. //1.0E00 Read in the root directory.
  165. EF((TreeData.FatTreeHandles[0] =
  166. DasdLoadSectors(
  167. VolData.hVolume,
  168. //The root dir resides right after the FATs.
  169. ReservedSectors + (NumberOfFats * SectorsPerFat),
  170. (NumberOfRootEntries * sizeof(DIRSTRUC)) / VolData.BytesPerSector,
  171. VolData.BytesPerSector)) != NULL);
  172. //0.0E00 get a pointer to the root dir's clusters
  173. EF((TreeData.pCurrentFatDir = TreeData.pCurrentEntry = (DIRSTRUC*)GlobalLock(TreeData.FatTreeHandles[0])) != NULL);
  174. TreeData.llCurrentFatDirLcn[0] = 0xFFFFFFFF;
  175. }
  176. //1.0E00 For FAT32 we have to create an extent list for the root directory and load it in.
  177. else{
  178. //1.0E00 Open the root directory.
  179. VolData.vFileName = VolData.cVolumeName;
  180. // put a trailing backslash to open the root dir
  181. VolData.vFileName += L"\\";
  182. VolData.bDirectory = TRUE;
  183. EF(OpenFatFile());
  184. //1.0E00 Get the extent list for the root directory.
  185. EF(GetExtentList(DEFAULT_STREAMS, NULL));
  186. pExtentList = (EXTENT_LIST*)((char*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER) + sizeof(STREAM_EXTENT_HEADER));
  187. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((char*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER));
  188. //1.0E00 Allocate the memory where we'll hold the root directory. This will be the first entry in FatTreeHandles.
  189. AllocateMemory((DWORD)(VolData.NumberOfClusters*VolData.BytesPerCluster)+(DWORD)VolData.BytesPerSector,
  190. &(TreeData.FatTreeHandles[0]), (void**)&(TreeData.pCurrentEntry));
  191. EF(TreeData.FatTreeHandles[0] != NULL);
  192. TreeData.pCurrentFatDir = TreeData.pCurrentEntry;
  193. TreeData.llCurrentFatDirLcn[0] = pExtentList->StartingLcn;
  194. //1.0E00 Loop through the extents of the root dir and read in each series of clusters.
  195. Cluster = 0;
  196. for(Extent=0; Extent<pStreamExtentHeader->ExtentCount; Extent++){
  197. EF(DasdReadClusters(VolData.hVolume,
  198. pExtentList[Extent].StartingLcn,
  199. pExtentList[Extent].ClusterCount,
  200. ((PUCHAR)TreeData.pCurrentEntry) + (Cluster * VolData.BytesPerCluster),
  201. VolData.BytesPerSector,
  202. VolData.BytesPerCluster));
  203. Cluster += pExtentList[Extent].ClusterCount;
  204. }
  205. }
  206. wsprintf(TreeData.DirName[0], TEXT("%s\\"), VolData.cVolumeName);
  207. }
  208. __finally{
  209. if(hBoot != NULL){
  210. while (GlobalUnlock(hBoot)){
  211. ;
  212. }
  213. EH_ASSERT(GlobalFree(hBoot) == NULL);
  214. }
  215. }
  216. return TRUE;
  217. }
  218. /*****************************************************************************************************************
  219. This is a comment for a defunct function (no pun intended), but it contains useful data about the format of the
  220. FAT file system. So, I'm leaving it in here. Zack Gainsforth 7 April 1997
  221. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  222. ROUTINE DESCRIPTION:
  223. In order to use our hooks to get a file's extent list it is necessary to get a handle to the file.
  224. It is not possible to get a handle to an active pagefile.
  225. This makes it necessary to acquire active pagefile extent lists manually.
  226. To manually acquire the extent list of a FAT file requires knowing the starting cluster of the file.
  227. The only way to get the starting cluster of a FAT file is to look at the file's directory entry.
  228. Active pagefiles are always in the root directory.
  229. There is no directory entry anywhere for the root directory.
  230. The location of the root directory's clusters is indicated from fields in the boot sector.
  231. GetFatBootSector extracts the location of the root directory from the boot sector and loads the
  232. root directory's clusters.
  233. This routine must be called after GetFatBootSector is called and before any calls are made to
  234. NextFatFile.
  235. This routine scans through the root directory's entries looking for a file named "pagefile.sys"
  236. using case insensitive compares. When such a file is found an attempt is made to open the file.
  237. If the open succeeds it is not an active pagefile and can therefore be processed the same way as
  238. all the other files on the volume.
  239. If the open fails it is an active pagefile (hopefully) so manual acquisition of the file's
  240. extent list begins.
  241. The FAT (File Allocation Table) contains one entry for each data cluster on the disk plus 2
  242. entries at the beginning which are actually used to indicate the volume type: Floppy, fixed, etc.
  243. FAT entry 2 references the first data cluster on the disk, entry 3 the 2nd, etc.
  244. The first data cluster on the disk is located by adding the number of boot sectors (always 1)
  245. to the number of FATS (always 2) times sectors per FAT (varies) and number of root directory
  246. sectors. The first sector of the first data cluster is NOT always a multiple of the sectors per
  247. cluster for the volume.
  248. FAT entries are either 12 or 16-bits each and all FAT entries on a volume are are the same size.
  249. Most volumes have 16-bit FATs since only VERY small volumes (about 10 mb or less) have 12-bit FATs.
  250. A 16-bit FAT uses 2 bytes to represent each cluster on the volume and a 12-bit FAT shares each
  251. 3-byte set with 2 clusters or one byte and one nibble of another byte per volume cluster.
  252. All volumes with less than 4087 total clusters have 12-bit FATs and all others are 16-bit.
  253. Each FAT entry contains either a code or the cluster number of the next cluster of the file.
  254. The codes are: 0 = free cluster (since the first data cluster is FAT entry 2 [cluster 2] there
  255. is no cluster 0); FFF8-FFFF (or FF8-FFF in 12-bit FATS) is last cluster in a file; xxx through xxx are
  256. bad clusters.
  257. Files are recorded as "cluster chains" with the first cluster of the file in the file's directory
  258. entry, the FAT entry for the file's first cluster containing the cluster number of the file's
  259. second cluster, the FAT entry for the file's second cluster containing the cluster number of the
  260. file's third cluster, etc. and the FAT entry for the file's last cluster containing FFF8-FFFF (or FF8-FFF
  261. in 12-bit FATS).
  262. To manually build an extent list for a FAT file the file's cluster chain is scanned and all clusters
  263. that are adjacent to each other are considered an extent.
  264. The extent list will be used by the Windows NT OS and filesystems. Although NT and DOS both are
  265. products of Microsoft, DOS (and FAT volume dir entries and the FATs) views the first data cluster
  266. on a FAT volume as cluster number 2 but NT views it as cluster number 0 (for compatibility with NTFS).
  267. So after the extent list is built 2 is subtracted from all cluster numbers in the list.
  268. */
  269. /*****************************************************************************************************************
  270. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  271. ROUTINE DESCRIPTION:
  272. This function steps through the directory tree of the FAT disk and returns a new filename each time
  273. it is called. It automatically remembers its position in the tree between calls.
  274. INPUT + OUTPUT:
  275. None.
  276. GLOBALS:
  277. IN OUT TreeData - Holds all the current data necessary to traverse the tree.
  278. IN VolData.FileSystem - Whether this is a FAT or FAT32
  279. OUT VolData.bDirectory - Whether the file found was a directory or not.
  280. OUT VolData.FileSize - The size in bytes of the file found.
  281. OUT VolData.StartingLcn - The first LCN of the file found.
  282. RETURN:
  283. Success - TRUE
  284. Failure - FALSE
  285. On Success:
  286. VolData.vFileName contains the filename of the next file, or a zero-length string if no more files.
  287. */
  288. BOOL
  289. NextFatFile(
  290. )
  291. {
  292. //0.0E00 Until we've found a file or hit the end of the disk
  293. while(TRUE){
  294. if(TreeData.bProcessingFiles){
  295. //0.0E00 Step through each entry in the current FAT dir.
  296. while(TRUE){
  297. //0.0E00 IF no more entries,
  298. if(TreeData.pCurrentEntry->Name[0] == EndOfDirectory){
  299. //0.0E00 Strip FAT dir of anything except subdir entries. (StripDir)
  300. EF(StripDir(&TreeData, KEEP_DIRECTORIES));
  301. //0.0E00 Set bProcessingFiles = FALSE so we will process directories.
  302. TreeData.bProcessingFiles = FALSE;
  303. TreeData.pCurrentEntry = TreeData.pCurrentFatDir;
  304. break;
  305. }
  306. if(TreeData.pCurrentEntry->Name[0] == Deleted){
  307. TreeData.pCurrentEntry++;
  308. continue;
  309. }
  310. //0.0E00 IF this entry is a file
  311. if(!(TreeData.pCurrentEntry->Attribute & LabelAttribute) &&
  312. TreeData.pCurrentEntry->Attribute != UnicodeAttribute){
  313. //0.0E00 Fill in VolData + return
  314. VolData.bDirectory = (TreeData.pCurrentEntry->Attribute & DirAttribute) ? TRUE : FALSE;
  315. VolData.FileSize = TreeData.pCurrentEntry->FileSize;
  316. //0.0E00 If this is a FAT volume.
  317. if(VolData.FileSystem == FS_FAT){
  318. VolData.StartingLcn = TreeData.pCurrentEntry->ClusterLow;
  319. }
  320. //1.0E00 Otherwise it is FAT32
  321. else{
  322. VolData.StartingLcn = (LONGLONG)((DWORD)TreeData.pCurrentEntry->ClusterLow | (DWORD)(TreeData.pCurrentEntry->ClusterHigh << 16));
  323. }
  324. #ifdef OFFLINEDK
  325. //If the StartingLcn is set to zero, then this is a small file (no clusters).
  326. if(VolData.StartingLcn == 0){
  327. //Set the StartingLcn to an invalid cluster number.
  328. VolData.StartingLcn = 0xFFFFFFFF;
  329. }
  330. else{
  331. //Otherwise change the StartingLcn to the correct Lcn (handle the FAT cluster number offset of 2).
  332. VolData.StartingLcn -= 2;
  333. }
  334. //0.0E00 Store the Lcn of the parent directory to this file (so other code can load the directory and read the entry if it likes).
  335. VolData.MasterLcn = TreeData.llCurrentFatDirLcn[TreeData.dwCurrentLevel];
  336. #endif
  337. #ifndef OFFLINEDK
  338. //0.0E00 If the StartingLcn is past the end of the disk, or the file size is greater than the disk, the disk is corrupt.
  339. //0.0E00 +2 because the FAT LCNs start from 2. So if there are 10 cluster #'s, the highest possible is 12.
  340. if (VolData.StartingLcn > VolData.TotalClusters+2 ||
  341. VolData.FileSize > (VolData.TotalClusters+2) * VolData.BytesPerCluster){
  342. VString formatString(IDMSG_CORRUPT_DISK, GetDfrgResHandle());
  343. TCHAR szMsg[500];
  344. DWORD_PTR dwParams[2];
  345. dwParams[0] = (DWORD_PTR)VolData.cDisplayLabel;
  346. dwParams[1] = 0;
  347. //0.0E00 Print out a user friendly message.
  348. //0.0E00 IDMSG_CORRUPT_DISK - "Diskeeper has detected corruption on drive %s:\nPlease run chkdsk /f"
  349. EF(FormatMessage(
  350. FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
  351. formatString.GetBuffer(),
  352. 0,
  353. 0,
  354. szMsg,
  355. sizeof(szMsg)/sizeof(TCHAR),
  356. (va_list*)dwParams));
  357. SendErrData(szMsg, ENGERR_GENERAL);
  358. EF(LogEvent(MSG_ENGINE_ERROR, szMsg));
  359. //0.0E00 Also print out a techie message.
  360. EF(FALSE);
  361. }
  362. #endif
  363. EF(GetUnicodePath(&TreeData, VolData.vFileName));
  364. TreeData.pCurrentEntry++;
  365. return TRUE;
  366. }
  367. //0.0E00 ELSE ignore the entry -- point to the next.
  368. TreeData.pCurrentEntry++;
  369. }
  370. }
  371. else { //if(!TreeData.bProcessingFiles){
  372. //0.0E00 Step through each entry in the current FAT dir.
  373. while(TRUE){
  374. //0.0E00 IF no more entries,
  375. if(TreeData.pCurrentEntry->Name[0] == EndOfDirectory){
  376. //0.0E00 IF in root dir, return finished code.
  377. if(TreeData.dwCurrentLevel == 0){
  378. VolData.vFileName.Empty();
  379. EF(TreeData.FatTreeHandles[TreeData.dwCurrentLevel] != NULL);
  380. // this one is sometimes locked more than once
  381. while (GlobalUnlock(TreeData.FatTreeHandles[TreeData.dwCurrentLevel])){
  382. ;
  383. }
  384. EH_ASSERT(GlobalFree(TreeData.FatTreeHandles[TreeData.dwCurrentLevel]) == NULL);
  385. TreeData.FatTreeHandles[TreeData.dwCurrentLevel] = NULL;
  386. TreeData.DirName[TreeData.dwCurrentLevel][0] = 0;
  387. TreeData.llCurrentFatDirLcn[TreeData.dwCurrentLevel] = 0;
  388. return TRUE;
  389. }
  390. //0.0E00 ELSE Step up one directory in the chain.
  391. else{
  392. EF(TreeData.FatTreeHandles[TreeData.dwCurrentLevel] != NULL);
  393. EH_ASSERT(GlobalUnlock(TreeData.FatTreeHandles[TreeData.dwCurrentLevel]) == FALSE);
  394. EH_ASSERT(GlobalFree(TreeData.FatTreeHandles[TreeData.dwCurrentLevel]) == NULL);
  395. TreeData.FatTreeHandles[TreeData.dwCurrentLevel] = NULL;
  396. TreeData.DirName[TreeData.dwCurrentLevel][0] = 0;
  397. TreeData.llCurrentFatDirLcn[TreeData.dwCurrentLevel] = 0;
  398. TreeData.dwCurrentLevel--;
  399. EF(TreeData.FatTreeHandles[TreeData.dwCurrentLevel] != NULL);
  400. TreeData.pCurrentEntry = TreeData.pCurrentFatDir = (DIRSTRUC*)GlobalLock(TreeData.FatTreeHandles[TreeData.dwCurrentLevel]);
  401. TreeData.pCurrentEntry += TreeData.CurrentEntryPos[TreeData.dwCurrentLevel];
  402. TreeData.bMovedUp = TRUE;
  403. }
  404. continue;
  405. }
  406. //0.0E00 IF this entry is a directory
  407. if(TreeData.pCurrentEntry->Attribute & DirAttribute){
  408. //0.0E00 Step into it.
  409. EF(LoadDir(&TreeData));
  410. if(VolData.vFileName.GetLength() == 0){
  411. TreeData.pCurrentEntry++;
  412. continue;
  413. }
  414. break;
  415. }
  416. //0.0E00 ELSE ignore the entry -- point to the next.
  417. TreeData.pCurrentEntry++;
  418. }
  419. }
  420. }
  421. }
  422. /*****************************************************************************************************************
  423. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  424. ROUTINE DESCRIPTION:
  425. Removes undesired entries in a FAT directory. It does this by scooting desired entries up over undesired entries.
  426. INPUT + OUTPUT:
  427. IN OUT pTreeData - Contains the FAT directory to strip.
  428. IN dwFlags - Indicates flags for which type of entries to keep, The flags are KEEP_DIRECTORIES and KEEP_FILES.
  429. GLOBALS:
  430. None.
  431. RETURN:
  432. Success - TRUE
  433. Failure - FALSE
  434. */
  435. BOOL
  436. StripDir(
  437. IN OUT TREE_DATA* pTreeData,
  438. IN DWORD dwFlags
  439. )
  440. {
  441. CHAR * pDest; //0.0E00 Where the MoveMemory will move to.
  442. CHAR * pStart; //0.0E00 The start of where we're moving from (the first unicode entry, for example)
  443. CHAR * pEnd; //0.0E00 The end of where we're moving from (just after the normal fat entry, for example)
  444. //0.0E00 Set everything to point to the beginning of the FAT dir.
  445. pTreeData->pCurrentEntry = pTreeData->pCurrentFatDir;
  446. pDest = (CHAR*)pTreeData->pCurrentEntry;
  447. pStart = pEnd = pDest;
  448. //0.0E00 Step through each entry in the current FAT dir
  449. while(pTreeData->pCurrentEntry->Name[0] != EndOfDirectory){
  450. //0.0E00 Keep track of the last entry we are keeping per dwFlags.
  451. //0.0E00 If this is an entry to keep per dwFlags, MoveMemory it just below the last entry.
  452. //0.0E00 IF it's deleted, and we are not keeping deleted files strip it.
  453. if((!(dwFlags & KEEP_DELFILES)) &&
  454. (!(dwFlags & KEEP_DELDIRECTORIES)) &&
  455. (TreeData.pCurrentEntry->Name[0] == Deleted) ) {
  456. //0.0E00 Look at the next entry next.
  457. pTreeData->pCurrentEntry++;
  458. pEnd += sizeof(DIRSTRUC);
  459. pStart = pEnd;
  460. }
  461. //0.0E00 IF it's a unicode entry update the pEnd pointer, we don't know yet whether or not to keep it.
  462. else if(pTreeData->pCurrentEntry->Attribute == UnicodeAttribute){
  463. //0.0E00 Look at the next entry next.
  464. pTreeData->pCurrentEntry++;
  465. pEnd += sizeof(DIRSTRUC);
  466. }
  467. //0.0E00 IF it's a dir attribute and we're supposed to keep dirs per dwFlags, keep it.
  468. else if(pTreeData->pCurrentEntry->Attribute & DirAttribute &&
  469. dwFlags & KEEP_DIRECTORIES &&
  470. pTreeData->pCurrentEntry->Name[0] != TEXT('.')){ //Don't keep the . and .. directories.
  471. //0.0E00 Look at the next entry next.
  472. pTreeData->pCurrentEntry++;
  473. pEnd += sizeof(DIRSTRUC);
  474. //0.0E00 Do the MoveMemory as long as we're not moving it to the same place it already is.
  475. if(pDest != pStart){
  476. MoveMemory(pDest, pStart, pEnd-pStart);
  477. }
  478. pDest += pEnd-pStart;
  479. pStart = pEnd;
  480. }
  481. //0.0E00 IF it's a file attribute and we're supposed to keep files per dwFlags, keep it.
  482. else if(!(TreeData.pCurrentEntry->Attribute & LabelAttribute) &&
  483. !(TreeData.pCurrentEntry->Attribute & DirAttribute) &&
  484. TreeData.pCurrentEntry->Attribute != UnicodeAttribute &&
  485. ((dwFlags & KEEP_FILES) || (dwFlags & KEEP_DELFILES))) {
  486. //0.0E01 Check if we want to skip Deleted files or Active Files.
  487. if ( (!(dwFlags & KEEP_DELFILES) && (TreeData.pCurrentEntry->Name[0] == Deleted)) ||
  488. (!(dwFlags & KEEP_FILES) && !(TreeData.pCurrentEntry->Name[0] == Deleted)) ) {
  489. // Yes, skip this entry
  490. pTreeData->pCurrentEntry++;
  491. pEnd += sizeof(DIRSTRUC);
  492. pStart = pEnd;
  493. }
  494. else {
  495. //0.0E01 No, Save this entry
  496. //0.0E00 Look at the next entry next.
  497. pTreeData->pCurrentEntry++;
  498. pEnd += sizeof(DIRSTRUC);
  499. // Save this entry
  500. //0.0E00 Do the MoveMemory as long as we're not moving it to the same place it already is.
  501. if(pDest != pStart){
  502. MoveMemory(pDest, pStart, pEnd-pStart);
  503. }
  504. pDest += pEnd-pStart;
  505. pStart = pEnd;
  506. }
  507. }
  508. //0.0E00 This is an entry that we strip, so just update the pointers.
  509. else{
  510. //0.0E00 Look at the next entry next.
  511. pTreeData->pCurrentEntry++;
  512. pEnd += sizeof(DIRSTRUC);
  513. pStart = pEnd;
  514. }
  515. }
  516. //0.0E00 When the whole FAT dir is done, copy the null entry at the end.
  517. if(pDest != pStart){
  518. pEnd += sizeof(DIRSTRUC);
  519. MoveMemory(pDest, pStart, pEnd-pStart);
  520. pDest += pEnd-pStart;
  521. }
  522. //0.0E00 Reset pointers and variables in pTreeData.
  523. pTreeData->pCurrentEntry = pTreeData->pCurrentFatDir;
  524. return TRUE;
  525. }
  526. /*****************************************************************************************************************
  527. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  528. ROUTINE DESCRIPTION:
  529. Goes down one level in the directory tree. It takes the current directory entry and
  530. opens it up and sets all the pointers and variables to point there.
  531. INPUT + OUTPUT:
  532. IN OUT pTreeData - Holds all the pointers and variable changes.
  533. GLOBALS:
  534. OUT VolData.vFileName - The name of the directory found.
  535. RETURN:
  536. Success - TRUE
  537. Failure - FALSE
  538. */
  539. BOOL
  540. LoadDir(
  541. IN TREE_DATA* pTreeData
  542. )
  543. {
  544. LONGLONG Cluster;
  545. PEXTENT_LIST pExtentList;
  546. DWORD Extent;
  547. VString fileName;
  548. STREAM_EXTENT_HEADER* pStreamExtentHeader = NULL;
  549. //0.0E00 Loop through the current directory until a directory entry that can be opened pops up.
  550. while(TRUE){
  551. //0.0E00 If this is the end of the directory, return
  552. if(pTreeData->pCurrentEntry->Name[0] == EndOfDirectory){
  553. VolData.vFileName.Empty();
  554. return TRUE;
  555. }
  556. //#DK183_008
  557. //0.0E00 IF this entry is a file
  558. if(!(pTreeData->pCurrentEntry->Attribute & LabelAttribute) &&
  559. pTreeData->pCurrentEntry->Attribute != UnicodeAttribute){
  560. GetUnicodeName(pTreeData, fileName);
  561. //#Dk183_008
  562. //0.0E00 Skip this directory if it is the . or .. directory since we can't open them.
  563. if(fileName != L"." && fileName != L"..") {
  564. break;
  565. }
  566. }
  567. //0.0E00 Go to the next entry.
  568. pTreeData->pCurrentEntry++;
  569. }
  570. //0.0E00 Get the pull path for this directory.
  571. GetUnicodePath(pTreeData, VolData.vFileName);
  572. //0.0E00 Put this directory's name in the next level in the TreeData structure.
  573. lstrcpy(pTreeData->DirName[pTreeData->dwCurrentLevel+1], fileName.GetBuffer());
  574. lstrcat(pTreeData->DirName[pTreeData->dwCurrentLevel+1], TEXT("\\"));
  575. //0.0E00 Note that this is a directory.
  576. VolData.bDirectory = TRUE;
  577. #ifndef OFFLINEDK
  578. //0.0E00 Open the directory.
  579. if(!OpenFatFile()){
  580. //0.0E00 If it won't open, error out...
  581. EH(FALSE);
  582. //0.0E00 And clean up so that other directories can be loaded in the future.
  583. VolData.vFileName.Empty();
  584. pTreeData->DirName[pTreeData->dwCurrentLevel+1][0] = 0; //Don't leave this directory's name in the TreeData.
  585. return TRUE;
  586. }
  587. //0.0E00 Get the extent list for this directory.
  588. EF(GetExtentList(DEFAULT_STREAMS, NULL));
  589. #else
  590. //0.0E00 If this is a FAT volume.
  591. //0.0E00 Load the starting Lcn for this directory file.
  592. if(VolData.FileSystem == FS_FAT){
  593. VolData.StartingLcn = TreeData.pCurrentEntry->ClusterLow;
  594. }
  595. //1.0E00 Otherwise it is FAT32
  596. else{
  597. VolData.StartingLcn = (LONGLONG)((DWORD)TreeData.pCurrentEntry->ClusterLow | (DWORD)(TreeData.pCurrentEntry->ClusterHigh << 16));
  598. }
  599. VolData.StartingLcn -= 2;
  600. //0.0E00 Get the extent list for this directory.
  601. EF(GetExtentListManuallyFat());
  602. #endif
  603. pExtentList = (EXTENT_LIST*)((char*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER) + sizeof(STREAM_EXTENT_HEADER));
  604. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((char*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER));
  605. //0.0E00 Set the pTreeData variables to point one level down.
  606. pTreeData->CurrentEntryPos[pTreeData->dwCurrentLevel] = (ULONG)(pTreeData->pCurrentEntry - pTreeData->pCurrentFatDir) + 1;
  607. GlobalUnlock(pTreeData->FatTreeHandles[pTreeData->dwCurrentLevel]);
  608. pTreeData->dwCurrentLevel++;
  609. pTreeData->bMovedUp = FALSE;
  610. pTreeData->bProcessingFiles = TRUE;
  611. pTreeData->pCurrentEntry = pTreeData->pCurrentFatDir = 0;
  612. pTreeData->FatTreeHandles[pTreeData->dwCurrentLevel] = NULL;
  613. pTreeData->llCurrentFatDirLcn[pTreeData->dwCurrentLevel] = pExtentList->StartingLcn;
  614. //0.0E00 Allocate memory for the FAT dir.
  615. EF(AllocateMemory((DWORD)((VolData.NumberOfClusters * VolData.BytesPerCluster) + VolData.BytesPerSector),
  616. &pTreeData->FatTreeHandles[pTreeData->dwCurrentLevel],
  617. (void**)&pTreeData->pCurrentFatDir));
  618. pTreeData->pCurrentEntry = pTreeData->pCurrentFatDir;
  619. //0.0E00 Load the FAT dir from disk, extent by extent.
  620. Cluster = 0;
  621. for(Extent = 0; Extent < pStreamExtentHeader->ExtentCount; Extent ++){
  622. #ifndef OFFLINEDK
  623. //0.0E00 If the StartingLcn is past the end of the disk, or the file size is greater than the disk, the disk is corrupt.
  624. //0.0E00 +2 because the FAT LCNs start from 2. So if there are 10 cluster #'s, the highest possible is 12.
  625. if (VolData.StartingLcn > VolData.TotalClusters+2 ||
  626. VolData.FileSize > (VolData.TotalClusters+2) * VolData.BytesPerCluster) {
  627. TCHAR cString[500];
  628. TCHAR szMsg[500];
  629. DWORD_PTR dwParams[10];
  630. //0.0E00 Print out a user friendly message.
  631. //0.0E00 IDMSG_CORRUPT_DISK - "Diskeeper has detected corruption on drive %s: Please run chkdsk /f"
  632. dwParams[0] = (DWORD_PTR)VolData.cDisplayLabel;
  633. dwParams[1] = 0;
  634. EF(FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
  635. GetString(szMsg, sizeof(szMsg)/sizeof(TCHAR), IDMSG_CORRUPT_DISK, GetDfrgResHandle()),
  636. 0,
  637. 0,
  638. cString,
  639. sizeof(cString)/sizeof(TCHAR),
  640. (va_list*)dwParams));
  641. SendErrData(cString, ENGERR_GENERAL);
  642. EF(LogEvent(MSG_ENGINE_ERROR, cString));
  643. //0.0E00 Also print out a techie message.
  644. EF(FALSE);
  645. }
  646. #endif
  647. EF(DasdReadClusters(VolData.hVolume,
  648. pExtentList[Extent].StartingLcn,
  649. pExtentList[Extent].ClusterCount,
  650. ((PUCHAR)pTreeData->pCurrentFatDir) + (Cluster * VolData.BytesPerCluster),
  651. VolData.BytesPerSector,
  652. VolData.BytesPerCluster));
  653. Cluster += pExtentList[Extent].ClusterCount;
  654. }
  655. //0.0E00 Strip unused entries (keep files and directories).
  656. EF(StripDir(pTreeData, KEEP_FILES|KEEP_DIRECTORIES));
  657. return TRUE;
  658. }
  659. /*****************************************************************************************************************
  660. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  661. ROUTINE DESCRIPTION:
  662. Gets the complete unicode name from the FAT entries.
  663. INPUT + OUTPUT:
  664. IN OUT pTreeData - Holds pointers to the FAT entries.
  665. OUT pUnicodeName - Where we store the file name when done.
  666. GLOBALS:
  667. None.
  668. RETURN:
  669. Success - TRUE
  670. Failure - FALSE
  671. */
  672. BOOL
  673. GetUnicodeName(
  674. IN OUT TREE_DATA* pTreeData,
  675. IN VString &unicodeName
  676. )
  677. {
  678. DWORD s, d;
  679. TCHAR Dest[2*MAX_PATH + 2]; //0.0E00 Use local buffer before conversion
  680. char * Source;
  681. int i, j;
  682. //0.0E00 Check to see if there are unicode entries before this entry.
  683. //#DK188_063 and if the previous entry is deleted.
  684. if(pTreeData->pCurrentEntry > pTreeData->pCurrentFatDir &&
  685. pTreeData->pCurrentEntry[-1].Attribute == UnicodeAttribute &&
  686. pTreeData->pCurrentEntry[-1].Name[0] != Deleted) {
  687. //0.0E00 i is the offset from the normal FAT entry to the current unicode entry.
  688. i = -1;
  689. //0.0E00 is the offset in dest, for our current unicode name.
  690. j = 0;
  691. //0.0E00 Do while there are more unicode entries before the current entry.
  692. do{
  693. //0.0E00 Read the unicode out of one entry into a local buffer.
  694. //0.0E00 These are the bytes used out of the FAT entries. Not all bytes in an entry
  695. //hold unicode for the filename.
  696. CopyMemory(Dest+j, pTreeData->pCurrentEntry[i].Name+1, 10);
  697. j += 10 / sizeof(TCHAR);
  698. CopyMemory(Dest+j, &(pTreeData->pCurrentEntry[i].Reserved)+2, 12);
  699. j += 12 / sizeof(TCHAR);
  700. CopyMemory(Dest+j, (char*)&(pTreeData->pCurrentEntry[i].FileSize), 4);
  701. j += 4 / sizeof(TCHAR);
  702. //0.0E00 Backup one entry.
  703. i--;
  704. //#DK186_053 Fixed so that it does not loop infinitely.
  705. //Check to see if there are unicode entries before this entry.
  706. //#DK188_063 check to see if the previous entry is deleted.
  707. }while( (&pTreeData->pCurrentEntry[i] >= pTreeData->pCurrentFatDir) &&
  708. (pTreeData->pCurrentEntry[i].Attribute == UnicodeAttribute) &&
  709. ((pTreeData->pCurrentEntry[i+1].Name[0] & 0xF0) != 0x40) &&
  710. (pTreeData->pCurrentEntry[i].Name[0] != Deleted) );
  711. //0.0E00 Add a terminator to the end of the unicode string in case there isn't one already.
  712. Dest[j++] = 0;
  713. Dest[j] = 0;
  714. //0.0E00 When all entries have been loaded into our local buffer.
  715. unicodeName = Dest;
  716. }
  717. //0.0E00 If no unicode entries then just use the 8.3 name.
  718. else{
  719. Source = (char*)pTreeData->pCurrentEntry;
  720. //0.0E00 copy each filename char except for spaces
  721. for(s = d = 0; s < 8; s ++){
  722. if(Source[s] != ' '){
  723. Dest[d ++] = Source[s];
  724. }
  725. }
  726. //0.0E00 if file extension doesn't start with space, add "." between name and ext
  727. //0.0E00 then copy each extension char except for spaces
  728. if(Source[s] != ' '){
  729. for(Dest[d ++] = TEXT('.'); s < 11; s ++){
  730. if(Source[s] != ' '){
  731. Dest[d ++] = Source[s];
  732. }
  733. }
  734. }
  735. //0.0E00 terminate the string
  736. Dest[d] = 0;
  737. unicodeName = Dest;
  738. }
  739. //0.0E00 Return ASCII string.
  740. return TRUE;
  741. }
  742. /*****************************************************************************************************************
  743. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  744. ROUTINE DESCRIPTION:
  745. Gets the complete unicode path and filename from the FAT entries.
  746. The path is derived from pTreeData structures.
  747. INPUT + OUTPUT:
  748. IN pTreeData - Holds pointers to the FAT entries.
  749. OUT pUnicodeName - Where we store the file name when done.
  750. GLOBALS:
  751. None.
  752. RETURN:
  753. Success - TRUE
  754. Failure - FALSE
  755. */
  756. BOOL
  757. GetUnicodePath(
  758. IN TREE_DATA* pTreeData,
  759. OUT VString &unicodePath
  760. )
  761. {
  762. VString unicodeName;
  763. // Get the unicode name first.
  764. EF(GetUnicodeName(pTreeData, unicodeName));
  765. // Build the full path\name for the dir
  766. unicodePath.Empty();
  767. for(int i = 0; wcslen(pTreeData->DirName[i]); i ++){
  768. unicodePath += pTreeData->DirName[i];
  769. }
  770. if (unicodeName.IsEmpty() == FALSE) {
  771. unicodePath += unicodeName;
  772. }
  773. return TRUE;
  774. }