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.

3522 lines
143 KiB

  1. /*****************************************************************************************************************
  2. FILENAME: NtfsSubs.cpp
  3. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  4. Contains routines for manipulating the NTFS filesystem.
  5. */
  6. #include "stdafx.h"
  7. extern "C"{
  8. #include <stdio.h>
  9. }
  10. #ifdef BOOTIME
  11. #include "Offline.h"
  12. #else
  13. #include <Windows.h>
  14. #endif
  15. //#include <winioctl.h>
  16. extern "C" {
  17. #include "SysStruc.h"
  18. }
  19. #include "ErrMacro.h"
  20. #include "DfrgCmn.h"
  21. #include "DfrgEngn.h"
  22. #include "DfrgRes.h"
  23. #include "DasdRead.h"
  24. #include "Devio.h"
  25. #include "FsSubs.h"
  26. #include "NtfsSubs.h"
  27. #include "Alloc.h"
  28. #include "IntFuncs.h"
  29. #include "ErrMsg.h"
  30. #include "Message.h"
  31. #include "Logging.h"
  32. #include "GetDfrgRes.h"
  33. #include "vString.hpp"
  34. #include "getreg.h"
  35. #define THIS_MODULE 'T'
  36. #include "logfile.h"
  37. #ifdef OFFLINEDK
  38. #include "OffNtfs.h"
  39. #include "OffLog.h"
  40. #endif
  41. #define BOOT_OPTIMIZE_REGISTRY_PATH TEXT("SOFTWARE\\Microsoft\\Dfrg\\BootOptimizeFunction")
  42. #define BOOT_OPTIMIZE_REGISTRY_LCNSTARTLOCATION TEXT("LcnStartLocation")
  43. #define BOOT_OPTIMIZE_REGISTRY_LCNENDLOCATION TEXT("LcnEndLocation")
  44. //Whew! I love this code. Take good care of it for me. Zack :-)
  45. /***************************************************************************************************************
  46. FORMAT OF NTFS FILE RECORDS:
  47. An NTFS file is referenced by an entry in the MFT (Master File Table) called a filerecord
  48. (or FileRecordSegment) which is a header and a variable number of structures called Attributes.
  49. For each file there is a Base filerecord (refered to as THE filerecord) and may be one or more
  50. secondary filerecords. The attributes come in different types such as $FILE_NAME, $DATA, etc.
  51. The structure of filerecord attributes contains a FormCode field which indicates RESIDENT_FORM or
  52. NONRESIDENT_FORM. If the attribute is RESIDENT_FORM then the attribute's information is contained
  53. within the attribute. If the attribute is NONRESIDENT_FORM then the attribute contains a list of
  54. extent data (called MappingPairs) which describe a set of clusters on the volume that contain the
  55. attribute's information.
  56. There are four file types: Small, Large, Huge and Mega. These types refer to levels of fragmentation,
  57. not file size. These classifications refer to the complexity in accessing the file's data.
  58. A contiguous file of enormous size only requires location of the file's first cluster and total
  59. clusters in the file to access the file. A much smaller file that is heavily fragmented requires the
  60. same amount of information for each fragment. Once the level of fragmentation reaches the point that
  61. the methodology of the file's current type can no longer describe the file's data the file graduates
  62. to the next file type. Increasing the size of other attributes (thus reducing the amount of space
  63. available for the data information) can also cause a file to graduate to the next file type. Examples
  64. of increasing the size of other attributes are renaming the file with a longer filename or adding
  65. more security information.
  66. Mega File = A non-resident $ATTRIBUTE_LIST attribute in the FRS contains a mapping pairs set
  67. for the clusters that contain resident $ATTRIBUTE_LIST attributes
  68. Small files have a RESIDENT_FORM $DATA attribute containing the file's data.
  69. Large file's have a NONRESIDENT_FORM $DATA attribute containing MappingPairs that describe a set of
  70. clusters which contain the file's data.
  71. Huge files have a RESIDENT_FORM $ATTRIBUTE_LIST attribute which contains a list of secondary file
  72. records containing NONRESIDENT_FORM $DATA attributes.
  73. Mega files have a NONRESIDENT_FORM $ATTRIBUTE_LIST attribute containing MappingPairs that describe
  74. a set of clusters which contain RESIDENT_FORM $ATTRIBUTE_LIST attributes holding a list of secondary
  75. file records containing NONRESIDENT_FORM $DATA attributes.
  76. Once the MappingPairs in the NONRESIDENT_FORM $ATTRIBUTE_LIST attribute exceeds the capacity of the
  77. base filerecord the file system gives up.
  78. A file with a RESIDENT_FORM $DATA attribute is a Small type file.
  79. A file with a NONRESIDENT_FORM $DATA attribute is a Large type file.
  80. A file with a RESIDENT_FORM $ATTRIBUTE_LIST attribute is a Huge type file.
  81. A file with a NONRESIDENT_FORM $ATTRIBUTE_LIST attribute is a Mega type file.
  82. This routine locates all the MappingPairs for a file and converts them into an extent list.
  83. First the filerecord is scanned for a $STANDARD_INFORMATION attribute to determine if the file is
  84. compressed.
  85. Then the filerecord is scanned for an $ATTRIBUTE_LIST or $DATA attribute and looks at the FormCode
  86. of whichever attribute it finds to determine which file type the file is and calls the appropriate
  87. routine to build the extent list.
  88. Finally: It is possible for a single extent to be described with multiple MappingPairs making it
  89. appear to be more than one extent. Also, compressed files are compressed in blocks with a
  90. MappingPair describing each block which also makes it appear that the file has more extents than it
  91. actually does. CollapseExtentList consolidates adjacent extents and derives a true count of extents
  92. and fragments in the file. CollapseExtentList also calls GetLowestStartingLcn to determine the
  93. earliest cluster in the file for the purpose of processing the files on the disk in sequential order.
  94. */
  95. /*****************************************************************************************************************
  96. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  97. ROUTINE DESCRIPTION:
  98. Build an extent list for a file.
  99. If TypeCode != 0 then the file's filerecord should already be in VolData.pFileRecord.
  100. If TypeCode = 0 then GetExtentList will load the filerecord.
  101. INPUT + OUTPUT:
  102. IN TypeCode - which attribute in the file's filerecord to look in for the file's extent data. See also description above.
  103. IN pFrs - The FRS for the file go get the extent list of. Set to NULL if GetExtentList has to load the FRS from disk.
  104. GLOBALS:
  105. IN OUT VolData.pFileRecord - The file record for the file.
  106. IN OUT VolData.FileRecordNumber - The file record number to use.
  107. OUT VolData.hExtentList - HANDLE to buffer of EXTENT_LIST structure
  108. OUT VolData.pExtentList - file's extent list
  109. OUT VolData.NumberOfExtents - extents in the list
  110. OUT VolData.NumberOfFragments - fragments in the file
  111. OUT VolData.NumberOfClusters - clusters in the file
  112. OUT VolData.FileSize - bytes in the file
  113. OUT VolData.bFragmented - whether the file is contiguous or fragmented
  114. OUT VolData.bCompressed - whether the file is compressed or not
  115. OUT VolData.bDirectory - whether the file is a directoty or not
  116. OUT VolData.StartingLcn - earliest cluster in the file
  117. RETURN:
  118. TRUE = Success
  119. FALSE = Failure
  120. */
  121. BOOL
  122. GetExtentList(
  123. DWORD dwEnabledStreams,
  124. FILE_RECORD_SEGMENT_HEADER* pFrs
  125. )
  126. {
  127. ATTRIBUTE_RECORD_HEADER* pArh = NULL; //The specific attribute we're looking at now.
  128. ATTRIBUTE_LIST_ENTRY* pAle = NULL; //The attribute entry we're looking at now (only used for huge and mega files).
  129. ATTRIBUTE_LIST_ENTRY* pAleStart = NULL; //The start of the attribute list (only used for huge and mega files).
  130. HANDLE hAle = NULL; //Handle to memory for attribute list if it was nonresident and had to be read off the disk.
  131. LONGLONG SaveFrn = 0; //Used to compare the FRN before and after getting a new FRS to make sure we got the one we want.
  132. DWORD dwAttrListLength = 0; //The length of the attribute list in a huge or mega file.
  133. EXTENT_LIST_DATA ExtentData;
  134. UINT i;
  135. BOOL fRC;
  136. // Set up the Extent pointers structure to fill in the extent list in VolData.
  137. ZeroMemory(&ExtentData, sizeof(EXTENT_LIST_DATA));
  138. ExtentData.hExtents = VolData.hExtentList;
  139. ExtentData.pExtents = VolData.pExtentList;
  140. ExtentData.ExtentListAlloced = (DWORD)VolData.ExtentListAlloced;
  141. ExtentData.ExtentListSize = 0;
  142. ExtentData.dwEnabledStreams = dwEnabledStreams;
  143. ExtentData.BytesRead = 0;
  144. ExtentData.TotalClusters = 0;
  145. // Initialize the VolData fields.
  146. VolData.FileSize = 0;
  147. VolData.bFragmented = FALSE;
  148. VolData.bCompressed = FALSE;
  149. VolData.bDirectory = FALSE;
  150. VolData.bInBootExcludeZone = FALSE;
  151. VolData.bBootOptimiseFile = FALSE;
  152. VolData.NumberOfClusters = 0;
  153. VolData.NumberOfRealClusters = 0;
  154. VolData.NumberOfFragments = 0;
  155. // Get the FRS if it wasn't already loaded.
  156. if(!pFrs) {
  157. // Set the pFrs to point to a buffer to hold the FRS.
  158. pFrs = (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord; //A shortcut to reduce code + cycles when accessing the frs.
  159. // Save the FRN so we know if we get the wrong FRS later.
  160. SaveFrn = VolData.FileRecordNumber;
  161. //Read in the FRS for the attribute.
  162. // Get the next file record.
  163. EF(GetInUseFrs(VolData.hVolume, &VolData.FileRecordNumber, pFrs, (ULONG)VolData.BytesPerFRS));
  164. // Make sure we got the FRS we requested.
  165. EF_ASSERT(VolData.FileRecordNumber == SaveFrn);
  166. }
  167. // Detect if it is a directory and set the flag
  168. if (pFrs->Flags & FILE_FILE_NAME_INDEX_PRESENT) {
  169. VolData.bDirectory = TRUE;
  170. }
  171. else {
  172. VolData.bDirectory = FALSE;
  173. }
  174. // Find $STANDARD_INFORMATION attribute -- if there is none then don't use this FRS.
  175. if(!FindAttributeByType($STANDARD_INFORMATION, pFrs, &pArh, (ULONG)VolData.BytesPerFRS)) {
  176. return FALSE;
  177. }
  178. // Note if the file is compressed or not.
  179. VolData.bCompressed = (((STANDARD_INFORMATION*)((UCHAR*)pArh+pArh->Form.Resident.ValueOffset))->FileAttributes
  180. & FILE_ATTRIBUTE_COMPRESSED) ? TRUE : FALSE;
  181. // Initialize the FILE_EXTENT_HEADER structure.
  182. ExtentData.pFileExtentHeader = (FILE_EXTENT_HEADER*)ExtentData.pExtents;
  183. ExtentData.pFileExtentHeader->FileRecordNumber = VolData.FileRecordNumber;
  184. ExtentData.pFileExtentHeader->NumberOfStreams = 0; //Contains the number of streams with extents.
  185. ExtentData.pFileExtentHeader->TotalNumberOfStreams = 0; //Contains the total number of streams.
  186. ExtentData.pFileExtentHeader->ExcessExtents = 0;
  187. // Initialize the FILE_STREAM_HEADER structure.
  188. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((char*)VolData.pExtentList
  189. + sizeof(FILE_EXTENT_HEADER));
  190. ExtentData.pStreamExtentHeader->StreamNumber = 0;
  191. ExtentData.pStreamExtentHeader->ExtentCount = 0;
  192. ExtentData.pStreamExtentHeader->ExcessExtents = 0;
  193. ExtentData.pStreamExtentHeader->AllocatedLength = 0;
  194. ExtentData.pStreamExtentHeader->FileSize = 0;
  195. __try {
  196. // Get non-data stream extents.
  197. EF(GetNonDataStreamExtents());
  198. // Look for an $ATTRIBUTE_LIST
  199. if(!FindAttributeByType($ATTRIBUTE_LIST, pFrs, &pArh, (ULONG)VolData.BytesPerFRS)) {
  200. // If no $ATTRIBUTE_LIST was found, move to the first stream in the FRS.
  201. if(!FindStreamInFrs(pFrs, &pArh, &ExtentData)) {
  202. // If no stream was found, then there is no extent list for this file.
  203. VolData.TotalSmallDirs++;
  204. return TRUE;
  205. }
  206. do{
  207. // If this is a nonresident stream, then get its extents, and add it to the extent list.
  208. if(pArh->FormCode == NONRESIDENT_FORM) {
  209. // Set the data in the stream header since we've found a stream;
  210. ExtentData.pStreamExtentHeader->StreamNumber = ExtentData.pFileExtentHeader->TotalNumberOfStreams;
  211. ExtentData.pStreamExtentHeader->ExtentCount = 0;
  212. ExtentData.pStreamExtentHeader->ExcessExtents = 0;
  213. ExtentData.pStreamExtentHeader->AllocatedLength = 0;
  214. ExtentData.pStreamExtentHeader->FileSize = 0;
  215. // Load the stream extents from the attributes in this FRS.
  216. GetLargeStreamExtentList(pFrs, pArh, &ExtentData);
  217. // Reset this variable for the next stream.
  218. ExtentData.BytesRead = 0;
  219. // Keep track of the number of streams with extent lists.
  220. ExtentData.pFileExtentHeader->NumberOfStreams++;
  221. // Bump up the file stream header pointer to the beginning of
  222. // where the next stream will be if it's found.
  223. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)ExtentData.pStreamExtentHeader
  224. + sizeof(STREAM_EXTENT_HEADER)
  225. + ExtentData.pStreamExtentHeader->ExtentCount
  226. * sizeof(EXTENT_LIST));
  227. // Check to see if the extent list is going to overrun with the
  228. // addition of another stream header.
  229. if(((UCHAR*)ExtentData.pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER))
  230. > (ExtentData.pExtents + ExtentData.ExtentListAlloced)) {
  231. UCHAR* pLastExtentList = ExtentData.pExtents;
  232. // If so, realloc it larger, 64K at a time.
  233. EF(AllocateMemory(ExtentData.ExtentListAlloced + 0x10000,
  234. &ExtentData.hExtents,
  235. (void**)&ExtentData.pExtents));
  236. ExtentData.ExtentListAlloced += 0x10000;
  237. // Reset the VolData fields which are dependent on pExtentList.
  238. if(pLastExtentList != ExtentData.pExtents) {
  239. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)ExtentData.pStreamExtentHeader
  240. - pLastExtentList
  241. + ExtentData.pExtents);
  242. ExtentData.pFileExtentHeader = (FILE_EXTENT_HEADER*)((UCHAR*)ExtentData.pFileExtentHeader
  243. - pLastExtentList
  244. + ExtentData.pExtents);
  245. }
  246. }
  247. }
  248. // Keep track of the fact the stream exists so they can be numbered correctly.
  249. ExtentData.pFileExtentHeader->TotalNumberOfStreams++;
  250. // Go to the next stream.
  251. }
  252. while(FindNextStreamInFrs(pFrs, &pArh, &ExtentData));
  253. }
  254. //If an $ATTRIBUTE_LIST was found
  255. else {
  256. // If the $ATTRIBUTE_LIST is nonresident get the entire
  257. // attribute list from the disk before proceeding.
  258. if(pArh->FormCode == NONRESIDENT_FORM) {
  259. // Load the attribute extents from the disk.
  260. LoadExtentDataToMem(pArh, &hAle, &dwAttrListLength);
  261. // Get a pointer to the allocated memory.
  262. EF_ASSERT(pAleStart = (ATTRIBUTE_LIST_ENTRY*)GlobalLock(hAle));
  263. VolData.NumberOfClusters = 0;
  264. }
  265. // If it was a resident attribute list, then the length of
  266. // the attribute list can be determined from the attribute.
  267. else {
  268. pAleStart = (ATTRIBUTE_LIST_ENTRY*)(pArh->Form.Resident.ValueOffset + (UCHAR*)pArh);
  269. dwAttrListLength = pArh->Form.Resident.ValueLength;
  270. }
  271. // Start at the beginning of the attribute list.
  272. pAle = pAleStart;
  273. // Move to the first stream.
  274. if(!FindStreamInAttrList(pAleStart, &pAle, dwAttrListLength, &ExtentData)) {
  275. // If no stream was found, then there is no extent list for this file.
  276. return TRUE;
  277. }
  278. do {
  279. // If this is a nonresident stream, then get it's extents, and add it to the extent list.
  280. if(AttributeFormCode(pAle, &ExtentData) == NONRESIDENT_FORM) {
  281. // Set the data in the stream header since we've found a stream;
  282. ExtentData.pStreamExtentHeader->StreamNumber = ExtentData.pFileExtentHeader->TotalNumberOfStreams;
  283. ExtentData.pStreamExtentHeader->ExtentCount = 0;
  284. ExtentData.pStreamExtentHeader->ExcessExtents = 0;
  285. ExtentData.pStreamExtentHeader->AllocatedLength = 0;
  286. ExtentData.pStreamExtentHeader->FileSize = 0;
  287. // Load the stream extents from the attributes in the various FRS's.
  288. GetHugeStreamExtentList(pAleStart, &pAle, dwAttrListLength, &ExtentData);
  289. // Reset this variable for the next stream.
  290. ExtentData.BytesRead = 0;
  291. // Keep track of the number of streams with extent lists.
  292. ExtentData.pFileExtentHeader->NumberOfStreams++;
  293. // Bump up the file stream header pointer to the beginning of where
  294. // the next stream will be if it's found.
  295. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)ExtentData.pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER)
  296. + ExtentData.pStreamExtentHeader->ExtentCount
  297. * sizeof(EXTENT_LIST));
  298. // Check to see if the extent list is going to overrun with the addition of another stream header.
  299. if(((UCHAR*)ExtentData.pStreamExtentHeader+sizeof(STREAM_EXTENT_HEADER))
  300. > (ExtentData.pExtents + ExtentData.ExtentListAlloced)) {
  301. UCHAR* pLastExtentList = ExtentData.pExtents;
  302. // If so, realloc it larger, 64K at a time.
  303. EF(AllocateMemory(ExtentData.ExtentListAlloced + 0x10000,
  304. &ExtentData.hExtents,
  305. (void**)&ExtentData.pExtents));
  306. ExtentData.ExtentListAlloced += 0x10000;
  307. // Reset the VolData fields which are dependent on pExtentList.
  308. if(pLastExtentList != ExtentData.pExtents) {
  309. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)ExtentData.pStreamExtentHeader
  310. - pLastExtentList
  311. + ExtentData.pExtents);
  312. ExtentData.pFileExtentHeader = (FILE_EXTENT_HEADER*)((UCHAR*)ExtentData.pFileExtentHeader
  313. - pLastExtentList
  314. + ExtentData.pExtents);
  315. }
  316. }
  317. }
  318. // Keep track of the fact the stream exists
  319. // so they can be numbered correctly.
  320. ExtentData.pFileExtentHeader->TotalNumberOfStreams++;
  321. // Find the next stream.
  322. }
  323. while(FindNextStreamInAttrList(pAleStart, &pAle, dwAttrListLength, &ExtentData));
  324. }
  325. // Reset the stream extent header to the first stream.
  326. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)ExtentData.pFileExtentHeader
  327. + sizeof(FILE_EXTENT_HEADER));
  328. // Loop through the streams to get a pointer to the end of the extent list.
  329. for(i=0; i<ExtentData.pFileExtentHeader->NumberOfStreams; i++) {
  330. // Set the stream extent header pointer to the next stream
  331. // (current position + header + # extents * sizeof each extent).
  332. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)ExtentData.pStreamExtentHeader
  333. + sizeof(STREAM_EXTENT_HEADER)
  334. + ExtentData.pStreamExtentHeader->ExtentCount
  335. * sizeof(EXTENT_LIST));
  336. }
  337. // Since pStreamExtentHeader now points to just after the last stream,
  338. // we can use it to determine the size of the extent list.
  339. ExtentData.ExtentListSize = (DWORD)((UCHAR*)ExtentData.pStreamExtentHeader
  340. - (UCHAR*)ExtentData.pFileExtentHeader);
  341. // Concatenate adjacent extents and keep track of the number of fragments.
  342. EF(CollapseExtentList(&ExtentData));
  343. }
  344. __finally {
  345. // Count the total number of excess extents for the file in all the streams.
  346. // First set the excess extents to zero.
  347. ExtentData.pFileExtentHeader->ExcessExtents = 0;
  348. // Reset the stream extent header to the first stream.
  349. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)ExtentData.pFileExtentHeader
  350. + sizeof(FILE_EXTENT_HEADER));
  351. // Loop through the streams counting excess extents.
  352. for(i=0; i<ExtentData.pFileExtentHeader->NumberOfStreams; i++) {
  353. // Add this stream's excess extents (those after the the first extent)
  354. // to the total excess extents count.
  355. ExtentData.pFileExtentHeader->ExcessExtents += ExtentData.pStreamExtentHeader->ExcessExtents;
  356. // Set the stream extent header pointer to the next stream
  357. // (current position + header + # extents * sizeof each extent).
  358. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)ExtentData.pStreamExtentHeader
  359. + sizeof(STREAM_EXTENT_HEADER)
  360. + ExtentData.pStreamExtentHeader->ExtentCount
  361. * sizeof(EXTENT_LIST));
  362. }
  363. // Since pStreamExtentHeader now points to just after the last stream,
  364. // we can use it to determine the size of the extent list.
  365. ExtentData.ExtentListSize = (DWORD)((UCHAR*)ExtentData.pStreamExtentHeader
  366. - (UCHAR*)ExtentData.pFileExtentHeader);
  367. // Now that we have the full extent list, put it back into VolData.
  368. VolData.hExtentList = ExtentData.hExtents;
  369. VolData.pExtentList = ExtentData.pExtents;
  370. VolData.ExtentListAlloced = ExtentData.ExtentListAlloced;
  371. // Free memory.
  372. if (hAle) {
  373. EH_ASSERT(GlobalUnlock(hAle) == FALSE);
  374. EH_ASSERT(GlobalFree(hAle) == NULL);
  375. }
  376. // Note how much memory is actually used by the extent list.
  377. // First get a pointer to the end.
  378. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((char*)VolData.pExtentList
  379. + sizeof(FILE_EXTENT_HEADER));
  380. // As long as we've got a pointer to the first stream, get the file size
  381. // (which is the allocated length for this stream.) The file size is
  382. // considered to the the file size for only the first stream.
  383. VolData.FileSize = ExtentData.pStreamExtentHeader->FileSize;
  384. // Loop through the streams.
  385. fRC = TRUE;
  386. for(i=0; i<ExtentData.pFileExtentHeader->NumberOfStreams; i++) {
  387. // If we hit a stream with a header but no extents, ERROR.
  388. if (!(ExtentData.pStreamExtentHeader->ExtentCount)) {
  389. LOG_ERR();
  390. fRC = FALSE;
  391. goto EndFinally;
  392. }
  393. // Set the stream extent header pointer to the next stream
  394. // (current position + header + # extents * sizeof each extent).
  395. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)ExtentData.pStreamExtentHeader
  396. + sizeof(STREAM_EXTENT_HEADER)
  397. + ExtentData.pStreamExtentHeader->ExtentCount
  398. * sizeof(EXTENT_LIST));
  399. }
  400. // Now that pStreamExtentHeader points to just after the last stream,
  401. // we can use it's position to determine the size of the whole extent list.
  402. VolData.ExtentListSize = (UINT_PTR)ExtentData.pStreamExtentHeader - (UINT_PTR)ExtentData.pExtents;
  403. // Keep track of the number of clusters and real clusters in all streams of the file.
  404. VolData.NumberOfClusters = ExtentData.TotalClusters;
  405. VolData.NumberOfRealClusters = ExtentData.TotalRealClusters;
  406. // If there are any excess extents then this file is fragmented.
  407. if(ExtentData.pFileExtentHeader->ExcessExtents) {
  408. VolData.bFragmented = TRUE;
  409. }
  410. EndFinally:;
  411. }
  412. return fRC;
  413. }
  414. //Loops through the attributes in an FRS looking for a valid stream.
  415. //A valid stream is a $DATA or $INDEX_ALLOCATION attribute. $BITMAP is also valid if pExtentData->dwEnabledStreams is TRUE;
  416. BOOL
  417. FindStreamInFrs(
  418. IN PFILE_RECORD_SEGMENT_HEADER pFrs,
  419. OUT PATTRIBUTE_RECORD_HEADER* ppArh,
  420. EXTENT_LIST_DATA* pExtentData
  421. )
  422. {
  423. PATTRIBUTE_RECORD_HEADER pArh;
  424. //Check validitity of input parameters.
  425. EF_ASSERT(pFrs);
  426. EF_ASSERT(ppArh);
  427. //Initialize this to zero.
  428. *ppArh = 0;
  429. //Point to the first attribute in the FRS.
  430. pArh = (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + pFrs->FirstAttributeOffset);
  431. //Look for a $DATA, $INDEX_ALLOCATION, or $BITMAP attribute, as these are the valid stream types.
  432. // Don't go beyond the end of the FRS.
  433. // Look or $DATA, $INDEX_ALLOCATION, or $BITMAP.
  434. // Another check to not go beyond the end of the valid data in the FRS.
  435. while(pArh < (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + VolData.BytesPerFRS) &&
  436. pArh->TypeCode != $DATA &&
  437. pArh->TypeCode != $INDEX_ALLOCATION &&
  438. (pExtentData->dwEnabledStreams ? pArh->TypeCode != $BITMAP : TRUE) &&
  439. pArh->TypeCode != $END){
  440. //That wasn't a valid attribute. Go to the next.
  441. pArh = (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pArh + pArh->RecordLength);
  442. }
  443. //If no attribute was found, return FALSE since we didn't find a stream.
  444. if((pArh >= (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + VolData.BytesPerFRS))
  445. || (pArh->TypeCode != $DATA &&
  446. pArh->TypeCode != $INDEX_ALLOCATION &&
  447. (pExtentData->dwEnabledStreams ? pArh->TypeCode != $BITMAP : TRUE))
  448. ){
  449. return FALSE;
  450. }
  451. //If found, but not unnamed, report a corrupt disk.
  452. //The unnamed stream must always exist and must always precede the named streams,
  453. //hence we must find one if any streams are found since we proceed from the beginning of the FRS.
  454. //$INDEX_ALLOCATIONS may exist and may have any name (usually $I30 or $O).
  455. //Found an unnamed stream. Success!
  456. *ppArh = pArh;
  457. return TRUE;
  458. }
  459. //Finds the next valid stream in an FRS. This will continue the loop which FindStreamInFrs started until it finds another, or none.
  460. BOOL
  461. FindNextStreamInFrs(
  462. IN PFILE_RECORD_SEGMENT_HEADER pFrs,
  463. OUT PATTRIBUTE_RECORD_HEADER* ppArh,
  464. EXTENT_LIST_DATA* pExtentData
  465. )
  466. {
  467. PATTRIBUTE_RECORD_HEADER pArh;
  468. BOOL bNewStreamFound = FALSE;
  469. //Check validitity of input parameters.
  470. EF_ASSERT(pFrs);
  471. EF_ASSERT(ppArh);
  472. EF_ASSERT(*ppArh);
  473. EF_ASSERT(pExtentData);
  474. //Set the pointer to the next attribute from the one currently pointed to.
  475. pArh = (ATTRIBUTE_RECORD_HEADER*)((char*)*ppArh + (*ppArh)->RecordLength);
  476. do{
  477. //Look for a $DATA, $INDEX_ALLOCATION or $BITMAP attribute after the last one found
  478. // Don't go beyond the end of the FRS.
  479. // Look or $DATA, $INDEX_ALLOCATION, or $BITMAP.
  480. // Another check to not go beyond the end of the valid data in the FRS.
  481. while(pArh < (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + VolData.BytesPerFRS) &&
  482. pArh->TypeCode != $DATA &&
  483. pArh->TypeCode != $INDEX_ALLOCATION &&
  484. (pExtentData->dwEnabledStreams ? pArh->TypeCode != $BITMAP : TRUE) &&
  485. pArh->TypeCode != $END){
  486. //That wasn't a valid attribute. Go to the next.
  487. pArh = (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pArh + pArh->RecordLength);
  488. }
  489. //If no attribute was found, return FALSE since we didn't find a stream.
  490. if((pArh >= (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + VolData.BytesPerFRS)) || (pArh->TypeCode != $DATA && pArh->TypeCode != $INDEX_ALLOCATION && (pExtentData->dwEnabledStreams ? pArh->TypeCode != $BITMAP : TRUE))){
  491. return FALSE;
  492. }
  493. //Check to see if this attribute is part of the last attribute's stream.
  494. //First compare TypeCodes.
  495. if(pArh->TypeCode == ((PATTRIBUTE_RECORD_HEADER)*ppArh)->TypeCode){
  496. //Then compare the streamnames.
  497. // Check to see if both attribs are unnamed.
  498. // Check to see if both attribs are named and the same name.
  499. if((pArh->NameLength == 0 && (*ppArh)->NameLength == 0) ||
  500. ((pArh->NameLength == (*ppArh)->NameLength) && !memcmp((UCHAR*)pArh+pArh->NameOffset, (UCHAR*)(*ppArh)+(*ppArh)->NameOffset, pArh->NameLength*sizeof(WCHAR)))){
  501. //The two stream names matched, so this attribute is part of the same stream as the previous attribute. Keep looking for another attribute.
  502. continue;
  503. }
  504. }
  505. //Found an another stream. Success!
  506. bNewStreamFound = TRUE;
  507. }while(!bNewStreamFound);
  508. //Return a pointer to the stream.
  509. *ppArh = pArh;
  510. return TRUE;
  511. }
  512. //Given an non-resident attribute, AddMappingPointersToStream will extract the mapping pointers, convert then to EXTENT_LISTs and
  513. //append them to the extent list.
  514. BOOL
  515. AddMappingPointersToStream(
  516. IN PATTRIBUTE_RECORD_HEADER pArh,
  517. EXTENT_LIST_DATA* pExtentData
  518. )
  519. {
  520. PUCHAR pMappingPairs = 0;
  521. LONGLONG ExtentLengthFieldSize = 0;
  522. LONGLONG ExtentOffsetFieldSize = 0;
  523. LONGLONG ExtentLength = 0;
  524. LONGLONG ExtentOffset = 0;
  525. LONGLONG ThisExtentStart = 0;
  526. LONGLONG BytesReadMax = 0;
  527. LONGLONG BytesRead = 0;
  528. LONGLONG LastExtentEnd = 0;
  529. EXTENT_LIST* pExtentList = NULL;
  530. ULONG Extent;
  531. //Make sure we have a stream header to add extents to.
  532. EF_ASSERT(pArh);
  533. EF_ASSERT(pExtentData->pStreamExtentHeader);
  534. //Make sure this is a non resident attribute.
  535. EF_ASSERT(pArh->FormCode == NONRESIDENT_FORM);
  536. //Get an extent list pointer that points to the current end of the stream.
  537. pExtentList = (EXTENT_LIST*)((UCHAR*)pExtentData->pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pExtentData->pStreamExtentHeader->ExtentCount * sizeof(EXTENT_LIST));
  538. //Note the end of the last extent so that future contiguous extents won't be counted as separate extents.
  539. //If there are no extents in this stream yet, then de facto there is not an end to the previous extent.
  540. if(pExtentData->pStreamExtentHeader->ExtentCount == 0){
  541. LastExtentEnd = 0xFFFFFFFFFFFFFFFF;
  542. }
  543. else{
  544. LastExtentEnd = pExtentList[-1].StartingLcn + pExtentList[-1].ClusterCount;
  545. }
  546. //0.0E00 Get a pointer to the mapping pairs in this attribute.
  547. pMappingPairs = (PUCHAR)pArh + pArh->Form.Nonresident.MappingPairsOffset;
  548. //Note how many bytes are allocated in the stream for data.
  549. //Sometimes an extent list will contain garbage pointers at the end beyond the last allocated cluster, so don't add these to the extent list.
  550. BytesReadMax = pExtentData->pStreamExtentHeader->AllocatedLength;
  551. //Note how many bytes have already been read from this stream.
  552. BytesRead = pExtentData->BytesRead;
  553. //Loop through each mapping pair in the attribute. The last mapping pair is filled with zeros, if there are not enough extents to go to AllocatedLength.
  554. // Initialize ThisExtentStart.
  555. // Don't go beyond the number of bytes allocated to the stream and into invalid extent data.
  556. // Don't go beyond the end of the record.
  557. // Don't go past a zero extent which indicates an end of extent list -- sometimes. (Sometimes it's determined by alloced size, or end of frs.)
  558. // Go to the next extent.
  559. for( ThisExtentStart=0, Extent=0;
  560. (BytesRead<BytesReadMax) &&
  561. (pMappingPairs < (PUCHAR)((char*)pArh + pArh->RecordLength)) &&
  562. (*pMappingPairs!=0);
  563. Extent ++){
  564. //Check to see if the extent list is going to overrun with the addition of another extent.
  565. if((UCHAR*)&pExtentList[Extent+1] > (pExtentData->pExtents + pExtentData->ExtentListAlloced)){
  566. UCHAR* pLastExtentList = pExtentData->pExtents;
  567. //If so, realloc it larger, 64K at a time.
  568. EF(AllocateMemory(pExtentData->ExtentListAlloced + 0x10000, &pExtentData->hExtents, (void**)&pExtentData->pExtents));
  569. pExtentData->ExtentListAlloced += 0x10000;
  570. //Reset the fields which are dependent on pExtents.
  571. if(pLastExtentList != pExtentData->pExtents){
  572. pExtentData->pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pExtentData->pStreamExtentHeader - pLastExtentList + pExtentData->pExtents);
  573. pExtentData->pFileExtentHeader = (FILE_EXTENT_HEADER*)((UCHAR*)pExtentData->pFileExtentHeader - pLastExtentList + pExtentData->pExtents);
  574. pExtentList = (EXTENT_LIST*)((UCHAR*)pExtentList - pLastExtentList + pExtentData->pExtents);
  575. }
  576. }
  577. //First, get the size of the field that stores the length of the run of clusters,
  578. //Then, get the size of the field that stores the length of the offset field.
  579. //0.1E00 The lower 4 bits hold a number from 0-15 that specifies how many bytes the length field is.
  580. ExtentLengthFieldSize = *pMappingPairs & 0x0F;
  581. //0.1E00 The upper bits of the UCHAR hold a number that specifies how many bytes the offset field is.
  582. ExtentOffsetFieldSize = *(pMappingPairs++) / 16;
  583. //0.1E00 Check for virtual extents. (Extents indicating clusters not on disk because the file is compressed and doesn't need all the clusters on disk that it occupies in memory.)
  584. ExtentLength = (*(pMappingPairs + ExtentLengthFieldSize - 1) & 0x80) ? -1 : 0;
  585. EF_ASSERT(ExtentLengthFieldSize);
  586. //0.1E00 Get the length of this extent from the extent length field.
  587. CopyMemory(&ExtentLength, pMappingPairs, (DWORD)ExtentLengthFieldSize);
  588. pMappingPairs += ExtentLengthFieldSize;
  589. //0.1E00 If the offset field has a value -- i.e. it isn't a virtual extent, then copy it over.
  590. if(ExtentOffsetFieldSize != 0){
  591. //0.1E00 Check for virtual extents.
  592. ExtentOffset = (*(pMappingPairs + ExtentOffsetFieldSize - 1) & 0x80) ? -1 : 0;
  593. //0.1E00 Get the length of this extent from the extent length field.
  594. CopyMemory(&ExtentOffset, pMappingPairs, (DWORD)ExtentOffsetFieldSize);
  595. pMappingPairs += ExtentOffsetFieldSize;
  596. //0.1E00 Since the offset field holds an offset from the last extent, translate this into an absolute offset from the
  597. //beginning of the disk. The first extent has an absolute offset, and each subsequent extent has a relative offset.
  598. ThisExtentStart += ExtentOffset;
  599. //0.0E00 If this extent is not adjacent to last extent, bump fragment count.
  600. //If this is the first extent the fragment count will not be incremented because LastExtentEnd = 0xFFFFFFFFFFFFFFFF.
  601. if((LastExtentEnd != 0xFFFFFFFFFFFFFFFF) && (ThisExtentStart != LastExtentEnd)){
  602. VolData.NumberOfFragments ++;
  603. }
  604. //0.1E00 Keep track of the last cluster of this extent so we can know if the next extent is adjacent or not.
  605. LastExtentEnd = ThisExtentStart + ExtentLength;
  606. //0.0E00 Update extent list
  607. pExtentList[Extent].StartingLcn = ThisExtentStart;
  608. }
  609. //0.1E00 If this was a virtual extent, then fill the starting lcn field with F's
  610. else{
  611. pExtentList[Extent].StartingLcn = 0xFFFFFFFFFFFFFFFF;
  612. }
  613. //0.1E00 Copy in the cluster count we calculated earlier.
  614. pExtentList[Extent].ClusterCount = ExtentLength;
  615. //Note that we added an extent to the stream.
  616. pExtentData->pStreamExtentHeader->ExtentCount++;
  617. //Keep track of the number of bytes we've read.
  618. pExtentData->BytesRead += ExtentLength*VolData.BytesPerCluster;
  619. //Keep track of the total number of clusters in the file.
  620. pExtentData->TotalClusters += ExtentLength;
  621. //Keep track of the total number of real clusters in the file.
  622. if(pExtentList[Extent].StartingLcn != 0xFFFFFFFFFFFFFFFF){
  623. pExtentData->TotalRealClusters += ExtentLength;
  624. }
  625. }
  626. return TRUE;
  627. }
  628. //Given the first attribute for a valid stream, GetLargeStreamExtentList will get the extent list for the stream.
  629. //This will also include other attributes that immediately follow which are part of the same stream.
  630. BOOL
  631. GetLargeStreamExtentList(
  632. IN PFILE_RECORD_SEGMENT_HEADER pFrs,
  633. IN PATTRIBUTE_RECORD_HEADER pArh,
  634. EXTENT_LIST_DATA* pExtentData
  635. )
  636. {
  637. PATTRIBUTE_RECORD_HEADER pPreviousArh = NULL;
  638. //check validity of input,
  639. EF_ASSERT(pFrs);
  640. EF_ASSERT(pArh);
  641. //Check the validity of the attribute passed in.
  642. EF_ASSERT(pArh->TypeCode == $DATA || pArh->TypeCode == $INDEX_ALLOCATION || (pExtentData->dwEnabledStreams ? pArh->TypeCode == $BITMAP : TRUE));
  643. //Note the number of bytes allocated to the stream so we don't overwrite it.
  644. pExtentData->pStreamExtentHeader->AllocatedLength = pArh->Form.Nonresident.AllocatedLength;
  645. //Note the file size for the stream for statistics for the user.
  646. pExtentData->pStreamExtentHeader->FileSize = pArh->Form.Nonresident.FileSize;
  647. while(TRUE){
  648. //Get the attribute's extent list.
  649. EF(AddMappingPointersToStream(pArh, pExtentData));
  650. //See if the next attribute is part of the same stream, and if so, count its extents too.
  651. //Remember which attribute this is.
  652. pPreviousArh = pArh;
  653. //Set the pointer to the next attribute from the one currently pointed to.
  654. pArh = (ATTRIBUTE_RECORD_HEADER*)((char*)pArh + pArh->RecordLength);
  655. //Look at the next attribute after the last one found and see if it is the same type.
  656. // Don't go beyond the end of the FRS.
  657. // Look for an attribute of the same type as the last.
  658. // Another check to not go beyond the end of the valid data in the FRS.
  659. if(pArh < (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + VolData.BytesPerFRS) &&
  660. pArh->TypeCode == pPreviousArh->TypeCode &&
  661. pArh->TypeCode != $END){
  662. //We found another attribute of the same type. Now compare the names to see if they're the same stream.
  663. //If both attributes have name lengths of zero they are both part of the unnamed stream.
  664. if(pArh->NameLength == 0 && pPreviousArh == 0){
  665. continue;
  666. }
  667. //If both attribs have the same name length, and the names match, they are part of the same named stream.
  668. if(pArh->NameLength == pPreviousArh->NameLength && !memcmp((char*)pArh+pArh->NameOffset, (char*)pPreviousArh+pPreviousArh->NameOffset, pArh->NameLength*sizeof(WCHAR))){
  669. continue;
  670. }
  671. }
  672. //The next attribute is not part of the same stream, so we're done.
  673. break;
  674. }
  675. return TRUE;
  676. }
  677. //Identical to FindStreamInFrs only it is fed an $ATTRIBUTE_LIST attribute instead of an FRS.
  678. BOOL
  679. FindStreamInAttrList(
  680. ATTRIBUTE_LIST_ENTRY* pAleStart,
  681. ATTRIBUTE_LIST_ENTRY** ppAle,
  682. LONGLONG ValueLength,
  683. EXTENT_LIST_DATA* pExtentData
  684. )
  685. {
  686. UCHAR* pAleEnd = NULL;
  687. ATTRIBUTE_LIST_ENTRY* pAle = NULL;
  688. //Check validitity of input parameters.
  689. EF_ASSERT(pAleStart);
  690. EF_ASSERT(ppAle);
  691. EF_ASSERT(ValueLength);
  692. EF_ASSERT(pExtentData);
  693. //Initialize this to zero.
  694. *ppAle = 0;
  695. //Initialize this to the start of the attribute list.
  696. pAle = pAleStart;
  697. //Note the end of the attribute list.
  698. pAleEnd = (UCHAR*)pAleStart + ValueLength;
  699. //Look for a $DATA, $INDEX_ALLOCATION, or $BITMAP attribute, as these are the valid stream types.
  700. // Don't go beyond the end of the attribute list.
  701. // Look for $DATA, $INDEX_ALLOCATION or $BITMAP
  702. while((UCHAR*)pAle < pAleEnd &&
  703. pAle->AttributeTypeCode != $DATA &&
  704. pAle->AttributeTypeCode != $INDEX_ALLOCATION &&
  705. pAle->AttributeTypeCode != $UNUSED &&
  706. (pExtentData->dwEnabledStreams ? pAle->AttributeTypeCode != $BITMAP : TRUE)){
  707. if (pAle->RecordLength == 0) {
  708. // This better have a Record Length > 0; else we're going to loop
  709. // around indefinitely
  710. VolData.bMFTCorrupt = TRUE;
  711. return FALSE;
  712. }
  713. pAle = (ATTRIBUTE_LIST_ENTRY*)((UCHAR*)pAle + pAle->RecordLength);
  714. }
  715. //If no attribute was found, return FALSE since we didn't find a stream.
  716. if(((UCHAR*)pAle >= pAleEnd) || (pAle->AttributeTypeCode == $UNUSED)){
  717. return FALSE;
  718. }
  719. //If found, but not unnamed, report a corrupt disk.
  720. //The unnamed stream must always exist and must always precede the named streams,
  721. //hence we must find one if any streams are found since we proceed from the beginning of the FRS.
  722. //$INDEX_ALLOCATIONS may exist and may have any name (usually $I30 or $O).
  723. //Found an unnamed stream. Success!
  724. *ppAle = pAle;
  725. return TRUE;
  726. }
  727. //Same as FindNextStreamInFrs only it is fed an $ATTRIBUTE_LIST attribute instead of an FRS.
  728. BOOL
  729. FindNextStreamInAttrList(
  730. ATTRIBUTE_LIST_ENTRY* pAleStart,
  731. ATTRIBUTE_LIST_ENTRY** ppAle,
  732. LONGLONG ValueLength,
  733. EXTENT_LIST_DATA* pExtentData
  734. )
  735. {
  736. UCHAR* pAleEnd = NULL;
  737. ATTRIBUTE_LIST_ENTRY* pAle = NULL;
  738. BOOL bNewStreamFound = FALSE;
  739. //Check validitity of input parameters.
  740. EF_ASSERT(pAleStart);
  741. EF_ASSERT(ppAle);
  742. EF_ASSERT(*ppAle);
  743. EF_ASSERT(ValueLength);
  744. EF_ASSERT(pExtentData);
  745. //Initialize this to the attribute after this stream in the attribute list.
  746. pAle = (ATTRIBUTE_LIST_ENTRY*)((UCHAR*)(*ppAle) + (*ppAle)->RecordLength);
  747. //Note the end of the attribute list.
  748. pAleEnd = (UCHAR*)pAleStart + ValueLength;
  749. do{
  750. //Look for a $DATA, $INDEX_ALLOCATION, or $BITMAP attribute, as these are the valid stream types.
  751. // Don't go beyond the end of the attribute list.
  752. // Look or $DATA, $INDEX_ALLOCATION, or $BITMAP
  753. while((UCHAR*)pAle < pAleEnd &&
  754. pAle->AttributeTypeCode != $DATA &&
  755. pAle->AttributeTypeCode != $INDEX_ALLOCATION &&
  756. pAle->AttributeTypeCode != $UNUSED &&
  757. (pExtentData->dwEnabledStreams ? pAle->AttributeTypeCode != $BITMAP : TRUE)){
  758. pAle = (ATTRIBUTE_LIST_ENTRY*)((UCHAR*)pAle + pAle->RecordLength);
  759. }
  760. //If no attribute was found, return FALSE since we didn't find a stream.
  761. if(((UCHAR*)pAle >= pAleEnd) || (pAle->AttributeTypeCode == $UNUSED)){
  762. return FALSE;
  763. }
  764. //Check to see if this attribute is part of the last attribute's stream.
  765. //First compare TypeCodes.
  766. if(pAle->AttributeTypeCode == (*ppAle)->AttributeTypeCode){
  767. //Then compare the streamnames.
  768. // Check to see if both attribs are unnamed.
  769. // Check to see if both attribs are named and the same name.
  770. if((pAle->AttributeNameLength == 0 && (*ppAle)->AttributeNameLength == 0) ||
  771. ((pAle->AttributeNameLength == (*ppAle)->AttributeNameLength) && !memcmp((UCHAR*)pAle+pAle->AttributeNameOffset, (UCHAR*)(*ppAle)+(*ppAle)->AttributeNameOffset, pAle->AttributeNameLength*sizeof(WCHAR)))){
  772. //The two stream names matched, so this attribute is part of the same stream as the previous attribute. Keep looking for another attribute.
  773. pAle = (ATTRIBUTE_LIST_ENTRY*)((UCHAR*)pAle + pAle->RecordLength);
  774. continue;
  775. }
  776. }
  777. //Found an another stream. Success!
  778. bNewStreamFound = TRUE;
  779. }while(!bNewStreamFound);
  780. //Return a pointer to the stream.
  781. (*ppAle) = pAle;
  782. return TRUE;
  783. }
  784. //Will get the actual attribute out of an FRS when given the pointer to it from an $ATTRIBUTE_LIST.
  785. ATTRIBUTE_RECORD_HEADER*
  786. FindAttributeByInstanceNumber(
  787. HANDLE* phFrs,
  788. ATTRIBUTE_LIST_ENTRY* pAle,
  789. EXTENT_LIST_DATA* pExtentData
  790. )
  791. {
  792. HANDLE hFrs = NULL;
  793. FILE_RECORD_SEGMENT_HEADER* pFrs = NULL;
  794. LONGLONG FileRecordNumber = 0;
  795. LONGLONG FileRecordNumberStore = 0;
  796. PATTRIBUTE_RECORD_HEADER pArh = NULL;
  797. EF_ASSERT(phFrs);
  798. EF_ASSERT(pAle);
  799. #ifdef OFFLINEDK
  800. //0.0E00 Get a pointer to the MFT bitmap
  801. UCHAR* pMftBitmap = (UCHAR*) GlobalLock(VolData.hMftBitmap);
  802. if (pMftBitmap) {
  803. BOOL isLocked = GlobalUnlock(VolData.hMftBitmap);
  804. EH(!isLocked);
  805. }
  806. #endif
  807. __try {
  808. // If a buffer for the FRS isn't already loaded, load it.
  809. if(!*phFrs){
  810. // Allocate a buffer to hold the file records, one at a time. (this locks the memory)
  811. if (!AllocateMemory((DWORD)(VolData.BytesPerFRS + VolData.BytesPerSector), phFrs, (void**)&pFrs)){
  812. EH(FALSE);
  813. __leave;
  814. }
  815. }
  816. else{
  817. // memory already exists, so lock it
  818. pFrs = (FILE_RECORD_SEGMENT_HEADER*) GlobalLock(*phFrs);
  819. if (!pFrs){
  820. EH_ASSERT(FALSE);
  821. __leave;
  822. }
  823. }
  824. //The file record number = the segment ref low part bitwise anded with the high part shifted left as many bits as the low part.
  825. FileRecordNumber = (pAle->SegmentReference.LowPart | (pAle->SegmentReference.HighPart << (sizeof(ULONG)*8)));
  826. FileRecordNumberStore = FileRecordNumber;
  827. //Read in the FRS for the attribute.
  828. #ifdef OFFLINEDK
  829. //0.1E00 Get an FRS.
  830. if (!GetFrs(&FileRecordNumber, VolData.pMftExtentList, pMftBitmap, VolData.pMftBuffer, pFrs)){
  831. EH(FALSE);
  832. __leave;
  833. }
  834. #else
  835. //0.1E00 Get the next file record. (does not lock the memory)
  836. if (!GetInUseFrs(VolData.hVolume, &FileRecordNumber, pFrs, (ULONG)VolData.BytesPerFRS)){
  837. EH(FALSE);
  838. __leave;
  839. }
  840. #endif
  841. //Make sure we got the FRS we requested.
  842. if (FileRecordNumber != FileRecordNumberStore){
  843. EH_ASSERT(FALSE);
  844. __leave;
  845. }
  846. //Find the attribute indicated by the attribute list entry.
  847. //Set pArh to the first attribute in the FRS.
  848. pArh = (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + pFrs->FirstAttributeOffset);
  849. // Look for a $DATA, $INDEX_ALLOCATION, or $BITMAP attribute, as these are the valid stream types.
  850. // Don't go beyond the end of the FRS.
  851. // Check for a match of the instance numbers.
  852. // Another check to not go beyond the end of the valid data in the FRS.
  853. while(pArh < (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + VolData.BytesPerFRS) &&
  854. pArh->Instance != pAle->Instance &&
  855. pArh->TypeCode != $END){
  856. //That wasn't a the attribute. Go to the next.
  857. pArh = (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pArh + pArh->RecordLength);
  858. }
  859. //If the attribute wasn't found, return NULL since we didn't find a stream.
  860. if((pArh >= (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + VolData.BytesPerFRS)) || (pArh->Instance != pAle->Instance) || (pArh->TypeCode == $END)){
  861. pArh = NULL;
  862. __leave;
  863. }
  864. //Error if the attribute doesn't match what was expected.
  865. if (pArh->TypeCode != pAle->AttributeTypeCode){
  866. EH_ASSERT(FALSE);
  867. __leave;
  868. }
  869. //Make sure the form code field is valid.
  870. if (pArh->FormCode != RESIDENT_FORM && pArh->FormCode != NONRESIDENT_FORM){
  871. EH_ASSERT(FALSE);
  872. __leave;
  873. }
  874. }
  875. __finally {
  876. // unlock the memory (it will be freed later)
  877. if(*phFrs){
  878. BOOL isLocked = GlobalUnlock(*phFrs);
  879. EH(!isLocked);
  880. }
  881. }
  882. //Return the attribute.
  883. return pArh;
  884. }
  885. //Given an ATTRIBUTE_LIST_ENTRY, will find out the form code of the attribute
  886. //(which requires reading the actual attribute out of the FRS, since that data is not stored in the ATTRIBUTE_LIST_ENTRY.)
  887. UCHAR
  888. AttributeFormCode(
  889. ATTRIBUTE_LIST_ENTRY* pAle,
  890. EXTENT_LIST_DATA* pExtentData
  891. )
  892. {
  893. HANDLE hFrs = NULL;
  894. // FILE_RECORD_SEGMENT_HEADER* pFrs = NULL;
  895. ATTRIBUTE_RECORD_HEADER* pArh = NULL; //The specific attribute we're looking at now.
  896. UCHAR uFormCode = 0; // assumes an error condition
  897. EF_ASSERT(pAle);
  898. EF_ASSERT(pExtentData);
  899. __try {
  900. // Get the attribute.
  901. //EF(pArh = FindAttributeByInstanceNumber(&hFrs, pAle, pExtentData));
  902. pArh = FindAttributeByInstanceNumber(&hFrs, pAle, pExtentData);
  903. if (!pArh){
  904. EH(FALSE);
  905. __leave;
  906. }
  907. // Error if the attribute doesn't match what was expected.
  908. //EF_ASSERT(pArh->TypeCode == pAle->AttributeTypeCode);
  909. if (pArh->TypeCode != pAle->AttributeTypeCode){
  910. EH(FALSE);
  911. __leave;
  912. }
  913. // Make sure the form code field is valid.
  914. //EF_ASSERT(pArh->FormCode == RESIDENT_FORM || pArh->FormCode == NONRESIDENT_FORM);
  915. if (pArh->FormCode != RESIDENT_FORM && pArh->FormCode != NONRESIDENT_FORM){
  916. EH(FALSE);
  917. __leave;
  918. }
  919. // Return its FormCode field.
  920. uFormCode = pArh->FormCode;
  921. }
  922. __finally {
  923. if(hFrs){
  924. // it is already unlocked EH_ASSERT(GlobalUnlock(hFrs) == FALSE);
  925. EH_ASSERT(GlobalFree(hFrs) == NULL);
  926. }
  927. }
  928. return uFormCode;
  929. }
  930. //Given an ATTRIBUTE_LIST_ENTRY, will find out the AllocatedLength of the attribute
  931. //(which requires reading the actual attribute out of the FRS, since that data is not stored in the ATTRIBUTE_LIST_ENTRY.)
  932. LONGLONG
  933. AttributeAllocatedLength(
  934. ATTRIBUTE_LIST_ENTRY* pAle,
  935. EXTENT_LIST_DATA* pExtentData
  936. )
  937. {
  938. HANDLE hFrs = NULL;
  939. // FILE_RECORD_SEGMENT_HEADER* pFrs = NULL;
  940. ATTRIBUTE_RECORD_HEADER* pArh = NULL; //The specific attribute we're looking at now.
  941. LONGLONG lReturnValue = (LONGLONG) -1;
  942. EM_ASSERT(pAle);
  943. EM_ASSERT(pExtentData);
  944. __try {
  945. //Get the attribute.
  946. //EM(pArh = FindAttributeByInstanceNumber(&hFrs, pAle, pExtentData));
  947. pArh = FindAttributeByInstanceNumber(&hFrs, pAle, pExtentData);
  948. if (!pArh){
  949. EH(FALSE);
  950. __leave;
  951. }
  952. //Error if the attribute doesn't match what was expected.
  953. //EM_ASSERT(pArh->TypeCode == pAle->AttributeTypeCode);
  954. if (pArh->TypeCode != pAle->AttributeTypeCode){
  955. EH(FALSE);
  956. __leave;
  957. }
  958. //Make sure the form code field is valid.
  959. EM_ASSERT(pArh->FormCode == NONRESIDENT_FORM);
  960. if (pArh->FormCode != NONRESIDENT_FORM){
  961. EH(FALSE);
  962. __leave;
  963. }
  964. // Return its AllocatedLength field.
  965. lReturnValue = pArh->Form.Nonresident.AllocatedLength;
  966. }
  967. __finally {
  968. if(hFrs){
  969. // it is already unlocked EH_ASSERT(GlobalUnlock(hFrs) == FALSE);
  970. EH_ASSERT(GlobalFree(hFrs) == NULL);
  971. }
  972. }
  973. // Return its AllocatedLength field.
  974. return lReturnValue;
  975. }
  976. //Given an ATTRIBUTE_LIST_ENTRY, will find out the FileSize field in the attribute
  977. //(which requires reading the actual attribute out of the FRS, since that data is not stored in the ATTRIBUTE_LIST_ENTRY.)
  978. LONGLONG
  979. AttributeFileSize(
  980. ATTRIBUTE_LIST_ENTRY* pAle,
  981. EXTENT_LIST_DATA* pExtentData
  982. )
  983. {
  984. HANDLE hFrs = NULL;
  985. // FILE_RECORD_SEGMENT_HEADER* pFrs = NULL;
  986. ATTRIBUTE_RECORD_HEADER* pArh = NULL; //The specific attribute we're looking at now.
  987. LONGLONG lReturnValue = -1;
  988. EM_ASSERT(pAle);
  989. EM_ASSERT(pExtentData);
  990. __try {
  991. //Get the attribute.
  992. //EM(pArh = FindAttributeByInstanceNumber(&hFrs, pAle, pExtentData));
  993. pArh = FindAttributeByInstanceNumber(&hFrs, pAle, pExtentData);
  994. if (!pArh){
  995. EH(FALSE);
  996. __leave;
  997. }
  998. //Error if the attribute doesn't match what was expected.
  999. //EM_ASSERT(pArh->TypeCode == pAle->AttributeTypeCode);
  1000. if (pArh->TypeCode != pAle->AttributeTypeCode){
  1001. EH(FALSE);
  1002. __leave;
  1003. }
  1004. //Make sure the form code field is valid.
  1005. //EM_ASSERT(pArh->FormCode == NONRESIDENT_FORM);
  1006. if (pArh->FormCode != NONRESIDENT_FORM){
  1007. EH(FALSE);
  1008. __leave;
  1009. }
  1010. //Return its FileSize field.
  1011. lReturnValue = pArh->Form.Nonresident.FileSize;
  1012. }
  1013. __finally {
  1014. if(hFrs){
  1015. // it is already unlocked EH_ASSERT(GlobalUnlock(hFrs) == FALSE);
  1016. EH_ASSERT(GlobalFree(hFrs) == NULL);
  1017. }
  1018. }
  1019. // Return its FileSize field.
  1020. return lReturnValue;
  1021. }
  1022. //Given an ATTRIBUTE_LIST_ENTRY pointing to the primary attribute in a stream, GetHugeStreamExtentList will build the extent list
  1023. //by querying all attributes in all FRS's in order that are a part of the stream.
  1024. BOOL
  1025. GetHugeStreamExtentList(
  1026. ATTRIBUTE_LIST_ENTRY* pAleStart,
  1027. ATTRIBUTE_LIST_ENTRY** ppAle,
  1028. LONGLONG ValueLength,
  1029. EXTENT_LIST_DATA* pExtentData
  1030. )
  1031. {
  1032. UCHAR* pAleEnd = NULL;
  1033. ATTRIBUTE_LIST_ENTRY* pAle = NULL;
  1034. HANDLE hFrs = NULL;
  1035. ATTRIBUTE_RECORD_HEADER* pArh = NULL;
  1036. BOOL bReturnValue = FALSE;
  1037. //Check validitity of input parameters.
  1038. EF_ASSERT(pAleStart);
  1039. EF_ASSERT(ppAle);
  1040. EF_ASSERT(*ppAle);
  1041. EF_ASSERT(ValueLength);
  1042. EF_ASSERT(pExtentData);
  1043. //Initialize this to the start of the stream in the attribute list.
  1044. pAle = *ppAle;
  1045. //Note the end of the attribute list.
  1046. pAleEnd = (UCHAR*)pAleStart + ValueLength;
  1047. //Get the type of this attribute and make sure it's a valid stream beginning.
  1048. EF_ASSERT(pAle->AttributeTypeCode == $DATA || pAle->AttributeTypeCode == $INDEX_ALLOCATION || (pExtentData->dwEnabledStreams ? pAle->AttributeTypeCode == $BITMAP : TRUE));
  1049. //Note the number of bytes allocated to the stream so we don't overwrite it.
  1050. //EF((pExtentData->pStreamExtentHeader->AllocatedLength = AttributeAllocatedLength(pAle, pExtentData)) != -1);
  1051. pExtentData->pStreamExtentHeader->AllocatedLength = AttributeAllocatedLength(pAle, pExtentData);
  1052. EF(pExtentData->pStreamExtentHeader->AllocatedLength != -1);
  1053. //Note the file size for user statistics.
  1054. //EF((pExtentData->pStreamExtentHeader->FileSize = AttributeFileSize(pAle, pExtentData)) != -1);
  1055. pExtentData->pStreamExtentHeader->FileSize = AttributeFileSize(pAle, pExtentData);
  1056. EF(pExtentData->pStreamExtentHeader->FileSize != -1);
  1057. __try {
  1058. //Go through the attribute list and the extents from all the attributes that belong to this stream in order.
  1059. do{
  1060. //Look for a $DATA, $INDEX_ALLOCATION, or $BITMAP attribute, as these are the valid stream types.
  1061. // Don't go beyond the end of the attribute list.
  1062. // Look or $DATA, $INDEX_ALLOCATION, or $BITMAP
  1063. while((UCHAR*)pAle < pAleEnd &&
  1064. pAle->AttributeTypeCode != $DATA &&
  1065. pAle->AttributeTypeCode != $INDEX_ALLOCATION &&
  1066. pAle->AttributeTypeCode != $UNUSED &&
  1067. (pExtentData->dwEnabledStreams ? pAle->AttributeTypeCode != $BITMAP : TRUE)
  1068. ){
  1069. pAle = (ATTRIBUTE_LIST_ENTRY*)((UCHAR*)pAle + pAle->RecordLength);
  1070. }
  1071. //If no attribute was found, break out of the loop.
  1072. if(((UCHAR*)pAle >= pAleEnd) || (pAle->AttributeTypeCode == $UNUSED)){
  1073. break;
  1074. }
  1075. //Check to see if this attribute is part of the stream.
  1076. //First compare TypeCodes.
  1077. if(pAle->AttributeTypeCode == (*ppAle)->AttributeTypeCode){
  1078. //Then compare the streamnames.
  1079. //Check to see if both attribs are unnamed.
  1080. //Check to see if both attribs are named and the same name.
  1081. if((pAle->AttributeNameLength == 0 && (*ppAle)->AttributeNameLength == 0) ||
  1082. (pAle->AttributeNameLength == (*ppAle)->AttributeNameLength && !memcmp((UCHAR*)pAle+pAle->AttributeNameOffset, (UCHAR*)(*ppAle)+(*ppAle)->AttributeNameOffset, pAle->AttributeNameLength*sizeof(WCHAR)))){
  1083. //The two stream names matched, so this attribute is part of the same stream as the previous attribute.
  1084. //Get the attribute from its FRS.
  1085. //EF_ASSERT(pArh = FindAttributeByInstanceNumber(&hFrs, pAle, pExtentData));
  1086. pArh = FindAttributeByInstanceNumber(&hFrs, pAle, pExtentData);
  1087. if (!pArh){
  1088. EH_ASSERT(FALSE);
  1089. __leave;
  1090. }
  1091. //Add this FRS and attribute to the stream's attribute list.
  1092. //EF(AddMappingPointersToStream(pArh, pExtentData));
  1093. if (!AddMappingPointersToStream(pArh, pExtentData)){
  1094. EH(FALSE);
  1095. __leave;
  1096. }
  1097. }
  1098. }
  1099. //Go to the next attribute.
  1100. pAle = (ATTRIBUTE_LIST_ENTRY*)((UCHAR*)pAle + pAle->RecordLength);
  1101. //Loop until no more attributes in the stream are found.
  1102. } while(TRUE);
  1103. //Success.
  1104. bReturnValue = TRUE;
  1105. }
  1106. __finally {
  1107. if(hFrs){
  1108. // it is already unlocked EH_ASSERT(GlobalUnlock(hFrs) == FALSE);
  1109. EH_ASSERT(GlobalFree(hFrs) == NULL);
  1110. }
  1111. }
  1112. return bReturnValue;
  1113. }
  1114. //Given an non-resident attribute, LoadExtentDataToMem will get the extent list for the attribute, and read
  1115. //the extents themselves contiguously into memory.
  1116. BOOL
  1117. LoadExtentDataToMem(
  1118. ATTRIBUTE_RECORD_HEADER* pArh,
  1119. HANDLE* phAle,
  1120. DWORD* pdwByteLen
  1121. )
  1122. {
  1123. DWORD i;
  1124. LONGLONG NumClusters;
  1125. EXTENT_LIST_DATA ExtentData;
  1126. EXTENT_LIST* pExtents;
  1127. UCHAR* pAle = NULL;
  1128. BOOL bReturn = FALSE; // assume an error
  1129. //Zero out our local ExtentData structure.
  1130. ZeroMemory(&ExtentData, sizeof(EXTENT_LIST_DATA));
  1131. //Make sure the inputs match. Either hAle was already alloced, or it wasn't.
  1132. EF_ASSERT((*phAle && *pdwByteLen) || (!*phAle && !*pdwByteLen));
  1133. //Allocate some memory for the extent list. 64K should do the trick.
  1134. ExtentData.ExtentListAlloced = 0x10000;
  1135. EF(AllocateMemory(ExtentData.ExtentListAlloced, &ExtentData.hExtents, (void**)&ExtentData.pExtents));
  1136. __try{
  1137. //There will be no FILE_EXTENT_HEADER for this extent list since we're just getting a single set of extents which might be data or non-data. (Only need a stream header, either way.)
  1138. ExtentData.pFileExtentHeader = NULL;
  1139. //Initialize the FILE_STREAM_HEADER structure.
  1140. ExtentData.pStreamExtentHeader = (STREAM_EXTENT_HEADER*)ExtentData.pExtents;
  1141. ExtentData.pStreamExtentHeader->StreamNumber = 0;
  1142. ExtentData.pStreamExtentHeader->ExtentCount = 0;
  1143. ExtentData.pStreamExtentHeader->ExcessExtents = 0;
  1144. ExtentData.pStreamExtentHeader->AllocatedLength = 0;
  1145. ExtentData.pStreamExtentHeader->FileSize = 0;
  1146. //Note the number of bytes allocated to the stream so we don't overwrite it.
  1147. ExtentData.pStreamExtentHeader->AllocatedLength = pArh->Form.Nonresident.AllocatedLength;
  1148. ExtentData.BytesRead = 0;
  1149. //Note the file size for the stream for statistics data for the user.
  1150. ExtentData.pStreamExtentHeader->FileSize = pArh->Form.Nonresident.FileSize;
  1151. //This returns the size of the data.
  1152. *pdwByteLen = (DWORD)ExtentData.pStreamExtentHeader->FileSize;
  1153. //Get the extents of the non-res attribute to read to memory.
  1154. if (!AddMappingPointersToStream(pArh, &ExtentData)){
  1155. EH(FALSE);
  1156. __leave;
  1157. }
  1158. //Get a pointer to the extents themselves.
  1159. pExtents = (EXTENT_LIST*)((UCHAR*)ExtentData.pExtents + sizeof(STREAM_EXTENT_HEADER));
  1160. //Count the number of clusters in all the extents so we know how much mem to alloc for the attribute list.
  1161. for(i=0, NumClusters=0; i<ExtentData.pStreamExtentHeader->ExtentCount; i++){
  1162. NumClusters += pExtents[i].ClusterCount;
  1163. }
  1164. //If there wasn't already memory allocated by the calling function, or if it was a different size, allocate/reallocate the memory to the correct size.
  1165. if(*pdwByteLen != (DWORD)(NumClusters * VolData.BytesPerCluster)){
  1166. //0.0E00 Allocate a buffer for the next extent
  1167. if (!AllocateMemory((DWORD)((NumClusters * VolData.BytesPerCluster) + VolData.BytesPerSector),
  1168. phAle,
  1169. (void**)&pAle)){
  1170. EH(FALSE);
  1171. __leave;
  1172. }
  1173. }
  1174. //bug #181935
  1175. //added 8/22/2000 by Scott Sipe, fix that was added some time ago in Glendale
  1176. //by John Joesph to fix the problem where pAle is NULL sometimes
  1177. //If there still isn't an I/O buffer, allocate one the correct size.
  1178. if(pAle == NULL){
  1179. //0.0E00 Allocate a buffer for the next extent
  1180. if (!AllocateMemory((DWORD)((NumClusters * VolData.BytesPerCluster) + VolData.BytesPerSector),
  1181. phAle,
  1182. (void**)&pAle)){
  1183. EH(FALSE);
  1184. __leave;
  1185. }
  1186. }
  1187. //Read in each of those extents.
  1188. for(i=0; i<ExtentData.pStreamExtentHeader->ExtentCount; i++){
  1189. //0.0E00 Read the next extent
  1190. if (!DasdReadClusters(VolData.hVolume,
  1191. pExtents[i].StartingLcn,
  1192. pExtents[i].ClusterCount,
  1193. pAle,
  1194. VolData.BytesPerSector,
  1195. VolData.BytesPerCluster)){
  1196. EH(FALSE);
  1197. __leave;
  1198. }
  1199. //Make pAle point to the memory block to write the next extent.
  1200. pAle += pExtents[i].ClusterCount * VolData.BytesPerCluster;
  1201. }
  1202. bReturn = TRUE; // everything is OK
  1203. }
  1204. __finally {
  1205. //Unlock our pointer to the attribute list memory.
  1206. if(*phAle){
  1207. BOOL isLocked = GlobalUnlock(*phAle);
  1208. EH(!isLocked);
  1209. }
  1210. //Free up our extent list.
  1211. if(ExtentData.hExtents){
  1212. EH_ASSERT(GlobalUnlock(ExtentData.hExtents) == FALSE);
  1213. EH_ASSERT(GlobalFree(ExtentData.hExtents) == NULL);
  1214. ExtentData.pExtents = NULL;
  1215. ExtentData.hExtents = NULL;
  1216. }
  1217. }
  1218. return bReturn;
  1219. }
  1220. //Given the name and type of a stream, GetStreamExtentsByNameAndType will track down the stream and build
  1221. //the extent list for it.
  1222. BOOL
  1223. GetStreamExtentsByNameAndType(
  1224. TCHAR* StreamName,
  1225. ATTRIBUTE_TYPE_CODE StreamType,
  1226. FILE_RECORD_SEGMENT_HEADER* pFrs
  1227. )
  1228. {
  1229. ULONG StreamNumber = 0;
  1230. FILE_EXTENT_HEADER* pFileExtentHeader;
  1231. STREAM_EXTENT_HEADER* pStreamExtentHeader;
  1232. EXTENT_LIST* pExtents;
  1233. BOOL bExtentFound = FALSE;
  1234. UINT i;
  1235. //validate input not null
  1236. EF_ASSERT(pFrs);
  1237. //Validate the input criteria to make sure it could represent a valid stream.
  1238. //($DATA, $INDEX_ALLOCATION, $ATTRIBUTE_LIST, $BITMAP).
  1239. EF_ASSERT(StreamType == $DATA || $INDEX_ALLOCATION || $BITMAP);
  1240. EF(GetStreamNumberFromNameAndType(&StreamNumber, StreamName, StreamType, pFrs));
  1241. EF(GetExtentList(ALL_STREAMS, pFrs));
  1242. //Pull out that number of stream.
  1243. //First set the file extent header to the beginning of the extent list.
  1244. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList;
  1245. //Set the stream extent header to point to the first stream.
  1246. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((char*)VolData.pExtentList + sizeof(FILE_EXTENT_HEADER));
  1247. //Make sure he's asking for a valid stream number for this file. Error out if not.
  1248. EF(StreamNumber < pFileExtentHeader->TotalNumberOfStreams);
  1249. //Loop through each of the streams in the file.
  1250. //Check against both NumberOfStreams and TotalNumberOfStreams to ensure we don't go beyond the end of the list.
  1251. for(i=0; i<pFileExtentHeader->NumberOfStreams; i++){
  1252. //If the stream number matches the one we're looking for, move the memory to the beginning of the extents list.
  1253. if(StreamNumber == pStreamExtentHeader->StreamNumber){
  1254. //Note that we found the stream and break;
  1255. bExtentFound = TRUE;
  1256. break;
  1257. }
  1258. //Set the pointer to the next stream header.
  1259. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount*sizeof(EXTENT_LIST));
  1260. //Check to make sure that we haven't gone beyond the stream number the user was asking for.
  1261. if(pStreamExtentHeader->StreamNumber > StreamNumber){
  1262. break;
  1263. }
  1264. }
  1265. //If we didn't find the stream number the user was asking for, that means it doesn't have any extents.
  1266. if(!bExtentFound){
  1267. //Fill in a stream header for an empty stream for the return.
  1268. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)VolData.pExtentList;
  1269. pStreamExtentHeader->StreamNumber = StreamNumber;
  1270. pStreamExtentHeader->ExtentCount = 0;
  1271. }
  1272. //Otherwise a stream was found, so move it to the top of the extent list buffer for the return.
  1273. else{
  1274. //Move the stream extent header and it's extents to the beginning and overwrite the file extent header and anything else.
  1275. MoveMemory(pFileExtentHeader, pStreamExtentHeader, UINT(sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount*sizeof(EXTENT_LIST)));
  1276. }
  1277. //Note how much memory is actually used by the extent list.
  1278. //First get a pointer to the end.
  1279. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)(char*)VolData.pExtentList;
  1280. //If we hit a stream with a header but no extents, error.
  1281. EF(pStreamExtentHeader->ExtentCount);
  1282. //Set the stream extent header pointer to the next stream (current position + header + # extents * sizeof each extent).
  1283. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount * sizeof(EXTENT_LIST));
  1284. //Now that pStreamExtentHeader points to just after the stream, we can use it's position to determine the size of the whole extent list.
  1285. VolData.ExtentListSize = (UINT_PTR)pStreamExtentHeader - (UINT_PTR)pFileExtentHeader;
  1286. //Count the number of clusters in the stream.
  1287. VolData.NumberOfClusters = 0;
  1288. VolData.NumberOfRealClusters = 0;
  1289. //First get a pointer to the end.
  1290. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)(char*)VolData.pExtentList;
  1291. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  1292. for(i=0; i<pStreamExtentHeader->ExtentCount; i++){
  1293. //Count the number of virtual clusters in the file.
  1294. VolData.NumberOfClusters += pExtents[i].ClusterCount;
  1295. //Count the number of real clusters in the file.
  1296. if(pExtents[i].StartingLcn != 0xFFFFFFFFFFFFFFFF){
  1297. VolData.NumberOfRealClusters += pExtents[i].ClusterCount;
  1298. }
  1299. }
  1300. //Get the file size for the stream.
  1301. VolData.FileSize = pStreamExtentHeader->FileSize;
  1302. //If there are any excess extents then this file is fragmented.
  1303. if(pStreamExtentHeader->ExtentCount > 1){
  1304. VolData.bFragmented = TRUE;
  1305. }
  1306. //Keep track of the number of extents in this stream.
  1307. VolData.NumberOfFragments = pStreamExtentHeader->ExcessExtents + 1;
  1308. return TRUE;
  1309. }
  1310. //Given the name and type of a stream, GetStreamNumberFromNameAndType finds which stream number corresponds.
  1311. BOOL
  1312. GetStreamNumberFromNameAndType(
  1313. ULONG* pStreamNumber,
  1314. TCHAR* StreamName,
  1315. ATTRIBUTE_TYPE_CODE TypeCode,
  1316. FILE_RECORD_SEGMENT_HEADER* pFrs
  1317. )
  1318. {
  1319. ATTRIBUTE_RECORD_HEADER* pArh = NULL; //The specific attribute we're looking at now.
  1320. ATTRIBUTE_LIST_ENTRY* pAle = NULL; //The attribute entry we're looking at now (only used for huge and mega files).
  1321. HANDLE hAle = NULL; //Handle to memory for attribute list if it was nonresident and had to be read off the disk.
  1322. LONGLONG SaveFrn = 0; //Used to compare the FRN before and after getting a new FRS to make sure we got the one we want.
  1323. DWORD dwAttrListLength = 0; //The length of the attribute list in a huge or mega file.
  1324. EXTENT_LIST_DATA ExtentData;
  1325. DWORD TotalNumberOfStreams;
  1326. //Validate the input criteria to make sure it could represent a valid stream.
  1327. //($DATA, $INDEX_ALLOCATION, $ATTRIBUTE_LIST, $BITMAP).
  1328. EF_ASSERT(TypeCode == $DATA || $INDEX_ALLOCATION || $BITMAP);
  1329. //Set up the Extent pointers structure to fill in the extent list in VolData.
  1330. ZeroMemory(&ExtentData, sizeof(EXTENT_LIST_DATA));
  1331. ExtentData.hExtents = VolData.hExtentList;
  1332. ExtentData.pExtents = VolData.pExtentList;
  1333. ExtentData.ExtentListAlloced = (ULONG)VolData.ExtentListAlloced;
  1334. ExtentData.dwEnabledStreams = TRUE; //Since the user is asking for the stream by name and type, it's valid to accept a request for a $BITMAP stream.
  1335. #ifdef OFFLINEDK
  1336. //Do initializtion to get the FRS if it wasn't already loaded.
  1337. if(!pFrs){
  1338. UCHAR* pMftBitmap = NULL;
  1339. FILE_RECORD_SEGMENT_HEADER* pFileRecord = (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord;
  1340. //0.0E00 Get a pointer to the MFT bitmap since we will be reading from the disk directly rather than via a hook.
  1341. EF((pMftBitmap = (UCHAR*)GlobalLock(VolData.hMftBitmap)) != NULL);
  1342. if (pMftBitmap != NULL) {
  1343. BOOL isLocked = GlobalUnlock(VolData.hMftBitmap);
  1344. EH(!isLocked);
  1345. }
  1346. }
  1347. #endif
  1348. //Initialize the VolData fields.
  1349. VolData.FileSize = 0;
  1350. VolData.bFragmented = FALSE;
  1351. VolData.bCompressed = FALSE;
  1352. VolData.bDirectory = FALSE;
  1353. //Get the FRS if it wasn't already loaded.
  1354. if(!pFrs){
  1355. //Set the pFrs to point to a buffer to hold the FRS.
  1356. pFrs = (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord; //A shortcut to reduce code + cycles when accessing the frs.
  1357. //Save the FRN so we know if we get the wrong FRS later.
  1358. SaveFrn = VolData.FileRecordNumber;
  1359. //Read in the FRS for the attribute.
  1360. #ifdef OFFLINEDK
  1361. //0.1E00 Get an FRS.
  1362. EF(GetFrs(&VolData.FileRecordNumber, VolData.pMftExtentList, pMftBitmap, VolData.pMftBuffer, pFileRecord));
  1363. #else
  1364. //0.1E00 Get the next file record.
  1365. EF(GetInUseFrs(VolData.hVolume, &VolData.FileRecordNumber, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord, (ULONG)VolData.BytesPerFRS));
  1366. #endif
  1367. //Make sure we got the FRS we requested.
  1368. EF_ASSERT(VolData.FileRecordNumber == SaveFrn);
  1369. }
  1370. //Detect if it is a directory and set flag
  1371. if(pFrs->Flags & FILE_FILE_NAME_INDEX_PRESENT){
  1372. VolData.bDirectory = TRUE;
  1373. }
  1374. else{
  1375. VolData.bDirectory = FALSE;
  1376. }
  1377. //Find $STANDARD_INFORMATION attribute -- if there is none then don't use this FRS.
  1378. if(!FindAttributeByType($STANDARD_INFORMATION, pFrs, &pArh, (ULONG)VolData.BytesPerFRS)){
  1379. return FALSE;
  1380. }
  1381. //Note if the file is compressed or not.
  1382. VolData.bCompressed = (((PSTANDARD_INFORMATION)((UCHAR*)pArh+pArh->Form.Resident.ValueOffset))->FileAttributes & FILE_ATTRIBUTE_COMPRESSED) ? TRUE : FALSE;
  1383. //Note that there were no streams found yet.
  1384. TotalNumberOfStreams = 0;
  1385. __try{
  1386. //Look for an $ATTRIBUTE_LIST
  1387. if(!FindAttributeByType($ATTRIBUTE_LIST, pFrs, &pArh, (ULONG)VolData.BytesPerFRS)){
  1388. //If no $ATTRIBUTE_LIST was found, move to the first stream in the FRS.
  1389. if(!FindStreamInFrs(pFrs, &pArh, &ExtentData)){
  1390. //If no stream was found, then there is no extent list for this file.
  1391. return TRUE;
  1392. }
  1393. do{
  1394. //Check for a match on the stream.
  1395. // If the type code matches and
  1396. // Either they are both nonamed or
  1397. // They are the same namelength, and the text in the names compare, then they are the stream we're looking for.
  1398. if(pArh->TypeCode == TypeCode &&
  1399. ((!pArh->NameLength && !lstrlen(StreamName)) ||
  1400. ((pArh->NameLength == lstrlen(StreamName)) && !memcmp((UCHAR*)pArh+pArh->NameOffset, StreamName, lstrlenW(StreamName)*sizeof(WCHAR))))){
  1401. *pStreamNumber = TotalNumberOfStreams;
  1402. return TRUE;
  1403. }
  1404. //Keep track of the fact the stream exists so they can be numbered correctly.
  1405. TotalNumberOfStreams++;
  1406. //Go to the next stream.
  1407. }while(FindNextStreamInFrs(pFrs, &pArh, &ExtentData));
  1408. }
  1409. //If an $ATTRIBUTE_LIST was found
  1410. else{
  1411. //If the $ATTRIBUTE_LIST is nonresident get the entire attribute list from the disk before proceeding.
  1412. if(pArh->FormCode == NONRESIDENT_FORM){
  1413. //Load the attribute extents from the disk.
  1414. LoadExtentDataToMem(pArh, &hAle, &dwAttrListLength);
  1415. //Get a pointer to the allocated memory.
  1416. EF_ASSERT(pAle = (ATTRIBUTE_LIST_ENTRY*)GlobalLock(hAle));
  1417. }
  1418. //If it was a resident attribute list, then the length of the attribute list can be determined from the attribute.
  1419. else{
  1420. pAle = (ATTRIBUTE_LIST_ENTRY*)(pArh->Form.Resident.ValueOffset + (UCHAR*)pArh);
  1421. dwAttrListLength = pArh->Form.Resident.ValueLength;
  1422. }
  1423. //Move to the first stream.
  1424. if(!FindStreamInAttrList(pAle, &pAle, dwAttrListLength, &ExtentData)){
  1425. //If no stream was found, then there is no extent list for this file.
  1426. return TRUE;
  1427. }
  1428. do{
  1429. //Check for a match on the stream.
  1430. // If the type code matches and
  1431. // Either they are both unnamed or
  1432. // They are the same namelength, and the text in the names compare, then they are the stream we're looking for.
  1433. if(pAle->AttributeTypeCode == TypeCode &&
  1434. ((!pAle->AttributeNameLength && !lstrlen(StreamName)) ||
  1435. ((pAle->AttributeNameLength == lstrlen(StreamName)) && !memcmp((UCHAR*)pAle+pAle->AttributeNameOffset, StreamName, lstrlenW(StreamName)*sizeof(WCHAR))))){
  1436. *pStreamNumber = TotalNumberOfStreams;
  1437. return TRUE;
  1438. }
  1439. //Keep track of the fact the stream exists so they can be numbered correctly.
  1440. TotalNumberOfStreams++;
  1441. //Go to the next stream.
  1442. }while(FindNextStreamInAttrList(pAle, &pAle, dwAttrListLength, &ExtentData));
  1443. }
  1444. //Invalid stream number because no stream was found.
  1445. *pStreamNumber = 0xFFFFFFFF;
  1446. }
  1447. __finally{
  1448. //If hAle was allocated, then it needs to be unlocked and freed.
  1449. if(hAle){
  1450. EH_ASSERT(GlobalUnlock(hAle) == FALSE);
  1451. EH_ASSERT(GlobalFree(hAle) == NULL);
  1452. }
  1453. }
  1454. return FALSE;
  1455. }
  1456. //Given the number of a stream, GetStreamNameAndTypeFromNumber finds what the stream's name and type are.
  1457. BOOL
  1458. GetStreamNameAndTypeFromNumber(
  1459. ULONG StreamNumber,
  1460. TCHAR* StreamName,
  1461. ATTRIBUTE_TYPE_CODE* pTypeCode,
  1462. FILE_RECORD_SEGMENT_HEADER* pFrs
  1463. )
  1464. {
  1465. ATTRIBUTE_RECORD_HEADER* pArh = NULL; //The specific attribute we're looking at now.
  1466. ATTRIBUTE_LIST_ENTRY* pAle = NULL; //The attribute entry we're looking at now (only used for huge and mega files).
  1467. HANDLE hAle = NULL; //Handle to memory for attribute list if it was nonresident and had to be read off the disk.
  1468. LONGLONG SaveFrn = 0; //Used to compare the FRN before and after getting a new FRS to make sure we got the one we want.
  1469. DWORD dwAttrListLength = 0; //The length of the attribute list in a huge or mega file.
  1470. EXTENT_LIST_DATA ExtentData;
  1471. DWORD TotalNumberOfStreams;
  1472. //Validate the input criteria to make sure it could represent a valid stream.
  1473. //($DATA, $INDEX_ALLOCATION, $ATTRIBUTE_LIST, $BITMAP).
  1474. EF_ASSERT(*pTypeCode == $DATA || $INDEX_ALLOCATION || $BITMAP);
  1475. //Set up the Extent pointers structure to fill in the extent list in VolData.
  1476. ZeroMemory(&ExtentData, sizeof(EXTENT_LIST_DATA));
  1477. ExtentData.hExtents = VolData.hExtentList;
  1478. ExtentData.pExtents = VolData.pExtentList;
  1479. ExtentData.ExtentListAlloced = (UINT)VolData.ExtentListAlloced;
  1480. #ifdef OFFLINEDK
  1481. //Do initializtion to get the FRS if it wasn't already loaded.
  1482. if(!pFrs){
  1483. UCHAR* pMftBitmap = NULL;
  1484. FILE_RECORD_SEGMENT_HEADER* pFileRecord = (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord;
  1485. //0.0E00 Get a pointer to the MFT bitmap since we will be reading from the disk directly rather than via a hook.
  1486. EF((pMftBitmap = (UCHAR*)GlobalLock(VolData.hMftBitmap)) != NULL);
  1487. if (pMftBitmap != NULL) {
  1488. BOOL isLocked = GlobalUnlock(VolData.hMftBitmap);
  1489. EH(!isLocked);
  1490. }
  1491. }
  1492. #endif
  1493. //Initialize the VolData fields.
  1494. VolData.FileSize = 0;
  1495. VolData.bFragmented = FALSE;
  1496. VolData.bCompressed = FALSE;
  1497. VolData.bDirectory = FALSE;
  1498. //Get the FRS if it wasn't already loaded.
  1499. if(!pFrs){
  1500. //Set the pFrs to point to a buffer to hold the FRS.
  1501. pFrs = (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord; //A shortcut to reduce code + cycles when accessing the frs.
  1502. //Save the FRN so we know if we get the wrong FRS later.
  1503. SaveFrn = VolData.FileRecordNumber;
  1504. //Read in the FRS for the attribute.
  1505. #ifdef OFFLINEDK
  1506. //0.1E00 Get an FRS.
  1507. EF(GetFrs(&VolData.FileRecordNumber, VolData.pMftExtentList, pMftBitmap, VolData.pMftBuffer, pFileRecord));
  1508. #else
  1509. //0.1E00 Get the next file record.
  1510. EF(GetInUseFrs(VolData.hVolume, &VolData.FileRecordNumber, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord, (ULONG)VolData.BytesPerFRS));
  1511. #endif
  1512. //Make sure we got the FRS we requested.
  1513. EF_ASSERT(VolData.FileRecordNumber == SaveFrn);
  1514. }
  1515. //Detect if it is a directory and set the flag
  1516. if(pFrs->Flags & FILE_FILE_NAME_INDEX_PRESENT){
  1517. VolData.bDirectory = TRUE;
  1518. }
  1519. else{
  1520. VolData.bDirectory = FALSE;
  1521. }
  1522. //Find $STANDARD_INFORMATION attribute -- if there is none then don't use this FRS.
  1523. if(!FindAttributeByType($STANDARD_INFORMATION, pFrs, &pArh, (ULONG)VolData.BytesPerFRS)){
  1524. return FALSE;
  1525. }
  1526. //Note if the file is compressed or not.
  1527. VolData.bCompressed = (((PSTANDARD_INFORMATION)((UCHAR*)pArh+pArh->Form.Resident.ValueOffset))->FileAttributes & FILE_ATTRIBUTE_COMPRESSED) ? TRUE : FALSE;
  1528. //Note that there were no streams found yet.
  1529. TotalNumberOfStreams = 0;
  1530. __try{
  1531. //Look for an $ATTRIBUTE_LIST
  1532. if(!FindAttributeByType($ATTRIBUTE_LIST, pFrs, &pArh, (ULONG)VolData.BytesPerFRS)){
  1533. //If no $ATTRIBUTE_LIST was found, move to the first stream in the FRS.
  1534. if(!FindStreamInFrs(pFrs, &pArh, &ExtentData)){
  1535. //If no stream was found, then there is no extent list for this file.
  1536. return TRUE;
  1537. }
  1538. do{
  1539. //Check for a match on the stream.
  1540. if(StreamNumber == TotalNumberOfStreams){
  1541. *pTypeCode = pArh->TypeCode;
  1542. if(pArh->NameLength){
  1543. CopyMemory(StreamName, (UCHAR*)pArh+pArh->NameOffset, pArh->NameLength*sizeof(TCHAR));
  1544. StreamName[pArh->NameLength] = 0;
  1545. }
  1546. else{
  1547. StreamName[0] = 0;
  1548. }
  1549. return TRUE;
  1550. }
  1551. //Keep track of the fact the stream exists so they can be numbered correctly.
  1552. TotalNumberOfStreams++;
  1553. //Go to the next stream.
  1554. }while(FindNextStreamInFrs(pFrs, &pArh, &ExtentData));
  1555. }
  1556. //If an $ATTRIBUTE_LIST was found
  1557. else{
  1558. //If the $ATTRIBUTE_LIST is nonresident get the entire attribute list from the disk before proceeding.
  1559. if(pArh->FormCode == NONRESIDENT_FORM){
  1560. //Load the attribute extents from the disk.
  1561. LoadExtentDataToMem(pArh, &hAle, &dwAttrListLength);
  1562. //Get a pointer to the allocated memory.
  1563. EF_ASSERT(pAle = (ATTRIBUTE_LIST_ENTRY*)GlobalLock(hAle));
  1564. }
  1565. //If it was a resident attribute list, then the length of the attribute list can be determined from the attribute.
  1566. else{
  1567. pAle = (ATTRIBUTE_LIST_ENTRY*)(pArh->Form.Resident.ValueOffset + (UCHAR*)pArh);
  1568. dwAttrListLength = pArh->Form.Resident.ValueLength;
  1569. }
  1570. //Move to the first stream.
  1571. if(!FindStreamInAttrList(pAle, &pAle, dwAttrListLength, &ExtentData)){
  1572. //If no stream was found, then there is no extent list for this file.
  1573. return TRUE;
  1574. }
  1575. do{
  1576. //Check for a match on the stream.
  1577. if(StreamNumber == TotalNumberOfStreams){
  1578. *pTypeCode = pAle->AttributeTypeCode;
  1579. if(pAle->AttributeNameLength){
  1580. CopyMemory(StreamName, (UCHAR*)pAle+pAle->AttributeNameOffset, pAle->AttributeNameLength*sizeof(TCHAR));
  1581. StreamName[pAle->AttributeNameLength] = 0;
  1582. }
  1583. else{
  1584. StreamName[0] = 0;
  1585. }
  1586. return TRUE;
  1587. }
  1588. //Keep track of the fact the stream exists so they can be numbered correctly.
  1589. TotalNumberOfStreams++;
  1590. //Go to the next stream.
  1591. }while(FindNextStreamInAttrList(pAle, &pAle, dwAttrListLength, &ExtentData));
  1592. }
  1593. //Invalid stream number because no stream was found.
  1594. StreamNumber = 0xFFFFFFFF;
  1595. }
  1596. __finally {
  1597. //If hAle was allocated, then it needs to be unlocked and freed.
  1598. if(hAle){
  1599. EH_ASSERT(GlobalUnlock(hAle) == FALSE);
  1600. EH_ASSERT(GlobalFree(hAle) == NULL);
  1601. }
  1602. }
  1603. return FALSE;
  1604. }
  1605. //Reads in the extents which are not part of the file's data. (I.E. extents which are contained in non-resident
  1606. //ATTRIBUTE_LIST's or other non-resident attributes other than $DATA and $INDEX_ALLOCATION.)
  1607. //Note that this function operates nearly identical to GetExtentList only it is looking for any attribute other than
  1608. //$DATA and $INDEX_ALLOCATION. The code can be copied and revised from GetExtentlist.
  1609. //Even cooler, see if you can integrate them into one. I'm not sure that's reasonably possible because:
  1610. //The most major difference between GetNonDataStreamExtents and GetExtentList is that GetNonDataStreamExtents should
  1611. //concatenate extent lists from all non-data attributes. Therefore, this function will create only one "stream" in the
  1612. //extent list. The reason for this is so that Offline can simply defrag all the non-data stream stuff in one shibang.
  1613. //Online won't be defragging it and will be simply coloring it system space color, so having only one stream simplifies
  1614. //the process for both Offline and Online and DKMS.
  1615. BOOL
  1616. GetNonDataStreamExtents(
  1617. )
  1618. {
  1619. //Look for an $ATTRIBUTE_LIST
  1620. //If not found
  1621. //Move to the first stream in the FRS.
  1622. //If this attribute is not a $DATA or $INDEX_ALLOCATION and it is nonresident�
  1623. //Load the extents from the attribute.
  1624. //Go to the next stream.
  1625. //If an $ATTRIBUTE_LIST was found
  1626. //If the $ATTRIBUTE_LIST is resident
  1627. //Move to the first stream.
  1628. //If the attribute is not a $DATA or $INDEX_ALLOCATION�
  1629. //Load its FRS.
  1630. //Go to the attribute.
  1631. //If the attribute is nonresident
  1632. //Load the extents from the attribute.
  1633. //Find the next stream.
  1634. //If the $ATTRIBUTE_LIST is non-resident
  1635. //Load the attribute extents from the disk.
  1636. //Move to the first stream.
  1637. //If the attribute is not a $DATA or $INDEX_ALLOCATION�
  1638. //Load its FRS.
  1639. //Go to the attribute.
  1640. //If the attribute is nonresident
  1641. //Load the extents from the attribute.
  1642. //Find the next stream.
  1643. return TRUE;
  1644. }
  1645. //A really quickie version of GetExtentList which only does enough work to identify whether the file has an extent list or not.
  1646. //This function is used only to identify how many entries to make in the file lists.
  1647. BOOL
  1648. IsNonresidentFile(
  1649. DWORD dwEnabledStreams,
  1650. FILE_RECORD_SEGMENT_HEADER* pFrs
  1651. )
  1652. {
  1653. EXTENT_LIST_DATA ExtentData;
  1654. ATTRIBUTE_RECORD_HEADER* pArh = NULL; //The specific attribute we're looking at now.
  1655. //bug occurred during stress. We do not test for NULL condition.
  1656. if(pFrs == NULL)
  1657. {
  1658. return FALSE;
  1659. }
  1660. //Set up the Extent pointers structure to fill in the extent list in VolData.
  1661. ZeroMemory(&ExtentData, sizeof(EXTENT_LIST_DATA));
  1662. ExtentData.dwEnabledStreams = dwEnabledStreams;
  1663. //Look for an $ATTRIBUTE_LIST
  1664. if(!FindAttributeByType($ATTRIBUTE_LIST, pFrs, &pArh, (ULONG)VolData.BytesPerFRS)){
  1665. if (VolData.bMFTCorrupt) {
  1666. return FALSE;
  1667. }
  1668. //If no $ATTRIBUTE_LIST was found, move to the first stream in the FRS.
  1669. if(!FindStreamInFrs(pFrs, &pArh, &ExtentData)){
  1670. //If no stream was found, then there is no extent list for this file -- therefore it is resident.
  1671. return FALSE;
  1672. }
  1673. do{
  1674. //If this is a nonresident stream, then this is a nonresident file.
  1675. if(pArh->FormCode == NONRESIDENT_FORM){
  1676. return TRUE;
  1677. }
  1678. //Go to the next stream.
  1679. }while(FindNextStreamInFrs(pFrs, &pArh, &ExtentData));
  1680. }
  1681. else{
  1682. //An attribute list was found, therefore, this is a nonresident file.
  1683. return TRUE;
  1684. }
  1685. //No nonresident attributes were found. Therefore this is a resident file.
  1686. return FALSE;
  1687. }
  1688. //Here is an alternate algorithm for IsNonresidentFile which would be more efficient. Check for robustness before implementing.
  1689. // //0.0E00 Skip this filerecord if it has no attribute list or data attribute
  1690. // if(!FindAttributeByType($ATTRIBUTE_LIST, pFileRecord, &pArh, (ULONG)VolData.BytesPerFRS)){
  1691. //
  1692. // //0.0E00 If there are no $DATA or $INDEX_ALLOCATION attributes then this is a small file and has no extents. Ignore it.
  1693. // if((!FindAttributeByType($DATA, pFileRecord, &pArh, (ULONG)VolData.BytesPerFRS) &&
  1694. // !FindAttributeByType($INDEX_ALLOCATION, pFileRecord, &pArh, (ULONG)VolData.BytesPerFRS))){
  1695. // continue;
  1696. // }
  1697. //
  1698. // //0.0E00 Skip small files -- there's no defragmentation to do.
  1699. // //That is, resident files, or nonresident files with no data on disk.
  1700. // if(pArh->FormCode == RESIDENT_FORM || (pArh->FormCode == NONRESIDENT_FORM && pArh->Form.Nonresident.TotalAllocated == 0)){
  1701. // continue;
  1702. // }
  1703. // }
  1704. /*****************************************************************************************************************
  1705. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1706. ROUTINE DESCRIPTION:
  1707. Directories can't really have their extent lists collapsed. But, we would
  1708. like to know if they need defragmenting, so the fragmentation state has to
  1709. be verified or denied, so we know what to do later. This was scavenged from
  1710. CollapseExtents, which calls GetLowestStartingLcn; that routine does its
  1711. thing based on the number of fragments, not the number of extents (??) so
  1712. we can't use that, either.
  1713. This routine is only used by OFFLINE.
  1714. INPUT + OUTPUT:
  1715. None.
  1716. GLOBALS:
  1717. OUT VolData.ExtentsState - Whether or not the file is contiguous or fragmented.
  1718. RETURN:
  1719. TRUE - Success.
  1720. FALSE - Fatal Error.
  1721. */
  1722. #ifdef OFFLINEDK
  1723. //This function needs to be rewritten to fit the new extents scheme.
  1724. //This is Offline code, aka. John's.
  1725. /*
  1726. BOOL
  1727. CheckFragged(
  1728. )
  1729. {
  1730. LONGLONG NumberOfExtents = VolData.NumberOfExtents;
  1731. LONGLONG Extent = 0;
  1732. EXTENT_LIST* pExtentList = VolData.pExtentList;
  1733. LONGLONG MinCluster = 0;
  1734. BOOL bSetMin =FALSE;
  1735. LONGLONG LastRealExtent;
  1736. //0.1E00 say it isn't fragmented to start with (innocent till proven guilty...)
  1737. VolData.bFragmented = FALSE;
  1738. VolData.NumberOfFragments = 1;
  1739. //Don't bother looping if nothing to loop on...
  1740. if(VolData.NumberOfExtents < 2){
  1741. VolData.StartingLcn = pExtentList[0].StartingLcn;
  1742. return TRUE;
  1743. }
  1744. //0.1E00 Go through each extent and see if it is really adjacent to the next
  1745. for(Extent = 0; Extent < NumberOfExtents; Extent ++){
  1746. //Ensure we skip virtual extents
  1747. if (pExtentList[Extent].StartingLcn >= 0) {
  1748. //ensure we get a valid minimum starting spot
  1749. if (!bSetMin) {
  1750. bSetMin = TRUE;
  1751. MinCluster = pExtentList[Extent].StartingLcn;
  1752. LastRealExtent = Extent;
  1753. continue;
  1754. }
  1755. //1.0E00 If this Lcn is lower than whatever was lowest so far...
  1756. if (pExtentList[Extent].StartingLcn < MinCluster) {
  1757. //Record it so it can be returned
  1758. MinCluster = pExtentList[Extent].StartingLcn;
  1759. }
  1760. //1.0E00 If this extent is adjacent to the last extent
  1761. if(pExtentList[LastRealExtent].StartingLcn +
  1762. pExtentList[LastRealExtent].ClusterCount !=
  1763. pExtentList[Extent].StartingLcn){
  1764. //Okay, it's not contiguous...
  1765. VolData.bFragmented = TRUE;
  1766. VolData.NumberOfFragments++;
  1767. } //end "if not adjacent" test
  1768. LastRealExtent = Extent;
  1769. } //end if Lcn >= 0
  1770. } //end "for Extent" loop
  1771. //0.1E00 Set the lowest Lcn of the file. This is used in our algorithms to determine which files to move.
  1772. VolData.StartingLcn = MinCluster;
  1773. return TRUE;
  1774. }
  1775. */
  1776. #endif
  1777. /*****************************************************************************************************************
  1778. FORMAT OF MAPPING PAIRS:
  1779. The first byte contains the sizes of the mapping pair fields. The low nibble contains the number of bytes
  1780. in the extent length field, and the high nibble contains the number of bytes in the extent offset field.
  1781. The next bytes are the extent length field, whose length is determined by the first byte.
  1782. The next bytes are the extent offset field, whoese length is determined by the first byte.
  1783. The extent offset field contains an offset in clusters from the previous extent, not an absolute value on the
  1784. disk. If this is the first extent, its offset is from zero.
  1785. The list of mapping pairs is null terminated -- the first byte describing the length of the fields will
  1786. contain a zero, and the extent length value will be zero.
  1787. If the size of the extent offset field is zero (and the extent length is not) then this is a virtual extent:
  1788. Virtual extents are used in compressed files; each block of a compressed file is represented by a
  1789. physical extent and a virtual extent (if the block did not compress there is no virtual extent).
  1790. The physical extent describes the clusters actually used on the disk by the block in it's compressed
  1791. form; the virtual extent only indicates the difference between the uncompressed block size and the
  1792. compressed block size. ie: if the uncompressed block is 16 clusters and the block compresses down to
  1793. 8 clusters then the block will have 2 mapping pairs: the physical extent length will be 8, the virtual
  1794. extent length will be 8, and the block will use 8 clusters of disk space.
  1795. /*****************************************************************************************************************/
  1796. /*****************************************************************************************************************
  1797. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1798. ROUTINE DESCRIPTION:
  1799. Get the basic statistics for an NTFS volume.
  1800. INPUT + OUTPUT:
  1801. None.
  1802. GLOBALS:
  1803. The VolData structure should be zeroed out with the exception of any fields already filled in before calling this function.
  1804. IN VolData.cDrive - The drive letter for the volume we wish to access.
  1805. OUT VolData.hVolume - Handle to the volume.
  1806. OUT VolData.FileSystem - FS_NTFS.
  1807. OUT VolData.MftStartLcn - First cluster of the MFT.
  1808. OUT VolData.Mft2StartLcn - First cluster of the MFT Mirror.
  1809. OUT VolData.BytesPerSector - Bytes Per Sector.
  1810. OUT VolData.BytesPerCluster - Bytes Per Cluster.
  1811. OUT VolData.BytesPerFRS - Bytes Per FileRecord.
  1812. OUT VolData.ClustersPerFRS - Clusters Per FileRecord.
  1813. OUT VolData.TotalSectors - Total Sectors on the volume.
  1814. OUT VolData.TotalClusters - Total Clusters on the volume.
  1815. OUT VolData.MftZoneStart - Mft Zone Start.
  1816. OUT VolData.MftZoneEnd - Mft Zone End.
  1817. OUT VolData.FreeSpace - Free Space on the volume.
  1818. OUT VolData.UsedSpace - Used Space on the volume.
  1819. OUT VolData.UsedClusters - Used Clusters on the volume.
  1820. OUT VolData.SectorsPerCluster - Sectors Per Cluster.
  1821. OUT VolData.MftStartOffset - Byte offset into the volume of the MFT.
  1822. OUT VolData.TotalFileRecords - Total FileRecords on the volume.
  1823. OUT VolData.FileRecordNumber - End of the MFT; where we start scanning filerecords.
  1824. OUT VolData.CenterOfDisk - Cluster at the center of the disk.
  1825. OUT VolData.CenterOfBitmap - Word at the Center Of the Bitmap.
  1826. OUT VolData.CenterMask - Mask for the bit in the word at the Center Of the Bitmap.
  1827. OUT VolData.CenterBit - Bit in the word at the Center Of the Bitmap.
  1828. OUT VolData.BitmapSize - Bitmap Size.
  1829. OUT VolData.hFile - INVALID_HANDLE_VALUE.
  1830. RETURN:
  1831. TRUE = Success
  1832. FALSE = Failure
  1833. */
  1834. BOOL
  1835. GetNtfsVolumeStats(
  1836. )
  1837. {
  1838. //TCHAR cVolume[10];
  1839. ULONG BytesReturned = 0;
  1840. NTFS_VOLUME_DATA_BUFFER VolumeData = {0};
  1841. //0.1E00 Fill in the handle values which aren't zero.
  1842. VolData.hVolume = INVALID_HANDLE_VALUE;
  1843. VolData.hFile = INVALID_HANDLE_VALUE;
  1844. //0.1E00 Get the file system on this drive.
  1845. EF(GetFileSystem(VolData.cVolumeName, &VolData.FileSystem, VolData.cVolumeLabel));
  1846. //0.1E00 If this volume is not NTFS, error out.
  1847. if(VolData.FileSystem != FS_NTFS){
  1848. return FALSE;
  1849. }
  1850. //0.0E00 Get handle to volume
  1851. VolData.hVolume = CreateFile(
  1852. VolData.cVolumeName,
  1853. GENERIC_READ | GENERIC_WRITE,
  1854. FILE_SHARE_READ|FILE_SHARE_WRITE,
  1855. NULL,
  1856. OPEN_EXISTING,
  1857. FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING,
  1858. NULL);
  1859. EF_ASSERT(VolData.hVolume != INVALID_HANDLE_VALUE);
  1860. //0.0E00 Get the volume data
  1861. EF(ESDeviceIoControl(VolData.hVolume,
  1862. FSCTL_GET_NTFS_VOLUME_DATA,
  1863. NULL,
  1864. 0,
  1865. &VolumeData,
  1866. sizeof(VolumeData),
  1867. &BytesReturned,
  1868. NULL));
  1869. //0.1E00 Copy over the values from the volume data that belong in the VolData as well.
  1870. VolData.MftStartLcn = VolumeData.MftStartLcn.QuadPart; //First cluster of the MFT.
  1871. VolData.Mft2StartLcn = VolumeData.Mft2StartLcn.QuadPart; //First Cluster of the MFT2.
  1872. VolData.BytesPerSector = VolumeData.BytesPerSector; //# of bytes per sector on the disk.
  1873. VolData.BytesPerCluster = VolumeData.BytesPerCluster; //# of bytes per cluster on the disk.
  1874. VolData.BytesPerFRS = VolumeData.BytesPerFileRecordSegment; //# of bytes in a file record in the MFT.
  1875. VolData.ClustersPerFRS = VolumeData.ClustersPerFileRecordSegment; //# of clusters in a file record in the MFT.
  1876. VolData.TotalSectors = VolumeData.NumberSectors.QuadPart; //# of sectors on the disk.
  1877. VolData.TotalClusters = VolumeData.TotalClusters.QuadPart; //# of clusters on the disk.
  1878. VolData.MftZoneStart = VolumeData.MftZoneStart.QuadPart; //Cluster # the MFT Zone starts on.
  1879. VolData.MftZoneEnd = VolumeData.MftZoneEnd.QuadPart; //Cluster # the MFT Zone ends on.
  1880. //0.1E00 Check for invalid values.
  1881. EF(VolData.BytesPerSector != 0);
  1882. EF(VolData.BytesPerFRS != 0);
  1883. //It turns out that the MFTZone values can be outside the boundaries of the disk
  1884. //this in turn causes the volume bitmap manipulators to fail with access vio's
  1885. //so we will ensure that this cannot happen JLJ 21Apr98 6June98
  1886. if(VolData.MftZoneStart > VolData.TotalClusters-1){
  1887. VolData.MftZoneStart = VolData.TotalClusters-1;
  1888. }
  1889. if(VolData.MftZoneEnd < VolData.MftZoneStart){
  1890. VolData.MftZoneEnd = VolData.MftZoneStart;
  1891. }
  1892. if(VolData.MftZoneEnd > VolData.TotalClusters-1){
  1893. VolData.MftZoneEnd = VolData.TotalClusters-1;
  1894. }
  1895. //0.1E00 Calulate a few values in VolData from the volume data.
  1896. VolData.FreeSpace = VolumeData.FreeClusters.QuadPart * VolData.BytesPerCluster; //Number of free bytes on the drive.
  1897. VolData.UsedSpace = (VolData.TotalClusters * VolData.BytesPerCluster) - VolData.FreeSpace; //Number of used bytes on the drive.
  1898. VolData.UsedClusters = VolData.TotalClusters - VolumeData.FreeClusters.QuadPart; //Number of used clusters on the disk.
  1899. VolData.SectorsPerCluster = VolData.BytesPerCluster / VolData.BytesPerSector; //Number of Sectors per cluster
  1900. VolData.MftStartOffset = VolData.MftStartLcn * VolData.BytesPerCluster; //How many bytes into the disk the MFT starts on.
  1901. VolData.TotalFileRecords = VolumeData.MftValidDataLength.QuadPart / VolData.BytesPerFRS; //Total number of flie records in use.
  1902. VolData.FileRecordNumber = VolData.TotalFileRecords; //Start at the last file record on the disk.
  1903. VolData.CenterOfDisk = VolData.TotalClusters / 2; //Find the center cluster on the disk.
  1904. //0.0E00 Compute number of bits needed for bitmap, rounded up to the nearest 32-bit number.
  1905. VolData.BitmapSize = (((VolData.TotalClusters /32) +
  1906. ((VolData.TotalClusters % 32) ? 1 : 0)) + 1) * 32;
  1907. VolData.BootOptimizeBeginClusterExclude = 0;
  1908. VolData.BootOptimizeEndClusterExclude = 0;
  1909. if (IsBootVolume(VolData.cDrive)) {
  1910. //get the registry value for BootOptimizeBeginClusterExclude
  1911. HKEY hValue = NULL;
  1912. DWORD dwRegValueSize = 0;
  1913. long ret = 0;
  1914. TCHAR cRegValue[100];
  1915. // get Boot Optimize Begin Cluster Exclude from registry
  1916. dwRegValueSize = sizeof(cRegValue);
  1917. ret = GetRegValue(
  1918. &hValue,
  1919. BOOT_OPTIMIZE_REGISTRY_PATH,
  1920. BOOT_OPTIMIZE_REGISTRY_LCNSTARTLOCATION,
  1921. cRegValue,
  1922. &dwRegValueSize);
  1923. RegCloseKey(hValue);
  1924. //check to see if the key exists, else exit from routine
  1925. if (ret == ERROR_SUCCESS) {
  1926. VolData.BootOptimizeBeginClusterExclude = _ttoi(cRegValue);
  1927. }
  1928. // get Boot Optimize End Cluster Exclude from registry
  1929. hValue = NULL;
  1930. dwRegValueSize = sizeof(cRegValue);
  1931. ret = GetRegValue(
  1932. &hValue,
  1933. BOOT_OPTIMIZE_REGISTRY_PATH,
  1934. BOOT_OPTIMIZE_REGISTRY_LCNENDLOCATION,
  1935. cRegValue,
  1936. &dwRegValueSize);
  1937. RegCloseKey(hValue);
  1938. //check to see if the key exists, else exit from routine
  1939. if (ret == ERROR_SUCCESS) {
  1940. VolData.BootOptimizeEndClusterExclude = _ttoi(cRegValue);
  1941. }
  1942. }
  1943. return TRUE;
  1944. }
  1945. /*****************************************************************************************************************
  1946. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  1947. ROUTINE DESCRIPTION:
  1948. Get the next file record that is in use..
  1949. INPUT + OUTPUT:
  1950. IN hVolume - The handle to the volume we're getting the FRS from.
  1951. IN OUT pFileRecordNumber - What file record number is desired. It's a pointer so we can write in what
  1952. file record number is actually obtained.
  1953. OUT pFrs - Where to write the file record after it is read.
  1954. IN uBytesPerFRS - The number of bytes per FRS.
  1955. GLOBALS:
  1956. None.
  1957. RETURN:
  1958. TRUE = Success
  1959. FALSE = Failure
  1960. */
  1961. BOOL
  1962. GetInUseFrs(
  1963. IN HANDLE hVolume,
  1964. IN OUT LONGLONG* pFileRecordNumber,
  1965. OUT FILE_RECORD_SEGMENT_HEADER* pFrs,
  1966. IN ULONG uBytesPerFRS
  1967. )
  1968. {
  1969. NTFS_FILE_RECORD_INPUT_BUFFER NtfsFileRecordInputBuffer;
  1970. PNTFS_FILE_RECORD_OUTPUT_BUFFER pNtfsFileRecordOutputBuffer;
  1971. PUCHAR pFileRecord;
  1972. ULONG BytesReturned = 0;
  1973. require(hVolume);
  1974. require(uBytesPerFRS);
  1975. //0.1E00 Make a pointer to a global buffer for the file records.
  1976. pNtfsFileRecordOutputBuffer = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)&FileRecordOutputBuffer;
  1977. //0.1E00 Get a pointer to the file record in it.
  1978. pFileRecord = (PUCHAR)&pNtfsFileRecordOutputBuffer->FileRecordBuffer;
  1979. //0.1E00 Set the number of the file record we wish to read.
  1980. NtfsFileRecordInputBuffer.FileReferenceNumber.QuadPart = *pFileRecordNumber;
  1981. //0.1E00 Set the number of bytes to read.
  1982. pNtfsFileRecordOutputBuffer->FileRecordLength = (ULONG)uBytesPerFRS;
  1983. //0.1E00 Do the read via our hooks in the OS.
  1984. EF(ESDeviceIoControl(
  1985. hVolume,
  1986. FSCTL_GET_NTFS_FILE_RECORD,
  1987. &NtfsFileRecordInputBuffer,
  1988. sizeof(NtfsFileRecordInputBuffer),
  1989. pNtfsFileRecordOutputBuffer,
  1990. sizeof(FileRecordOutputBuffer),
  1991. &BytesReturned,
  1992. NULL));
  1993. //0.1E00 Copy the memory out of the file record buffer.
  1994. CopyMemory(pFrs, pFileRecord, (ULONG)uBytesPerFRS);
  1995. //0.1E00 Copy out the file record number that was actually obtained.
  1996. *pFileRecordNumber = (LONGLONG)pNtfsFileRecordOutputBuffer->FileReferenceNumber.QuadPart;
  1997. return TRUE;
  1998. }
  1999. /*****************************************************************************************************************
  2000. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  2001. ROUTINE DESCRIPTION:
  2002. This routine builds the full path for a file from its file record
  2003. INPUT + OUTPUT:
  2004. None.
  2005. GLOBALS:
  2006. IN OUT Various VolData fields.
  2007. RETURN:
  2008. TRUE = Success
  2009. FALSE = Failure
  2010. */
  2011. BOOL
  2012. GetNtfsFilePath(
  2013. )
  2014. {
  2015. TCHAR cName[MAX_PATH+1];
  2016. LONGLONG SaveFileRecordNumber;
  2017. LONGLONG ParentFileRecordNumber;
  2018. HANDLE hFrs = NULL;
  2019. FILE_RECORD_SEGMENT_HEADER* pFrs = NULL;
  2020. static LONGLONG LastPathFrs = 0;
  2021. #ifdef OFFLINEDK
  2022. // todo max_path UCHAR* pMftBitmap = NULL;
  2023. FILE_RECORD_SEGMENT_HEADER* pFileRecord = (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord;
  2024. #endif
  2025. //0.0E00 Get file's name and file record number of its parent directory-
  2026. EF(GetNameFromFileRecord((FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord, cName, &ParentFileRecordNumber));
  2027. if (VolData.bMFTCorrupt) {
  2028. return FALSE;
  2029. }
  2030. //0.0E00 If this file is in the same dir as the last file was, use the last file's path
  2031. if ((ParentFileRecordNumber == LastPathFrs) && (VolData.vFileName.GetLength() != 0)){
  2032. //0.1E00 Find the last slash in the previous file name.
  2033. TCHAR *pFileName = wcsrchr(VolData.vFileName.GetBuffer(), L'\\');
  2034. //0.1E00 Error if no slash.
  2035. EF_ASSERT(pFileName);
  2036. // move past the backslash and put a NULL
  2037. pFileName++;
  2038. *pFileName = (TCHAR) NULL;
  2039. // append the new file name
  2040. VolData.vFileName += cName;
  2041. //0.1E00 We have our path so return.
  2042. return TRUE;
  2043. }
  2044. // Save the parent's FRS # in case we can
  2045. // detect in the above if-block that we are in the same sub-directory
  2046. LastPathFrs = ParentFileRecordNumber;
  2047. //0.0E00 If this file's dir is the root, tack the drive letter to the beginning and return
  2048. if(ParentFileRecordNumber == ROOT_FILE_NAME_INDEX_NUMBER){
  2049. //0.1E00 Save our name.
  2050. // start with the volume name (guid or UNC)
  2051. VolData.vFileName = VolData.cVolumeName;
  2052. //0.1E00 Reformat our name with the drive letter.
  2053. // tack on the file name
  2054. VolData.vFileName.AddChar(L'\\');
  2055. //
  2056. // Append the file name. Note that for the root directory, we don't
  2057. // want to append the filename ("."), since we won't be able to get
  2058. // a handle to the directory then.
  2059. //
  2060. if (VolData.FileRecordNumber != ROOT_FILE_NAME_INDEX_NUMBER) {
  2061. VolData.vFileName += cName;
  2062. }
  2063. //0.1E00 We have our path so return.
  2064. return TRUE;
  2065. }
  2066. // we need to rebuild the entire path, so start off with the file name
  2067. VolData.vFileName = cName;
  2068. //0.0E00 Allocate and lock a buffer for the parent directory's file record
  2069. EF(AllocateMemory((DWORD)(VolData.BytesPerFRS + VolData.BytesPerSector), &hFrs, (void**)&pFrs));
  2070. BOOL bReturnValue = FALSE; // assume an error
  2071. VString tmpPath;
  2072. //0.0E00 Loop while moving up the tree until we hit the root.
  2073. while(ParentFileRecordNumber != ROOT_FILE_NAME_INDEX_NUMBER){
  2074. //0.1E00 Save the parent's file record number.
  2075. SaveFileRecordNumber = ParentFileRecordNumber;
  2076. #if OFFLINEDK
  2077. //0.0E00 Get parent dir's File Record Segment
  2078. if (!GetFrs(&ParentFileRecordNumber, VolData.pMftExtentList, pMftBitmap, VolData.pMftBuffer, pFrs)){
  2079. EH(FALSE);
  2080. //__leave;
  2081. goto END;
  2082. }
  2083. #else
  2084. //0.0E00 Get parent dir's File Record Segment
  2085. if (!GetInUseFrs(VolData.hVolume, &ParentFileRecordNumber, pFrs, (ULONG)VolData.BytesPerFRS)){
  2086. EH(FALSE);
  2087. //__leave;
  2088. goto END;
  2089. }
  2090. #endif
  2091. //0.1E00 Error out if we didn't get the parent's File Record Segment
  2092. if (ParentFileRecordNumber != SaveFileRecordNumber){
  2093. EH_ASSERT(FALSE);
  2094. //__leave;
  2095. goto END;
  2096. }
  2097. //0.0E00 Get parent dir's name and its parent's file record number
  2098. if (!GetNameFromFileRecord(pFrs, cName, &ParentFileRecordNumber)){
  2099. if (VolData.bMFTCorrupt) {
  2100. bReturnValue = FALSE;
  2101. }
  2102. EH(FALSE);
  2103. //__leave;
  2104. goto END;
  2105. }
  2106. //0.0E00 Append prior path to current dir name
  2107. wcscat(cName, L"\\");
  2108. // start off with the dir we just got
  2109. tmpPath = cName;
  2110. // add the existing path to create a complete path
  2111. tmpPath += VolData.vFileName;
  2112. // assign that back to the final resting place
  2113. VolData.vFileName = tmpPath;
  2114. }
  2115. // pre-pend the volume name
  2116. tmpPath = VolData.cVolumeName;
  2117. tmpPath.AddChar(L'\\');
  2118. tmpPath += VolData.vFileName;
  2119. // assign that back to the final resting place
  2120. VolData.vFileName = tmpPath;
  2121. bReturnValue = TRUE; // no errors
  2122. END:
  2123. if(hFrs) {
  2124. EH_ASSERT(GlobalUnlock(hFrs) == FALSE);
  2125. EH_ASSERT(GlobalFree(hFrs) == NULL);
  2126. }
  2127. return bReturnValue;
  2128. }
  2129. /*****************************************************************************************************************
  2130. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  2131. ROUTINE DESCRIPTION:
  2132. This routine gets the wide character NTFS long name from a file record, and returns it as a character string.
  2133. This also returns it's parent file record number.
  2134. INPUT + OUTPUT:
  2135. IN pFrs - Pointer to buffer containing file's file record
  2136. OUT pcName - Pointer to character buffer to hold the name from this file record
  2137. IN pParentFileRecordNumber - pointer to file record number of parent dir
  2138. GLOBALS:
  2139. IN VolData.BytesPerFRS - The number of bytes in a file record segment.
  2140. RETURN:
  2141. TRUE = Success
  2142. FALSE = Failure
  2143. */
  2144. BOOL
  2145. GetNameFromFileRecord(
  2146. IN FILE_RECORD_SEGMENT_HEADER* pFrs,
  2147. OUT TCHAR* pcName,
  2148. IN LONGLONG* pParentFileRecordNumber
  2149. )
  2150. {
  2151. PATTRIBUTE_RECORD_HEADER pArh = NULL;
  2152. PFILE_NAME pFileNameStruc = NULL;
  2153. PFILE_NAME pLastFileNameStruc = NULL;
  2154. PWCHAR pwName = NULL;
  2155. int i;
  2156. ATTRIBUTE_LIST_ENTRY* pAle = NULL; //The attribute entry we're looking at now (only used for huge and mega files).
  2157. ATTRIBUTE_LIST_ENTRY* pAleStart = NULL; //The start of the attribute list (only used for huge and mega files).
  2158. HANDLE hAle = NULL; //Handle to memory for attribute list if it was nonresident and had to be read off the disk.
  2159. LONGLONG SaveFrn = 0; //Used to compare the FRN before and after getting a new FRS to make sure we got the one we want.
  2160. DWORD dwAttrListLength = 0; //The length of the attribute list in a huge or mega file.
  2161. HANDLE hLocalFRS=NULL;
  2162. FILE_RECORD_SEGMENT_HEADER* pLocalFRS=NULL;
  2163. FILE_RECORD_SEGMENT_HEADER* pLocalFRSSave=NULL;
  2164. LONGLONG llLocalFRN = 0;
  2165. BOOL bReturnValue = FALSE; // assume an error condition
  2166. //1.0E00 The pointer to the current MFT record better not be NULL...
  2167. EF_ASSERT(pFrs);
  2168. __try {
  2169. //1.1E00 Look for an $ATTRIBUTE_LIST first
  2170. if(!FindAttributeByType($ATTRIBUTE_LIST, pFrs, &pArh, (ULONG)VolData.BytesPerFRS)){
  2171. //1.1E00 This paragraph handles the minimal case; this is where
  2172. //both $FILE_NAME attributes are in the base MFT record
  2173. //1.1E00 Find the filename attribute; error if it can't be found
  2174. if (!FindAttributeByType($FILE_NAME, pFrs, &pArh, (ULONG)VolData.BytesPerFRS)){
  2175. EH(FALSE);
  2176. __leave;
  2177. }
  2178. //1.1E00 Find the NTFS long name attribute
  2179. while(TRUE){
  2180. //1.1E00 Get pointer to name structure in name attribute
  2181. pFileNameStruc = (PFILE_NAME)((char*)pArh+pArh->Form.Resident.ValueOffset);
  2182. //1.1E00 If this is the NTFS name attribute then we found it, so break
  2183. //Only bits 1 and 2 have meaning in the Flags:
  2184. // Neither bits set: Long Name Only, use this one
  2185. // Bit 0x01 only: Long file name, but 8.3 is also available, keep looking and you'll find it
  2186. // Bit 0x02 only: 8.3 name, but long file name is also available, keep looking
  2187. // Both bits: This is both the NTFS and 8.3 name (long name IS 8.3 format)
  2188. if(pFileNameStruc->Flags & FILE_NAME_NTFS || pFileNameStruc->Flags == 0){
  2189. break;
  2190. }
  2191. //1.1E00 if we didn't get the NTFS filename, null the pointer
  2192. //so we realize it at the end of the loop
  2193. pFileNameStruc = NULL;
  2194. //1.1E00 Update our pointer to the next attribute.
  2195. pArh = (PATTRIBUTE_RECORD_HEADER)((char*)pArh + pArh->RecordLength);
  2196. //1.1E00 find the next name attribute
  2197. while(pArh < (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + (ULONG)VolData.BytesPerFRS) &&
  2198. pArh->TypeCode != $FILE_NAME &&
  2199. pArh->TypeCode != $END){
  2200. //1.1E00 We did not find our attribute, so go to the next.
  2201. pArh = (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pArh + pArh->RecordLength);
  2202. }
  2203. //1.1E00 Error if attribute not found
  2204. if(pArh->TypeCode != $FILE_NAME){
  2205. EH(FALSE);
  2206. __leave;
  2207. }
  2208. } // end while (TRUE)
  2209. } // end if no $ATTRIBUTE_LIST found
  2210. // 1.1E00 we come here if an $ATTRIBUTE_LIST was found
  2211. // (resident or non-resident)
  2212. else{
  2213. //1.1E00 This (much longer) paragraph handles the case where
  2214. //either $FILE_NAME attribute is not within the base MFT record.
  2215. //Where it actually can be found depends on whether the
  2216. //ATTRIBUTE_LIST attribute lists are resident (within the base
  2217. //MFT record) or non-resident (within external randomly-allocated
  2218. //clusters).
  2219. //1.1E00 If the $ATTRIBUTE_LIST is nonresident get the entire
  2220. //attribute list from the disk before proceeding.
  2221. if(pArh->FormCode == NONRESIDENT_FORM){
  2222. //1.1E00 Load the attribute extents from the disk (this allocate memory)
  2223. LoadExtentDataToMem(pArh, &hAle, &dwAttrListLength);
  2224. //1.1E00 Get a pointer to the allocated memory.
  2225. pAleStart = (ATTRIBUTE_LIST_ENTRY *) GlobalLock(hAle);
  2226. if (!pAleStart){
  2227. EH(FALSE);
  2228. __leave;
  2229. }
  2230. }
  2231. //1.1E00 If it was a resident attribute list, then the length
  2232. //of the attribute list can be determined from the attribute.
  2233. else{
  2234. pAleStart = (ATTRIBUTE_LIST_ENTRY*)(pArh->Form.Resident.ValueOffset + (UCHAR*)pArh);
  2235. dwAttrListLength = pArh->Form.Resident.ValueLength;
  2236. }
  2237. //1.1E00 Start at the beginning of the attribute list.
  2238. pAle = pAleStart;
  2239. while(TRUE) {
  2240. //1.1E00 find the next name attribute
  2241. while(pAle < (ATTRIBUTE_LIST_ENTRY*)((PUCHAR)pAleStart + (ULONG)dwAttrListLength) &&
  2242. pAle->AttributeTypeCode != $FILE_NAME &&
  2243. pAle->AttributeTypeCode != $END){
  2244. //1.1E00 We did not find our attribute, so go to the next.
  2245. pAle = (ATTRIBUTE_LIST_ENTRY*)((PUCHAR)pAle + pAle->RecordLength);
  2246. }
  2247. //The attribute list must end with a $END.
  2248. if (pAle >= (ATTRIBUTE_LIST_ENTRY*)((PUCHAR)pAleStart + (ULONG)dwAttrListLength)) {
  2249. VolData.bMFTCorrupt = TRUE;
  2250. bReturnValue = FALSE;
  2251. __leave;
  2252. }
  2253. //1.1E00 Error if attribute not found
  2254. if(pAle->AttributeTypeCode != $FILE_NAME){
  2255. __leave;
  2256. }
  2257. //1.1E00 grab the FRS# out of the attribute list
  2258. llLocalFRN = (pAle->SegmentReference.LowPart | (pAle->SegmentReference.HighPart << (sizeof(ULONG)*8)));
  2259. //1.1E00 if we make it this far, we'll need a buffer for
  2260. //examining MFT records so, only get the buffer if we haven't
  2261. //already gotten one
  2262. if (hLocalFRS == NULL) {
  2263. //1.1E00 Allocate and lock a buffer for the file record
  2264. if (!AllocateMemory((DWORD)(VolData.BytesPerFRS + VolData.BytesPerSector),
  2265. &hLocalFRS, (void**)&pLocalFRS)){
  2266. EH(FALSE);
  2267. __leave;
  2268. }
  2269. //1.1E00 Save the pointer to we can ensure the right
  2270. //pointer is always passed to the MFT record get routine
  2271. pLocalFRSSave = pLocalFRS;
  2272. }
  2273. else {
  2274. //1.1E00 if we already got the buffer, refresh the pointer
  2275. pLocalFRS = pLocalFRSSave;
  2276. }
  2277. #if OFFLINEDK
  2278. //1.1E00 Get the filename's File Record Segment
  2279. EF(GetFrs(&llLocalFRN, VolData.pMftExtentList, pMftBitmap, VolData.pMftBuffer, pLocalFrs));
  2280. #else
  2281. //1.1E00 Get the filename's File Record Segment
  2282. if (!GetInUseFrs(VolData.hVolume, &llLocalFRN, pLocalFRS, (ULONG)VolData.BytesPerFRS)){
  2283. EH(FALSE);
  2284. __leave;
  2285. }
  2286. #endif
  2287. //1.1E00 if we didn't get the NTFS filename, null the pointer
  2288. //so we realize we didn't get it at the end of the loop
  2289. pFileNameStruc = NULL;
  2290. //1.1E00 Find the filename attribute; error if it can't be found
  2291. if (!FindAttributeByType($FILE_NAME, pLocalFRS, &pArh, (ULONG)VolData.BytesPerFRS)) {
  2292. EH(FALSE);
  2293. __leave;
  2294. }
  2295. //1.1E00 Find the NTFS long name attribute
  2296. while(TRUE){
  2297. //1.1E00 Get pointer to name structure in name attribute
  2298. pFileNameStruc = (PFILE_NAME)((char*)pArh+pArh->Form.Resident.ValueOffset);
  2299. //1.1E00 If this is the NTFS name attribute then we found it, so break
  2300. //Only bits 1 and 2 have meaning in the Flags:
  2301. // Neither bits set: Long Name Only, use this one
  2302. // Bit 0x01 only: Long file name, but 8.3 is also available, keep looking and you'll find it
  2303. // Bit 0x02 only: 8.3 name, but long file name is also available, keep looking
  2304. // Both bits: This is both the NTFS and 8.3 name (long name IS 8.3 format)
  2305. if(pFileNameStruc->Flags & FILE_NAME_NTFS || pFileNameStruc->Flags == 0){
  2306. break;
  2307. }
  2308. //1.1E00 if we didn't get the NTFS filename, null the
  2309. //pointer so we realize it at the end of the loop
  2310. pFileNameStruc = NULL;
  2311. //1.1E00 Update our pointer to the next attribute.
  2312. pArh = (PATTRIBUTE_RECORD_HEADER)((char*)pArh + pArh->RecordLength);
  2313. //1.1E00 find the next name attribute
  2314. while(pArh < (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pLocalFRS + (ULONG)VolData.BytesPerFRS) &&
  2315. pArh->TypeCode != $FILE_NAME &&
  2316. pArh->TypeCode != $END){
  2317. //1.1E00 We did not find our attribute, so go to the next.
  2318. pArh = (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pArh + pArh->RecordLength);
  2319. }
  2320. //1.1E00 Error if attribute not found
  2321. if(pArh->TypeCode != $FILE_NAME){
  2322. break;
  2323. }
  2324. } // end while (TRUE)
  2325. //1.1E00 break out of loop if we found a NTFS filename
  2326. if (pFileNameStruc != NULL) {
  2327. break;
  2328. }
  2329. //1.1E00 We did not find our attribute, so go to the next.
  2330. pAle = (ATTRIBUTE_LIST_ENTRY*)((PUCHAR)pAle + pAle->RecordLength);
  2331. } // end while (TRUE)
  2332. } // end else (if $ATTRIBUTE_LIST present)
  2333. //1.1E00 This is the main point of this routine...we should never get
  2334. //here with a NULL pointer to the FileName in the MFT record
  2335. if (!pFileNameStruc){
  2336. EH_ASSERT(FALSE);
  2337. __leave;
  2338. }
  2339. //0.0E00 Get pointer to wide character name field
  2340. pwName = (PWCHAR)&pFileNameStruc->FileName;
  2341. //0.0E00 Copy the filename over.
  2342. for(i=0; (i<pFileNameStruc->FileNameLength) && (i<MAX_PATH) && (*pwName!=TEXT('\\'));pcName[i++] = (TCHAR)*pwName++)
  2343. ;
  2344. //EF_ASSERT(*pwName!=TEXT('\\'));
  2345. if (*pwName == TEXT('\\')){
  2346. EH_ASSERT(FALSE);
  2347. __leave;
  2348. }
  2349. //EF_ASSERT(i<MAX_PATH);
  2350. if (i >= MAX_PATH){
  2351. EH_ASSERT(FALSE);
  2352. __leave;
  2353. }
  2354. //0.0E00 Null terminate the string
  2355. pcName[i] = 0;
  2356. //0.0E00 Return the parent's file record number
  2357. *pParentFileRecordNumber = pFileNameStruc->ParentDirectory.LowPart + (pFileNameStruc->ParentDirectory.HighPart<<32);
  2358. //0.0E00 Success!
  2359. bReturnValue = TRUE;
  2360. }
  2361. //1.1E00 on exit, make sure we've released any memory fetched.
  2362. __finally {
  2363. if(pLocalFRS) {
  2364. EH_ASSERT(GlobalUnlock(hLocalFRS) == FALSE);
  2365. EH_ASSERT(GlobalFree(hLocalFRS) == NULL);
  2366. }
  2367. if(hAle) {
  2368. EH_ASSERT(GlobalUnlock(hAle) == FALSE);
  2369. EH_ASSERT(GlobalFree(hAle) == NULL);
  2370. }
  2371. }
  2372. return bReturnValue;
  2373. }
  2374. BOOL
  2375. GetNextNtfsFile(
  2376. IN CONST PRTL_GENERIC_TABLE pTable,
  2377. IN CONST BOOLEAN Restart,
  2378. IN CONST LONGLONG ClusterCount,
  2379. IN CONST PFILE_LIST_ENTRY pEntry
  2380. )
  2381. {
  2382. LONGLONG fileRecordNumber = 0;
  2383. PFILE_LIST_ENTRY pFileListEntry = NULL;
  2384. BOOLEAN bNext = (Restart ? FALSE : TRUE),
  2385. done = TRUE;
  2386. static PVOID pRestartKey = NULL;
  2387. static ULONG ulDeleteCount = 0;
  2388. static FILE_LIST_ENTRY entry;
  2389. if (Restart) {
  2390. if (pEntry) {
  2391. CopyMemory(&entry, pEntry, sizeof(FILE_LIST_ENTRY));
  2392. }
  2393. else {
  2394. pFileListEntry = (PFILE_LIST_ENTRY) RtlEnumerateGenericTableAvl(pTable, TRUE);
  2395. if (pFileListEntry) {
  2396. CopyMemory(&entry, pFileListEntry, sizeof(FILE_LIST_ENTRY));
  2397. }
  2398. else {
  2399. ZeroMemory(&entry, sizeof(FILE_LIST_ENTRY));
  2400. }
  2401. }
  2402. pRestartKey = NULL;
  2403. }
  2404. do {
  2405. done = TRUE;
  2406. //
  2407. // Find the next entry in the table
  2408. //
  2409. do {
  2410. pFileListEntry = (PFILE_LIST_ENTRY) RtlEnumerateGenericTableLikeADirectory(
  2411. pTable,
  2412. NULL,
  2413. NULL,
  2414. bNext,
  2415. &pRestartKey,
  2416. &ulDeleteCount,
  2417. &entry);
  2418. bNext = TRUE;
  2419. } while ((ClusterCount) && (pFileListEntry) && (pFileListEntry->ClusterCount >= ClusterCount));
  2420. VolData.pFileListEntry = pFileListEntry;
  2421. if (!pFileListEntry) {
  2422. //
  2423. // We're done with this enumeration
  2424. //
  2425. return FALSE;
  2426. }
  2427. //
  2428. // Keep a copy of this entry around, in case it gets deleted and we need
  2429. // to carry on with our enumeration. (Usually, the enumeration will
  2430. // continue on the basis of pRestartKey, but if that key is deleted,
  2431. // it continues based on this).
  2432. //
  2433. CopyMemory(&entry, pFileListEntry, sizeof(FILE_LIST_ENTRY));
  2434. //1.0E00 Since a file was found in one of these lists, set the return values accordingly.
  2435. VolData.LastStartingLcn = VolData.StartingLcn = pFileListEntry->StartingLcn;
  2436. VolData.NumberOfClusters = pFileListEntry->ClusterCount;
  2437. //Get the file record number of the file from the list.
  2438. VolData.FileRecordNumber = pFileListEntry->FileRecordNumber;
  2439. // Set the flags
  2440. VolData.bDirectory = (pFileListEntry->Flags & FLE_DIRECTORY) ? TRUE : FALSE;
  2441. VolData.bFragmented = (pFileListEntry->Flags & FLE_FRAGMENTED) ? TRUE : FALSE;
  2442. VolData.bBootOptimiseFile = (pFileListEntry->Flags & FLE_BOOTOPTIMISE) ? TRUE : FALSE;
  2443. //1.0E00 This is used to compare with the FileRecordNumber returned by GetInUseFrs below to see if a file was deleted.
  2444. fileRecordNumber = VolData.FileRecordNumber;
  2445. //Check to see if the file record number is out of range.
  2446. if(fileRecordNumber > VolData.TotalFileRecords){
  2447. Trace(log, "GetNextNtfsFile: Invalid File Record Number: %x (TotalFileRecords:%x)",
  2448. fileRecordNumber, VolData.FileRecordNumber);
  2449. return FALSE;
  2450. }
  2451. EF(GetInUseFrs(VolData.hVolume, &VolData.FileRecordNumber, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord, (ULONG)VolData.BytesPerFRS));
  2452. //0.0E00 Check to see if the file has been deleted out from under us.
  2453. if (VolData.FileRecordNumber != fileRecordNumber) {
  2454. //If GetInUseFrs doesn't find the file record number, then it has been deleted.
  2455. //Remove this file from our list
  2456. RtlDeleteElementGenericTable(pTable, pFileListEntry);
  2457. done = FALSE;
  2458. }
  2459. } while (!done);
  2460. return TRUE;
  2461. }
  2462. BOOL
  2463. UpdateFileTables(
  2464. IN OUT PRTL_GENERIC_TABLE pFragmentedTable,
  2465. IN OUT PRTL_GENERIC_TABLE pContiguousTable
  2466. )
  2467. {
  2468. FILE_EXTENT_HEADER* pFileExtentHeader;
  2469. PFREE_SPACE_ENTRY pFreeSpaceEntry = VolData.pFreeSpaceEntry;
  2470. PFILE_LIST_ENTRY pFileListEntry = VolData.pFileListEntry;
  2471. FILE_LIST_ENTRY NewEntry;
  2472. PVOID p = NULL;
  2473. BOOLEAN bNewElement = TRUE;
  2474. //Update the information on this file.
  2475. pFileListEntry->StartingLcn = pFreeSpaceEntry->StartingLcn;
  2476. memcpy(&NewEntry, pFileListEntry, sizeof(FILE_LIST_ENTRY));
  2477. NewEntry.Flags &= ~FLE_FRAGMENTED;
  2478. // 1. Update the FreeSpaceEntry's count
  2479. pFreeSpaceEntry->StartingLcn += VolData.NumberOfClusters;
  2480. pFreeSpaceEntry->ClusterCount -= VolData.NumberOfClusters;
  2481. // 2. Add this file to the contiguous-files table
  2482. p = RtlInsertElementGenericTable(
  2483. pContiguousTable,
  2484. (PVOID) &NewEntry,
  2485. sizeof(FILE_LIST_ENTRY),
  2486. &bNewElement);
  2487. if (!p) {
  2488. // An allocation failed
  2489. return FALSE;
  2490. };
  2491. // 3. Remove this file from the fragmented-files table
  2492. bNewElement = RtlDeleteElementGenericTable(pFragmentedTable, pFileListEntry);
  2493. if (!bNewElement) {
  2494. assert(FALSE);
  2495. }
  2496. VolData.pFileListEntry = NULL;
  2497. return TRUE;
  2498. }
  2499. BOOL
  2500. UpdateInFileList(
  2501. )
  2502. {
  2503. LONGLONG EarliestStartingLcn = 0xFFFFFFFFFFFFFFFF;
  2504. FILE_EXTENT_HEADER* pFileExtentHeader;
  2505. FILE_LIST_ENTRY* pFileListEntry;
  2506. //Get a pointer to the entry.
  2507. pFileListEntry = VolData.pFileListEntry;
  2508. //Sanity check the file record number. They should be the same.
  2509. EF(pFileListEntry->FileRecordNumber == (UINT)VolData.FileRecordNumber);
  2510. //Get a pointer to the file extent header.
  2511. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList;
  2512. //Get the earliest starting lcn for the file which we'll use in our algorithms to determine which file to move next.
  2513. EF(GetLowestStartingLcn(&EarliestStartingLcn, pFileExtentHeader));
  2514. //Update the information on this file.
  2515. pFileListEntry->StartingLcn = EarliestStartingLcn;
  2516. pFileListEntry->ExcessExtentCount = (UINT)VolData.NumberOfFragments - pFileExtentHeader->NumberOfStreams; //Only count *excess* extents since otherwise files with multiple streams would be "fragmented".
  2517. //Set or clear the fragmented flag depending on whether the file is fragmented or not.
  2518. if(VolData.bFragmented){
  2519. //Set the fragmented flag.
  2520. pFileListEntry->Flags |= FLE_FRAGMENTED;
  2521. }
  2522. else{
  2523. //Clear the fragmented flag.
  2524. pFileListEntry->Flags &= ~FLE_FRAGMENTED;
  2525. }
  2526. //Set or clear the directory flag depending on whether the file is a directory or not.
  2527. if(VolData.bDirectory){
  2528. //Set the directory flag.
  2529. pFileListEntry->Flags |= FLE_DIRECTORY;
  2530. }
  2531. else{
  2532. //Clear the directory flag.
  2533. pFileListEntry->Flags &= ~FLE_DIRECTORY;
  2534. }
  2535. return TRUE;
  2536. }
  2537. /*****************************************************************************************************************
  2538. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  2539. ROUTINE DESCRIPTION:
  2540. Opens a file on an NTFS volume.
  2541. INPUT + OUTPUT:
  2542. None.
  2543. GLOBALS:
  2544. IN OUT Various VolData fields.
  2545. RETURN:
  2546. TRUE = Success
  2547. FALSE = Failure
  2548. */
  2549. BOOL
  2550. OpenNtfsFile(
  2551. )
  2552. {
  2553. TCHAR cPath[MAX_PATH] = {0};
  2554. DWORD Error;
  2555. // TCHAR cString[300];
  2556. //0.0E00 Display the File path and name.
  2557. // Message(TEXT("NtfsSubs::OpenNtfsFile"), -1, VolData.cFileName);
  2558. //0.0E00 Display File Number, number of extents and number of fragments.
  2559. // wsprintf(cString,
  2560. // TEXT(" FileNumber = 0x%lX Extents = 0x%lX Fragments = 0x%lX"),
  2561. // (ULONG)VolData.FileRecordNumber,
  2562. // (ULONG)VolData.NumberOfExtents,
  2563. // (ULONG)VolData.NumberOfFragments);
  2564. // Message(TEXT("NtfsSubs::OpenNtfsFile"), -1, cString);
  2565. // wsprintf(cString, TEXT(" %s file at Lcn 0x%lX for Cluster Count of 0x%lX"),
  2566. // (VolData.bFragmented == TRUE) ? TEXT("Fragmented") : TEXT("Contiguous"),
  2567. // (ULONG)VolData.StartingLcn,
  2568. // (ULONG)VolData.NumberOfClusters);
  2569. // Message(TEXT("NtfsSubs::OpenNtfsFile"), -1, cString);
  2570. if(VolData.bDirectory){
  2571. Error = 0;
  2572. }
  2573. if(VolData.hFile != INVALID_HANDLE_VALUE){
  2574. CloseHandle(VolData.hFile);
  2575. }
  2576. if (VolData.vFileName.IsEmpty()) {
  2577. VolData.hFile = INVALID_HANDLE_VALUE;
  2578. return FALSE;
  2579. }
  2580. DWORD dwCreationDisposition;
  2581. dwCreationDisposition = FILE_FLAG_BACKUP_SEMANTICS | FILE_OPEN_REPARSE_POINT; //sks createfile parameters
  2582. // Get a transparent handle to the file.
  2583. VolData.hFile = CreateFile(
  2584. VolData.vFileName.GetBuffer(),
  2585. FILE_READ_ATTRIBUTES | SYNCHRONIZE, //sks bug #184735
  2586. 0,
  2587. NULL,
  2588. OPEN_EXISTING,
  2589. dwCreationDisposition,
  2590. NULL);
  2591. if(VolData.hFile == INVALID_HANDLE_VALUE) {
  2592. //
  2593. // Handle the special $Extend files: see bug 303250
  2594. //
  2595. PWSTR pwszFileName = VolData.vFileName.GetBuffer();
  2596. if (!pwszFileName) {
  2597. return FALSE;
  2598. }
  2599. if (_tcsstr(pwszFileName, TEXT("\\$Extend")) != (PTCHAR) NULL){
  2600. if (_tcsstr(pwszFileName, TEXT("\\$Quota")) != (PTCHAR) NULL){
  2601. VolData.vFileName += L":$Q:$INDEX_ALLOCATION";
  2602. }
  2603. else if (_tcsstr(pwszFileName, TEXT("\\$ObjId")) != (PTCHAR) NULL){
  2604. VolData.vFileName += L":$O:$INDEX_ALLOCATION";
  2605. }
  2606. else if (_tcsstr(pwszFileName, TEXT("\\$Reparse")) != (PTCHAR) NULL){
  2607. VolData.vFileName += L":$R:$INDEX_ALLOCATION";
  2608. }
  2609. VolData.hFile = CreateFile(
  2610. VolData.vFileName.GetBuffer(),
  2611. FILE_READ_ATTRIBUTES | SYNCHRONIZE, //sks bug #184735
  2612. 0,
  2613. NULL,
  2614. OPEN_EXISTING,
  2615. dwCreationDisposition,
  2616. NULL);
  2617. }
  2618. }
  2619. if(VolData.hFile == INVALID_HANDLE_VALUE) {
  2620. Error = GetLastError();
  2621. Message(TEXT("OpenNtfsFile - CreateFile"), GetLastError(), ESICompressFilePath(VolData.vFileName.GetBuffer()));
  2622. SetLastError(Error);
  2623. return FALSE; // Try later.
  2624. }
  2625. Message(ESICompressFilePath(VolData.vFileName.GetBuffer()), -1, NULL);
  2626. return TRUE;
  2627. }
  2628. /*****************************************************************************************************************
  2629. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  2630. ROUTINE DESCRIPTION:
  2631. Gets the extent list for the system files -- the MFT, MFT2, and the Uppercase file.
  2632. INPUT + OUTPUT:
  2633. None.
  2634. GLOBALS:
  2635. IN VolData.hVolume - The handle to the volume.
  2636. IN VolData.BytesPerFRS - The number of bytes in a file record.
  2637. IN OUT VolData.pExtentList - Where to put the extents for all the system files.
  2638. IN VolData.NumberOfExtents - The number of extents in the extent list.
  2639. IN VolData.ExtentListAlloced - The number of bytes allocated for the extent list.
  2640. RETURN:
  2641. TRUE = Success
  2642. FALSE = Failure
  2643. */
  2644. BOOL
  2645. GetSystemsExtentList(
  2646. )
  2647. {
  2648. LONGLONG StartingLcn;
  2649. LONGLONG EndingLcn;
  2650. LONGLONG FileRecordNumber;
  2651. STREAM_EXTENT_HEADER* pStreamExtentHeader;
  2652. EXTENT_LIST* pExtents;
  2653. //1.0E00 Get the start of the Mft mirror
  2654. //Get the FRS.
  2655. FileRecordNumber = 1;
  2656. EF(GetInUseFrs(VolData.hVolume, &FileRecordNumber, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord, (ULONG)VolData.BytesPerFRS));
  2657. EF_ASSERT(FileRecordNumber == 1);
  2658. VolData.FileRecordNumber = FileRecordNumber;
  2659. //Get the extent list.
  2660. EF(GetStreamExtentsByNameAndType(TEXT(""), $DATA, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord));
  2661. //Get a pointer to the stream header.
  2662. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)VolData.pExtentList;
  2663. //Make sure this is the first stream.
  2664. EF_ASSERT(pStreamExtentHeader->StreamNumber == 0);
  2665. //Make sure there are extents in this stream.
  2666. EF_ASSERT(pStreamExtentHeader->ExtentCount);
  2667. //Get a pointer to the extents in this stream.
  2668. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  2669. //The first extent points to the beginning of the mft mirror.
  2670. StartingLcn = pExtents->StartingLcn;
  2671. //1.0E00 Get the end of the Upcase file
  2672. //Get the FRS.
  2673. FileRecordNumber = 0x0a;
  2674. EF(GetInUseFrs(VolData.hVolume, &FileRecordNumber, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord, (ULONG)VolData.BytesPerFRS));
  2675. EF_ASSERT(FileRecordNumber == 0x0a);
  2676. VolData.FileRecordNumber = FileRecordNumber;
  2677. //Get the extent list.
  2678. EF(GetStreamExtentsByNameAndType(TEXT(""), $DATA, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord));
  2679. //Get a pointer to the stream header.
  2680. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)VolData.pExtentList;
  2681. //Make sure this is the first stream.
  2682. EF_ASSERT(pStreamExtentHeader->StreamNumber == 0);
  2683. //Make sure there are extents in this stream.
  2684. EF_ASSERT(pStreamExtentHeader->ExtentCount);
  2685. //Get a pointer to the extents in this stream.
  2686. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  2687. //The first extent points to the whole upcase file which ends off the MFT mirror section.
  2688. EndingLcn = pExtents->StartingLcn + pExtents->ClusterCount;
  2689. //1.0E00 Get the Mft extent list
  2690. //Get the FRS
  2691. FileRecordNumber = 0;
  2692. EF(GetInUseFrs(VolData.hVolume, &FileRecordNumber, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord, (ULONG)VolData.BytesPerFRS));
  2693. EF_ASSERT(FileRecordNumber == 0);
  2694. VolData.FileRecordNumber = FileRecordNumber;
  2695. //Get the extent list.
  2696. EF(GetStreamExtentsByNameAndType(TEXT(""), $DATA, (FILE_RECORD_SEGMENT_HEADER*)VolData.pFileRecord));
  2697. //Get a pointer to the stream header.
  2698. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)VolData.pExtentList;
  2699. //Make sure this is the first stream.
  2700. EF_ASSERT(pStreamExtentHeader->StreamNumber == 0);
  2701. //Make sure there are extents in this stream.
  2702. EF_ASSERT(pStreamExtentHeader->ExtentCount);
  2703. //Get a pointer to the extents in this stream.
  2704. pExtents = (EXTENT_LIST*)((UCHAR*)pStreamExtentHeader + sizeof(STREAM_EXTENT_HEADER));
  2705. //1.0E00 Extend the MFT extent list to add an extent for MFT2
  2706. pStreamExtentHeader->ExtentCount ++;
  2707. //0.0E00 Extend the extent list buffer as necessary
  2708. if((sizeof(STREAM_EXTENT_HEADER) + pStreamExtentHeader->ExtentCount*sizeof(EXTENT_LIST)) > (size_t) VolData.ExtentListAlloced){
  2709. //If so, realloc it larger, 64K at a time.
  2710. EF(AllocateMemory((UINT)VolData.ExtentListAlloced + 0x10000, &VolData.hExtentList, (void**)&VolData.pExtentList));
  2711. VolData.ExtentListAlloced += 0x10000;
  2712. //Reset pointers that might have changed.
  2713. pStreamExtentHeader = (STREAM_EXTENT_HEADER*)VolData.pExtentList;
  2714. pExtents = (EXTENT_LIST*)((UCHAR*)VolData.pExtentList + sizeof(STREAM_EXTENT_HEADER));
  2715. }
  2716. //1.0E00 Add an extent for Mirror thru Upcase
  2717. pExtents[pStreamExtentHeader->ExtentCount - 1].StartingLcn = StartingLcn;
  2718. pExtents[pStreamExtentHeader->ExtentCount - 1].ClusterCount = EndingLcn - StartingLcn;
  2719. //We do not increase ExcessExtents because the MFT2 region is not considered an excess fragment.
  2720. //We do need to change the allocated length for the system extents, however, to encompass the second extent.
  2721. pStreamExtentHeader->AllocatedLength += pExtents[pStreamExtentHeader->ExtentCount - 1].ClusterCount * VolData.BytesPerCluster;
  2722. //Increase the file size for the system extents list since the purpose of the file size in this case is to tell the user how many bytes
  2723. //the system files occupy, and there is no similar statistic that can be obtained from the file system to conform to.
  2724. pStreamExtentHeader->FileSize += pExtents[pStreamExtentHeader->ExtentCount - 1].ClusterCount * VolData.BytesPerCluster;
  2725. //The caller code will want to know how many extents there are here.
  2726. VolData.MftNumberOfExtents = pStreamExtentHeader->ExtentCount;
  2727. return TRUE;
  2728. }
  2729. /*****************************************************************************************************************
  2730. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  2731. ROUTINE DESCRIPTION:
  2732. Given an extent list it adds it to a file list.
  2733. INPUT + OUTPUT:
  2734. OUT pList - A pointer to the file list.
  2735. IN pListIndex - An index of how far the list has been written into.
  2736. IN ListEntries - The number of entries in the extent list.
  2737. IN FileRecordNumber - The File Record Number for this file.
  2738. IN pExtentList - The extent list.
  2739. GLOBALS:
  2740. IN VolData.bDirectory - Whether or not this file a directory.
  2741. IN VolData.NumberOfFragments - The number of fragments in this file.
  2742. IN VolData.NumberOfExtents - The number of extents in this file.
  2743. IN VolData.FileSize - The size of the file.
  2744. RETURN:
  2745. TRUE = Success
  2746. FALSE = Failure
  2747. */
  2748. BOOL
  2749. AddFileToListNtfs(
  2750. IN PRTL_GENERIC_TABLE Table,
  2751. IN LONGLONG FileRecordNumber
  2752. )
  2753. {
  2754. FILE_LIST_ENTRY FileListEntry;
  2755. FILE_EXTENT_HEADER* pFileExtentHeader = NULL;
  2756. BOOLEAN bNewElement = FALSE;
  2757. PVOID p = NULL;
  2758. //Get a pointer to the file extent header.
  2759. pFileExtentHeader = (FILE_EXTENT_HEADER*)VolData.pExtentList;
  2760. //Fill in the file info
  2761. FileListEntry.StartingLcn = VolData.StartingLcn;
  2762. FileListEntry.FileRecordNumber = FileRecordNumber;
  2763. FileListEntry.ExcessExtentCount = (UINT)VolData.NumberOfFragments - pFileExtentHeader->NumberOfStreams; //Only count *excess* extents since otherwise files with multiple streams would be "fragmented".
  2764. FileListEntry.ClusterCount = VolData.NumberOfClusters;
  2765. FileListEntry.Flags = 0;
  2766. //Set or clear the fragmented flag depending on whether the file is fragmented or not.
  2767. if(VolData.bFragmented){
  2768. //Set the fragmented flag.
  2769. FileListEntry.Flags |= FLE_FRAGMENTED;
  2770. }
  2771. else{
  2772. //Clear the fragmented flag.
  2773. FileListEntry.Flags &= ~FLE_FRAGMENTED;
  2774. }
  2775. //Set or clear the directory flag depending on whether the file is a directory or not.
  2776. if(VolData.bDirectory){
  2777. //Set the directory flag.
  2778. FileListEntry.Flags |= FLE_DIRECTORY;
  2779. }
  2780. else{
  2781. //Clear the directory flag.
  2782. FileListEntry.Flags &= ~FLE_DIRECTORY;
  2783. }
  2784. if (VolData.bBootOptimiseFile) {
  2785. FileListEntry.Flags |= FLE_BOOTOPTIMISE;
  2786. }
  2787. else {
  2788. FileListEntry.Flags &= ~FLE_BOOTOPTIMISE;
  2789. }
  2790. p = RtlInsertElementGenericTable(
  2791. Table,
  2792. (PVOID) &FileListEntry,
  2793. sizeof(FILE_LIST_ENTRY),
  2794. &bNewElement);
  2795. if (!p) {
  2796. //
  2797. // An allocation failed
  2798. //
  2799. return FALSE;
  2800. };
  2801. // This better be a recognised as a new element. If it isn't, something's
  2802. // wrong with our compare routine
  2803. // ASSERT(bNewElement);
  2804. return TRUE;
  2805. }
  2806. /*****************************************************************************************************************
  2807. COPYRIGHT 2001 Microsoft Corporation and Executive Software International, Inc.
  2808. ROUTINE DESCRIPTION:
  2809. Locate an attribute in a File Record Segment
  2810. INPUT + OUTPUT:
  2811. IN TypeCode - TypeCode of the attribute to find
  2812. IN pFrs - Pointer to the File Record Segment
  2813. OUT ppArh - Returns the pointer to the attribute
  2814. IN uBytesPerFrs - The number of bytes in an FRS.
  2815. GLOBALS:
  2816. None.
  2817. RETURN:
  2818. TRUE = Success
  2819. FALSE = Failure
  2820. */
  2821. BOOL
  2822. FindAttributeByType(
  2823. IN ATTRIBUTE_TYPE_CODE TypeCode,
  2824. IN PFILE_RECORD_SEGMENT_HEADER pFrs,
  2825. OUT PATTRIBUTE_RECORD_HEADER* ppArh,
  2826. IN ULONG uBytesPerFRS
  2827. )
  2828. {
  2829. PATTRIBUTE_RECORD_HEADER pArh = (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + pFrs->FirstAttributeOffset);
  2830. require(pFrs);
  2831. require(ppArh);
  2832. require(uBytesPerFRS);
  2833. //0.0E00 Seach through the attributes in this file record for the first one of the requested type
  2834. //0.1E00 Make sure our attribute pointer doesn't go beyond the end of the FRS and check if this is the attribute we're
  2835. //looking for or the end of the record.
  2836. while(pArh < (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + uBytesPerFRS) &&
  2837. pArh->TypeCode != TypeCode &&
  2838. pArh->TypeCode != $END){
  2839. //0.1E00 We did not find our attribute, so go to the next.
  2840. pArh = (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pArh + pArh->RecordLength);
  2841. }
  2842. if (pArh >= (ATTRIBUTE_RECORD_HEADER*)((PUCHAR)pFrs + uBytesPerFRS)) {
  2843. VolData.bMFTCorrupt = TRUE;
  2844. return FALSE;
  2845. }
  2846. //0.0E00 Error if attribute not found
  2847. if(pArh->TypeCode != TypeCode){
  2848. return FALSE;
  2849. }
  2850. //0.0E00 Return pointer to the attribute
  2851. *ppArh = pArh;
  2852. return TRUE;
  2853. }