Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4471 lines
119 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. NtfsBoot.c
  5. Abstract:
  6. This module implements the Ntfs boot file system used by the operating system
  7. loader.
  8. Author:
  9. Gary Kimura [GaryKi] 10-April-1992
  10. Revision History:
  11. --*/
  12. //
  13. // Stuff to get around the fact that we include both Fat, Hpfs, and Ntfs include
  14. // environments
  15. //
  16. #define _FAT_
  17. #define _HPFS_
  18. #define _CVF_
  19. #define VBO ULONG
  20. #define LBO ULONG
  21. #define BIOS_PARAMETER_BLOCK ULONG
  22. #define CVF_LAYOUT ULONG
  23. #define CVF_HEADER ULONG
  24. #define COMPONENT_LOCATION ULONG
  25. #define PCVF_FAT_EXTENSIONS PCHAR
  26. typedef struct DIRENT {
  27. char Garbage[32];
  28. } DIRENT; // sizeof = 32
  29. #include "bootlib.h"
  30. #include "stdio.h"
  31. #include "blcache.h"
  32. #include "bootstatus.h"
  33. BOOTFS_INFO NtfsBootFsInfo={L"ntfs"};
  34. #undef VBO
  35. #undef LBO
  36. #undef BIOS_PARAMETER_BLOCK
  37. #undef DIRENT
  38. #include "ntfs.h"
  39. int Line = 0;
  40. VOID NtfsPrint( IN PCHAR FormatString, ... )
  41. { va_list arglist; CHAR text[78+1]; ULONG Count,Length;
  42. va_start(arglist,FormatString);
  43. Length = _vsnprintf(text,sizeof(text),FormatString,arglist);
  44. text[78] = 0;
  45. ArcWrite(ARC_CONSOLE_OUTPUT,text,Length,&Count);
  46. va_end(arglist);
  47. }
  48. VOID NtfsGetChar(VOID) { UCHAR c; ULONG count; ArcRead(ARC_CONSOLE_INPUT,&c,1,&count); }
  49. #define ReadConsole(c) { \
  50. UCHAR Key=0; ULONG Count; \
  51. while (Key != c) { \
  52. if (ArcGetReadStatus(BlConsoleInDeviceId) == ESUCCESS) { \
  53. ArcRead(BlConsoleInDeviceId, &Key, sizeof(Key), &Count); \
  54. } \
  55. } \
  56. }
  57. #define Pause ReadConsole( ' ' )
  58. #if FALSE
  59. #define PausedPrint(x) { \
  60. NtfsPrint x; \
  61. Line++; \
  62. if (Line >= 20) { \
  63. NtfsPrint( ">" ); \
  64. Pause; \
  65. Line = 0; \
  66. } \
  67. }
  68. #else
  69. #define PausedPrint(x)
  70. #endif
  71. #define ToUpper(C) ((((C) >= 'a') && ((C) <= 'z')) ? (C) - 'a' + 'A' : (C))
  72. #define DereferenceFileRecord(idx) { \
  73. NtfsFileRecordBufferPinned[idx] -= 1; \
  74. if (NtfsFileRecordBufferPinned[idx] & 0xFF00) { \
  75. PausedPrint(( "NtfsFileRecordBufferPinned[%x]=%x\r\n", \
  76. idx, NtfsFileRecordBufferPinned[idx])); \
  77. } \
  78. }
  79. //
  80. // Low level disk read routines
  81. //
  82. //
  83. // VOID
  84. // ReadDisk (
  85. // IN ULONG DeviceId,
  86. // IN LONGLONG Lbo,
  87. // IN ULONG ByteCount,
  88. // IN OUT PVOID Buffer,
  89. // IN BOOLEAN CacheNewData
  90. // );
  91. //
  92. ARC_STATUS
  93. NtfsReadDisk (
  94. IN ULONG DeviceId,
  95. IN LONGLONG Lbo,
  96. IN ULONG ByteCount,
  97. IN OUT PVOID Buffer,
  98. IN BOOLEAN CacheNewData
  99. );
  100. #define ReadDisk(A,B,C,D,E) { ARC_STATUS _s; \
  101. if ((_s = NtfsReadDisk(A,B,C,D,E)) != ESUCCESS) {return _s;} \
  102. }
  103. //
  104. // Low level disk write routines
  105. //
  106. //
  107. // VOID
  108. // WriteDisk (
  109. // IN ULONG DeviceId,
  110. // IN LONGLONG Lbo,
  111. // IN ULONG ByteCount,
  112. // IN OUT PVOID Buffer
  113. // );
  114. //
  115. ARC_STATUS
  116. NtfsWriteDisk (
  117. IN ULONG DeviceId,
  118. IN LONGLONG Lbo,
  119. IN ULONG ByteCount,
  120. IN OUT PVOID Buffer
  121. );
  122. #define WriteDisk(A,B,C,D) { ARC_STATUS _s; \
  123. if ((_s = NtfsWriteDisk(A,B,C,D)) != ESUCCESS) {return _s;} \
  124. }
  125. //
  126. // Attribute lookup and read routines
  127. //
  128. //
  129. // VOID
  130. // LookupAttribute (
  131. // IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  132. // IN LONGLONG FileRecord,
  133. // IN ATTRIBUTE_TYPE_CODE TypeCode,
  134. // OUT PBOOLEAN FoundAttribute,
  135. // OUT PNTFS_ATTRIBUTE_CONTEXT AttributeContext
  136. // );
  137. //
  138. // VOID
  139. // ReadAttribute (
  140. // IN PNTFS_STRUCTURE_CONTEXT StructureContext,
  141. // IN PNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  142. // IN VBO Vbo,
  143. // IN ULONG Length,
  144. // IN PVOID Buffer
  145. // );
  146. //
  147. // VOID
  148. // ReadAndDecodeFileRecord (
  149. // IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  150. // IN LONGLONG FileRecord,
  151. // OUT PULONG Index
  152. // );
  153. //
  154. // VOID
  155. // DecodeUsa (
  156. // IN PVOID UsaBuffer,
  157. // IN ULONG Length
  158. // );
  159. //
  160. ARC_STATUS
  161. NtfsLookupAttribute(
  162. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  163. IN LONGLONG FileRecord,
  164. IN ATTRIBUTE_TYPE_CODE TypeCode,
  165. OUT PBOOLEAN FoundAttribute,
  166. OUT PNTFS_ATTRIBUTE_CONTEXT AttributeContext
  167. );
  168. ARC_STATUS
  169. NtfsReadResidentAttribute (
  170. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  171. IN PCNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  172. IN VBO Vbo,
  173. IN ULONG Length,
  174. IN PVOID Buffer
  175. );
  176. ARC_STATUS
  177. NtfsReadNonresidentAttribute (
  178. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  179. IN PCNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  180. IN VBO Vbo,
  181. IN ULONG Length,
  182. IN PVOID Buffer
  183. );
  184. ARC_STATUS
  185. NtfsWriteNonresidentAttribute (
  186. IN PNTFS_STRUCTURE_CONTEXT StructureContext,
  187. IN PNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  188. IN VBO Vbo,
  189. IN ULONG Length,
  190. IN PVOID Buffer
  191. );
  192. ARC_STATUS
  193. NtfsReadAndDecodeFileRecord (
  194. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  195. IN LONGLONG FileRecord,
  196. OUT PULONG Index
  197. );
  198. ARC_STATUS
  199. NtfsDecodeUsa (
  200. IN PVOID UsaBuffer,
  201. IN ULONG Length
  202. );
  203. #define LookupAttribute(A,B,C,D,E) { ARC_STATUS _s; \
  204. if ((_s = NtfsLookupAttribute(A,B,C,D,E)) != ESUCCESS) {return _s;} \
  205. }
  206. #define ReadAttribute(A,B,C,D,E) { ARC_STATUS _s; \
  207. if ((B)->IsAttributeResident) { \
  208. if ((_s = NtfsReadResidentAttribute(A,B,C,D,E)) != ESUCCESS) {return _s;} \
  209. } else { \
  210. if ((_s = NtfsReadNonresidentAttribute(A,B,C,D,E)) != ESUCCESS) {return _s;} \
  211. } \
  212. }
  213. #define ReadAndDecodeFileRecord(A,B,C) { ARC_STATUS _s; \
  214. if ((_s = NtfsReadAndDecodeFileRecord(A,B,C)) != ESUCCESS) { return _s; } \
  215. }
  216. #define DecodeUsa(A,B) { ARC_STATUS _s; \
  217. if ((_s = NtfsDecodeUsa(A,B)) != ESUCCESS) {return _s;} \
  218. }
  219. //
  220. // Directory and index lookup routines
  221. //
  222. //
  223. // VOID
  224. // SearchForFileName (
  225. // IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  226. // IN CSTRING FileName,
  227. // IN OUT PLONGLONG FileRecord,
  228. // OUT PBOOLEAN FoundFileName,
  229. // OUT PBOOLEAN IsDirectory
  230. // );
  231. //
  232. // VOID
  233. // IsRecordAllocated (
  234. // IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  235. // IN PCNTFS_ATTRIBUTE_CONTEXT AllocationBitmap,
  236. // IN ULONG BitOffset,
  237. // OUT PBOOLEAN IsAllocated
  238. // );
  239. //
  240. ARC_STATUS
  241. NtfsSearchForFileName (
  242. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  243. IN CSTRING FileName,
  244. IN OUT PLONGLONG FileRecord,
  245. OUT PBOOLEAN FoundFileName,
  246. OUT PBOOLEAN IsDirectory
  247. );
  248. ARC_STATUS
  249. NtfsIsRecordAllocated (
  250. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  251. IN PCNTFS_ATTRIBUTE_CONTEXT AllocationBitmap,
  252. IN ULONG BitOffset,
  253. OUT PBOOLEAN IsAllocated
  254. );
  255. ARC_STATUS
  256. NtfsLinearDirectoryScan(
  257. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  258. IN CSTRING FileName,
  259. IN OUT PLONGLONG FileRecord,
  260. OUT PBOOLEAN Found,
  261. OUT PBOOLEAN IsDirectory
  262. );
  263. ARC_STATUS
  264. NtfsInexactSortedDirectoryScan(
  265. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  266. IN CSTRING FileName,
  267. IN OUT PLONGLONG FileRecord,
  268. OUT PBOOLEAN Found,
  269. OUT PBOOLEAN IsDirectory
  270. );
  271. #define SearchForFileName(A,B,C,D,E) \
  272. { \
  273. ARC_STATUS _s; \
  274. if ((_s = NtfsSearchForFileName(A,B,C,D,E)) != ESUCCESS) { \
  275. return _s; \
  276. } \
  277. }
  278. #define IsRecordAllocated(A,B,C,D) \
  279. { \
  280. ARC_STATUS _s; \
  281. if ((_s = NtfsIsRecordAllocated(A,B,C,D)) != ESUCCESS) { \
  282. return _s; \
  283. } \
  284. }
  285. #define LinearDirectoryScan(A,B,C,D,E) \
  286. { \
  287. ARC_STATUS _s; \
  288. if ((_s = NtfsLinearDirectoryScan(A,B,C,D,E)) != ESUCCESS) {\
  289. return _s; \
  290. } \
  291. }
  292. #define InexactSortedDirectoryScan(A,B,C,D,E) \
  293. { \
  294. ARC_STATUS _s; \
  295. if ((_s = NtfsInexactSortedDirectoryScan(A,B,C,D,E)) != ESUCCESS) {\
  296. return _s; \
  297. } \
  298. }
  299. //
  300. // Mcb support routines
  301. //
  302. //
  303. // VOID
  304. // LoadMcb (
  305. // IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  306. // IN PCNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  307. // IN VBO Vbo,
  308. // IN PNTFS_MCB Mcb
  309. // );
  310. //
  311. // VOID
  312. // VboToLbo (
  313. // IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  314. // IN PCNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  315. // IN VBO Vbo,
  316. // OUT PLBO Lbo,
  317. // OUT PULONG ByteCount
  318. // );
  319. //
  320. // VOID
  321. // DecodeRetrievalInformation (
  322. // IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  323. // IN PNTFS_MCB Mcb,
  324. // IN VCN Vcn,
  325. // IN PATTRIBUTE_RECORD_HEADER AttributeHeader
  326. // );
  327. //
  328. ARC_STATUS
  329. NtfsLoadMcb (
  330. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  331. IN PCNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  332. IN VBO Vbo,
  333. IN PNTFS_MCB Mcb
  334. );
  335. ARC_STATUS
  336. NtfsVboToLbo (
  337. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  338. IN PCNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  339. IN VBO Vbo,
  340. OUT PLBO Lbo,
  341. OUT PULONG ByteCount
  342. );
  343. ARC_STATUS
  344. NtfsDecodeRetrievalInformation (
  345. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  346. IN PNTFS_MCB Mcb,
  347. IN VCN Vcn,
  348. IN PATTRIBUTE_RECORD_HEADER AttributeHeader
  349. );
  350. #define LoadMcb(A,B,C,D) { ARC_STATUS _s; \
  351. if ((_s = NtfsLoadMcb(A,B,C,D)) != ESUCCESS) {return _s;} \
  352. }
  353. #define VboToLbo(A,B,C,D,E) { ARC_STATUS _s; \
  354. if ((_s = NtfsVboToLbo(A,B,C,D,E)) != ESUCCESS) {return _s;} \
  355. }
  356. #define DecodeRetrievalInformation(A,B,C,D) { ARC_STATUS _s; \
  357. if ((_s = NtfsDecodeRetrievalInformation(A,B,C,D)) != ESUCCESS) {return _s;} \
  358. }
  359. //
  360. // Miscellaneous routines
  361. //
  362. VOID
  363. NtfsFirstComponent (
  364. IN OUT PCSTRING String,
  365. OUT PCSTRING FirstComponent
  366. );
  367. int
  368. NtfsCompareName (
  369. IN CSTRING AnsiString,
  370. IN UNICODE_STRING UnicodeString
  371. );
  372. VOID
  373. NtfsInvalidateCacheEntries(
  374. IN ULONG DeviceId
  375. );
  376. //
  377. // VOID
  378. // FileReferenceToLargeInteger (
  379. // IN PFILE_REFERENCE FileReference,
  380. // OUT PLONGLONG LargeInteger
  381. // );
  382. //
  383. // VOID
  384. // InitializeAttributeContext (
  385. // IN PNTFS_STRUCTURE_CONTEXT StructureContext,
  386. // IN PVOID FileRecordBuffer,
  387. // IN PVOID AttributeHeader,
  388. // IN LONGLONG FileRecord,
  389. // OUT PNTFS_ATTRIBUTE_CONTEXT AttributeContext
  390. // );
  391. //
  392. #define FileReferenceToLargeInteger(FR,LI) { \
  393. *(LI) = *(PLONGLONG)&(FR); \
  394. ((PFILE_REFERENCE)(LI))->SequenceNumber = 0; \
  395. }
  396. //
  397. //**** note that the code to get the compression engine will need to change
  398. //**** once the NTFS format changes
  399. //
  400. #define InitializeAttributeContext(SC,FRB,AH,FR,AC) { \
  401. (AC)->TypeCode = (AH)->TypeCode; \
  402. (AC)->FileRecord = (FR); \
  403. (AC)->FileRecordOffset = (USHORT)PtrOffset((FRB),(AH)); \
  404. if (((AC)->IsAttributeResident = ((AH)->FormCode == RESIDENT_FORM)) != 0) { \
  405. (AC)->DataSize = /*xxFromUlong*/((AH)->Form.Resident.ValueLength); \
  406. } else { \
  407. (AC)->DataSize = (AH)->Form.Nonresident.FileSize; \
  408. } \
  409. (AC)->CompressionFormat = COMPRESSION_FORMAT_NONE; \
  410. if ((AH)->Flags & ATTRIBUTE_FLAG_COMPRESSION_MASK) { \
  411. ULONG _i; \
  412. (AC)->CompressionFormat = COMPRESSION_FORMAT_LZNT1; \
  413. (AC)->CompressionUnit = (SC)->BytesPerCluster; \
  414. for (_i = 0; _i < (AH)->Form.Nonresident.CompressionUnit; _i += 1) { \
  415. (AC)->CompressionUnit *= 2; \
  416. } \
  417. } \
  418. }
  419. #define FlagOn(Flags,SingleFlag) ((BOOLEAN)(((Flags) & (SingleFlag)) != 0))
  420. #define SetFlag(Flags,SingleFlag) { (Flags) |= (SingleFlag); }
  421. #define ClearFlag(Flags,SingleFlag) { (Flags) &= ~(SingleFlag); }
  422. #define Add2Ptr(POINTER,INCREMENT) ((PVOID)((PUCHAR)(POINTER) + (INCREMENT)))
  423. #define PtrOffset(BASE,OFFSET) ((ULONG)((ULONG_PTR)(OFFSET) - (ULONG_PTR)(BASE)))
  424. #define Minimum(X,Y) ((X) < (Y) ? (X) : (Y))
  425. #define IsCharZero(C) (((C) & 0x000000ff) == 0x00000000)
  426. #define IsCharLtrZero(C) (((C) & 0x00000080) == 0x00000080)
  427. //
  428. // The following types and macros are used to help unpack the packed and misaligned
  429. // fields found in the Bios parameter block
  430. //
  431. typedef union _UCHAR1 { UCHAR Uchar[1]; UCHAR ForceAlignment; } UCHAR1, *PUCHAR1;
  432. typedef union _UCHAR2 { UCHAR Uchar[2]; USHORT ForceAlignment; } UCHAR2, *PUCHAR2;
  433. typedef union _UCHAR4 { UCHAR Uchar[4]; ULONG ForceAlignment; } UCHAR4, *PUCHAR4;
  434. //
  435. // This macro copies an unaligned src byte to an aligned dst byte
  436. //
  437. #define CopyUchar1(Dst,Src) { \
  438. *((UCHAR1 *)(Dst)) = *((UNALIGNED UCHAR1 *)(Src)); \
  439. }
  440. //
  441. // This macro copies an unaligned src word to an aligned dst word
  442. //
  443. #define CopyUchar2(Dst,Src) { \
  444. *((UCHAR2 *)(Dst)) = *((UNALIGNED UCHAR2 *)(Src)); \
  445. }
  446. //
  447. // This macro copies an unaligned src longword to an aligned dsr longword
  448. //
  449. #define CopyUchar4(Dst,Src) { \
  450. *((UCHAR4 *)(Dst)) = *((UNALIGNED UCHAR4 *)(Src)); \
  451. }
  452. //
  453. // Define global data.
  454. //
  455. ULONG LastMcb = 0;
  456. BOOLEAN FirstTime = TRUE;
  457. //
  458. // File entry table - This is a structure that provides entry to the NTFS
  459. // file system procedures. It is exported when a NTFS file structure
  460. // is recognized.
  461. //
  462. BL_DEVICE_ENTRY_TABLE NtfsDeviceEntryTable;
  463. //
  464. // These are the static buffers that we use when read file records and index
  465. // allocation buffers. To save ourselves some extra reads we will identify the
  466. // current file record by its Vbo within the mft.
  467. //
  468. #define BUFFER_COUNT (64)
  469. USHORT NtfsFileRecordBufferPinned[BUFFER_COUNT];
  470. VBO NtfsFileRecordBufferVbo[BUFFER_COUNT];
  471. PFILE_RECORD_SEGMENT_HEADER NtfsFileRecordBuffer[BUFFER_COUNT];
  472. PINDEX_ALLOCATION_BUFFER NtfsIndexAllocationBuffer;
  473. //
  474. // The following field are used to identify and store the cached
  475. // compressed buffer and its uncompressed equivalent. The first
  476. // two fields identifies the attribute stream, and the third field
  477. // identifies the Vbo within the attribute stream that we have
  478. // cached. The compressed and uncompressed buffer contains
  479. // the data.
  480. //
  481. LONGLONG NtfsCompressedFileRecord;
  482. USHORT NtfsCompressedOffset;
  483. ULONG NtfsCompressedVbo;
  484. PUCHAR NtfsCompressedBuffer;
  485. PUCHAR NtfsUncompressedBuffer;
  486. UCHAR NtfsBuffer0[MAXIMUM_FILE_RECORD_SIZE+256];
  487. UCHAR NtfsBuffer1[MAXIMUM_FILE_RECORD_SIZE+256];
  488. UCHAR NtfsBuffer2[MAXIMUM_INDEX_ALLOCATION_SIZE+256];
  489. UCHAR NtfsBuffer3[MAXIMUM_COMPRESSION_UNIT_SIZE+256];
  490. UCHAR NtfsBuffer4[MAXIMUM_COMPRESSION_UNIT_SIZE+256];
  491. //
  492. // The following is a simple prefix cache to speed up directory traversal
  493. //
  494. typedef struct {
  495. //
  496. // DeviceId used to for I/O. Serves as unique volume identifier
  497. //
  498. ULONG DeviceId;
  499. //
  500. // Parent file record of entry
  501. //
  502. LONGLONG ParentFileRecord;
  503. //
  504. // Name length and text of entry. This is already uppercased!
  505. //
  506. ULONG NameLength;
  507. UCHAR RelativeName[32];
  508. //
  509. // File record of name relative to parent
  510. //
  511. LONGLONG ChildFileRecord;
  512. } NTFS_CACHE_ENTRY;
  513. #define MAX_CACHE_ENTRIES 8
  514. NTFS_CACHE_ENTRY NtfsLinkCache[MAX_CACHE_ENTRIES];
  515. ULONG NtfsLinkCacheCount = 0;
  516. PBL_DEVICE_ENTRY_TABLE
  517. IsNtfsFileStructure (
  518. IN ULONG DeviceId,
  519. IN PVOID OpaqueStructureContext
  520. )
  521. /*++
  522. Routine Description:
  523. This routine determines if the partition on the specified channel contains an
  524. Ntfs file system volume.
  525. Arguments:
  526. DeviceId - Supplies the file table index for the device on which read operations
  527. are to be performed.
  528. StructureContext - Supplies a pointer to a Ntfs file structure context.
  529. Return Value:
  530. A pointer to the Ntfs entry table is returned if the partition is recognized as
  531. containing a Ntfs volume. Otherwise, NULL is returned.
  532. --*/
  533. {
  534. PNTFS_STRUCTURE_CONTEXT StructureContext = (PNTFS_STRUCTURE_CONTEXT)OpaqueStructureContext;
  535. PPACKED_BOOT_SECTOR BootSector;
  536. BIOS_PARAMETER_BLOCK Bpb;
  537. ULONG ClusterSize;
  538. ULONG FileRecordSize;
  539. PATTRIBUTE_RECORD_HEADER AttributeHeader;
  540. ULONG i;
  541. //
  542. // Clear the file system context block for the specified channel and initialize
  543. // the global buffer pointers that we use for buffering I/O
  544. //
  545. RtlZeroMemory(StructureContext, sizeof(NTFS_STRUCTURE_CONTEXT));
  546. //
  547. // Zero out the pinned buffer array because we start with nothing pinned
  548. // Also negate the vbo array to not let us get spooked with stale data
  549. //
  550. RtlZeroMemory( NtfsFileRecordBufferPinned, sizeof(NtfsFileRecordBufferPinned));
  551. for (i = 0; i < BUFFER_COUNT; i += 1) { NtfsFileRecordBufferVbo[i] = -1; }
  552. NtfsCompressedFileRecord = 0;
  553. NtfsCompressedOffset = 0;
  554. NtfsCompressedVbo = 0;
  555. //
  556. // Set up a local pointer that we will use to read in the boot sector and check
  557. // for an Ntfs partition. We will temporarily use the global file record buffer
  558. //
  559. BootSector = (PPACKED_BOOT_SECTOR)NtfsFileRecordBuffer[0];
  560. //
  561. // Now read in the boot sector and return null if we can't do the read
  562. //
  563. if (NtfsReadDisk(DeviceId, 0, sizeof(PACKED_BOOT_SECTOR), BootSector, CACHE_NEW_DATA) != ESUCCESS) {
  564. return NULL;
  565. }
  566. //
  567. // Unpack the Bios parameter block
  568. //
  569. NtfsUnpackBios( &Bpb, &BootSector->PackedBpb );
  570. //
  571. // Check if it is NTFS, by first checking the signature, then must be zero
  572. // fields, then the media type, and then sanity check the non zero fields.
  573. //
  574. if (RtlCompareMemory( &BootSector->Oem[0], "NTFS ", 8) != 8) {
  575. return NULL;
  576. }
  577. if ((Bpb.ReservedSectors != 0) ||
  578. (Bpb.Fats != 0) ||
  579. (Bpb.RootEntries != 0) ||
  580. (Bpb.Sectors != 0) ||
  581. (Bpb.SectorsPerFat != 0) ||
  582. (Bpb.LargeSectors != 0)) {
  583. return NULL;
  584. }
  585. if ((Bpb.Media != 0xf0) &&
  586. (Bpb.Media != 0xf8) &&
  587. (Bpb.Media != 0xf9) &&
  588. (Bpb.Media != 0xfc) &&
  589. (Bpb.Media != 0xfd) &&
  590. (Bpb.Media != 0xfe) &&
  591. (Bpb.Media != 0xff)) {
  592. return NULL;
  593. }
  594. if ((Bpb.BytesPerSector != 128) &&
  595. (Bpb.BytesPerSector != 256) &&
  596. (Bpb.BytesPerSector != 512) &&
  597. (Bpb.BytesPerSector != 1024) &&
  598. (Bpb.BytesPerSector != 2048)) {
  599. return NULL;
  600. }
  601. if ((Bpb.SectorsPerCluster != 1) &&
  602. (Bpb.SectorsPerCluster != 2) &&
  603. (Bpb.SectorsPerCluster != 4) &&
  604. (Bpb.SectorsPerCluster != 8) &&
  605. (Bpb.SectorsPerCluster != 16) &&
  606. (Bpb.SectorsPerCluster != 32) &&
  607. (Bpb.SectorsPerCluster != 64) &&
  608. (Bpb.SectorsPerCluster != 128)) {
  609. return NULL;
  610. }
  611. if ((BootSector->NumberSectors == 0) ||
  612. (BootSector->MftStartLcn == 0) ||
  613. (BootSector->Mft2StartLcn == 0) ||
  614. (BootSector->ClustersPerFileRecordSegment == 0) ||
  615. (BootSector->DefaultClustersPerIndexAllocationBuffer == 0)) {
  616. return NULL;
  617. }
  618. if ((BootSector->ClustersPerFileRecordSegment < 0) &&
  619. ((BootSector->ClustersPerFileRecordSegment > -9) ||
  620. (BootSector->ClustersPerFileRecordSegment < -31))) {
  621. return NULL;
  622. }
  623. //
  624. // So far the boot sector has checked out to be an NTFS partition so now compute
  625. // some of the volume constants.
  626. //
  627. StructureContext->DeviceId = DeviceId;
  628. StructureContext->BytesPerCluster =
  629. ClusterSize = Bpb.SectorsPerCluster * Bpb.BytesPerSector;
  630. //
  631. // If the number of clusters per file record is less than zero then the file record
  632. // size computed by using the negative of this number as a shift value.
  633. //
  634. if (BootSector->ClustersPerFileRecordSegment > 0) {
  635. StructureContext->BytesPerFileRecord =
  636. FileRecordSize = BootSector->ClustersPerFileRecordSegment * ClusterSize;
  637. } else {
  638. StructureContext->BytesPerFileRecord =
  639. FileRecordSize = 1 << (-1 * BootSector->ClustersPerFileRecordSegment);
  640. }
  641. //
  642. // Read in the base file record for the mft
  643. //
  644. if (NtfsReadDisk( DeviceId,
  645. /*xxXMul*/(BootSector->MftStartLcn * ClusterSize),
  646. FileRecordSize,
  647. NtfsFileRecordBuffer[0],
  648. CACHE_NEW_DATA) != ESUCCESS) {
  649. return NULL;
  650. }
  651. //
  652. // Decode Usa for the file record
  653. //
  654. if (NtfsDecodeUsa(NtfsFileRecordBuffer[0], FileRecordSize) != ESUCCESS) {
  655. return NULL;
  656. }
  657. //
  658. // Make sure the file record is in use
  659. //
  660. if (!FlagOn(NtfsFileRecordBuffer[0]->Flags, FILE_RECORD_SEGMENT_IN_USE)) {
  661. return NULL;
  662. }
  663. //
  664. // Search for the unnamed $data attribute header, if we reach $end then it is
  665. // an error
  666. //
  667. for (AttributeHeader = NtfsFirstAttribute( NtfsFileRecordBuffer[0] );
  668. (AttributeHeader->TypeCode != $DATA) || (AttributeHeader->NameLength != 0);
  669. AttributeHeader = NtfsGetNextRecord( AttributeHeader )) {
  670. if (AttributeHeader->TypeCode == $END) {
  671. return NULL;
  672. }
  673. }
  674. //
  675. // Make sure the $data attribute for the mft is non resident
  676. //
  677. if (AttributeHeader->FormCode != NONRESIDENT_FORM) {
  678. return NULL;
  679. }
  680. //
  681. // Now set the mft structure context up for later use
  682. //
  683. InitializeAttributeContext( StructureContext,
  684. NtfsFileRecordBuffer[0],
  685. AttributeHeader,
  686. 0,
  687. &StructureContext->MftAttributeContext );
  688. //
  689. // Now decipher the part of the Mcb that is stored in the file record
  690. //
  691. if (NtfsDecodeRetrievalInformation( StructureContext,
  692. &StructureContext->MftBaseMcb,
  693. 0,
  694. AttributeHeader ) != ESUCCESS) {
  695. return NULL;
  696. }
  697. //
  698. // We have finished initializing the structure context so now Initialize the
  699. // file entry table and return the address of the table.
  700. //
  701. NtfsDeviceEntryTable.Open = NtfsOpen;
  702. NtfsDeviceEntryTable.Close = NtfsClose;
  703. NtfsDeviceEntryTable.Read = NtfsRead;
  704. NtfsDeviceEntryTable.Seek = NtfsSeek;
  705. NtfsDeviceEntryTable.Write = NtfsWrite;
  706. NtfsDeviceEntryTable.GetFileInformation = NtfsGetFileInformation;
  707. NtfsDeviceEntryTable.SetFileInformation = NtfsSetFileInformation;
  708. NtfsDeviceEntryTable.BootFsInfo = &NtfsBootFsInfo;
  709. return &NtfsDeviceEntryTable;
  710. }
  711. ARC_STATUS
  712. NtfsClose (
  713. IN ULONG FileId
  714. )
  715. /*++
  716. Routine Description:
  717. This routine closes the file specified by the file id.
  718. Arguments:
  719. FileId - Supplies the file table index.
  720. Return Value:
  721. ESUCCESS if returned as the function value.
  722. --*/
  723. {
  724. //
  725. // Indicate that the file isn't open any longer
  726. //
  727. BlFileTable[FileId].Flags.Open = 0;
  728. //
  729. // And return to our caller
  730. //
  731. return ESUCCESS;
  732. }
  733. ARC_STATUS
  734. NtfsGetFileInformation (
  735. IN ULONG FileId,
  736. OUT FILE_INFORMATION * FIRMWARE_PTR Buffer
  737. )
  738. /*++
  739. Routine Description:
  740. This procedure returns to the user a buffer filled with file information
  741. Arguments:
  742. FileId - Supplies the File id for the operation
  743. Buffer - Supplies the buffer to receive the file information. Note that
  744. it must be large enough to hold the full file name
  745. Return Value:
  746. ESUCCESS is returned if the open operation is successful. Otherwise,
  747. an unsuccessful status is returned that describes the reason for failure.
  748. --*/
  749. {
  750. PBL_FILE_TABLE FileTableEntry;
  751. PNTFS_STRUCTURE_CONTEXT StructureContext;
  752. PNTFS_FILE_CONTEXT FileContext;
  753. NTFS_ATTRIBUTE_CONTEXT AttributeContext;
  754. BOOLEAN Found;
  755. STANDARD_INFORMATION StandardInformation;
  756. ULONG i;
  757. //
  758. // Setup some local references
  759. //
  760. FileTableEntry = &BlFileTable[FileId];
  761. StructureContext = (PNTFS_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  762. FileContext = &FileTableEntry->u.NtfsFileContext;
  763. //
  764. // Zero out the output buffer and fill in its non-zero values
  765. //
  766. RtlZeroMemory(Buffer, sizeof(FILE_INFORMATION));
  767. Buffer->EndingAddress.QuadPart = FileContext->DataSize;
  768. Buffer->CurrentPosition = FileTableEntry->Position;
  769. //
  770. // Locate and read in the standard information for the file. This will get us
  771. // the attributes for the file.
  772. //
  773. LookupAttribute( StructureContext,
  774. FileContext->FileRecord,
  775. $STANDARD_INFORMATION,
  776. &Found,
  777. &AttributeContext );
  778. if (!Found) { return EBADF; }
  779. ReadAttribute( StructureContext,
  780. &AttributeContext,
  781. 0,
  782. sizeof(STANDARD_INFORMATION),
  783. &StandardInformation );
  784. //
  785. // Now check for set bits in the standard information structure and set the
  786. // appropriate bits in the output buffer
  787. //
  788. if (FlagOn(StandardInformation.FileAttributes, FAT_DIRENT_ATTR_READ_ONLY)) {
  789. SetFlag(Buffer->Attributes, ArcReadOnlyFile);
  790. }
  791. if (FlagOn(StandardInformation.FileAttributes, FAT_DIRENT_ATTR_HIDDEN)) {
  792. SetFlag(Buffer->Attributes, ArcHiddenFile);
  793. }
  794. if (FlagOn(StandardInformation.FileAttributes, FAT_DIRENT_ATTR_SYSTEM)) {
  795. SetFlag(Buffer->Attributes, ArcSystemFile);
  796. }
  797. if (FlagOn(StandardInformation.FileAttributes, FAT_DIRENT_ATTR_ARCHIVE)) {
  798. SetFlag(Buffer->Attributes, ArcArchiveFile);
  799. }
  800. if (FlagOn(StandardInformation.FileAttributes, DUP_FILE_NAME_INDEX_PRESENT)) {
  801. SetFlag(Buffer->Attributes, ArcDirectoryFile);
  802. }
  803. //
  804. // Get the file name from the file table entry
  805. //
  806. Buffer->FileNameLength = FileTableEntry->FileNameLength;
  807. for (i = 0; i < FileTableEntry->FileNameLength; i += 1) {
  808. Buffer->FileName[i] = FileTableEntry->FileName[i];
  809. }
  810. //
  811. // And return to our caller
  812. //
  813. return ESUCCESS;
  814. }
  815. ARC_STATUS
  816. NtfsOpen (
  817. IN CHAR * FIRMWARE_PTR RWFileName,
  818. IN OPEN_MODE OpenMode,
  819. IN ULONG * FIRMWARE_PTR FileId
  820. )
  821. /*++
  822. Routine Description:
  823. This routine searches the root directory for a file matching FileName.
  824. If a match is found the dirent for the file is saved and the file is
  825. opened.
  826. Arguments:
  827. FileName - Supplies a pointer to a zero terminated file name.
  828. OpenMode - Supplies the mode of the open.
  829. FileId - Supplies a pointer to a variable that specifies the file
  830. table entry that is to be filled in if the open is successful.
  831. Return Value:
  832. ESUCCESS is returned if the open operation is successful. Otherwise,
  833. an unsuccessful status is returned that describes the reason for failure.
  834. --*/
  835. {
  836. const CHAR * FIRMWARE_PTR FileName = (const CHAR * FIRMWARE_PTR)RWFileName;
  837. PBL_FILE_TABLE FileTableEntry;
  838. PNTFS_STRUCTURE_CONTEXT StructureContext;
  839. PNTFS_FILE_CONTEXT FileContext;
  840. CSTRING PathName;
  841. CSTRING Name;
  842. LONGLONG FileRecord;
  843. BOOLEAN IsDirectory;
  844. BOOLEAN Found;
  845. PausedPrint(( "NtfsOpen(\"%s\")\r\n", FileName ));
  846. //
  847. // Load our local variables
  848. //
  849. FileTableEntry = &BlFileTable[*FileId];
  850. StructureContext = (PNTFS_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  851. FileContext = &FileTableEntry->u.NtfsFileContext;
  852. //
  853. // Zero out the file context and position information in the file table entry
  854. //
  855. FileTableEntry->Position.QuadPart = 0;
  856. RtlZeroMemory(FileContext, sizeof(NTFS_FILE_CONTEXT));
  857. //
  858. // Construct a file name descriptor from the input file name
  859. //
  860. RtlInitString( (PSTRING)&PathName, FileName );
  861. //
  862. // Open the root directory as our starting point, The root directory file
  863. // reference number is 5.
  864. //
  865. FileRecord = 5;
  866. IsDirectory = TRUE;
  867. //
  868. // While the path name has some characters left in it and current attribute
  869. // context is a directory we will continue our search
  870. //
  871. while ((PathName.Length > 0) && IsDirectory) {
  872. //
  873. // Extract the first component and search the directory for a match, but
  874. // first copy the first part to the file name buffer in the file table entry
  875. //
  876. if (PathName.Buffer[0] == '\\') {
  877. PathName.Buffer +=1;
  878. PathName.Length -=1;
  879. }
  880. for (FileTableEntry->FileNameLength = 0;
  881. (((USHORT)FileTableEntry->FileNameLength < PathName.Length) &&
  882. (PathName.Buffer[FileTableEntry->FileNameLength] != '\\'));
  883. FileTableEntry->FileNameLength += 1) {
  884. FileTableEntry->FileName[FileTableEntry->FileNameLength] =
  885. PathName.Buffer[FileTableEntry->FileNameLength];
  886. }
  887. NtfsFirstComponent( &PathName, &Name );
  888. //
  889. // Search for the name in the current directory
  890. //
  891. SearchForFileName( StructureContext,
  892. Name,
  893. &FileRecord,
  894. &Found,
  895. &IsDirectory );
  896. //
  897. // If we didn't find it then we should get out right now
  898. //
  899. if (!Found) { return ENOENT; }
  900. }
  901. //
  902. // At this point we have exhausted our pathname or we did not get a directory
  903. // Check if we didn't get a directory and we still have a name to crack
  904. //
  905. if (PathName.Length > 0) {
  906. return ENOTDIR;
  907. }
  908. //
  909. // Now FileRecord is the one we wanted to open. Check the various open modes
  910. // against what we have located
  911. //
  912. if (IsDirectory) {
  913. switch (OpenMode) {
  914. case ArcOpenDirectory:
  915. //
  916. // To open the directory we will lookup the index root as our file
  917. // context and then increment the appropriate counters.
  918. //
  919. LookupAttribute( StructureContext,
  920. FileRecord,
  921. $INDEX_ROOT,
  922. &Found,
  923. FileContext );
  924. if (!Found) { return EBADF; }
  925. FileTableEntry->Flags.Open = 1;
  926. FileTableEntry->Flags.Read = 1;
  927. return ESUCCESS;
  928. case ArcCreateDirectory:
  929. return EROFS;
  930. default:
  931. return EISDIR;
  932. }
  933. }
  934. switch (OpenMode) {
  935. case ArcOpenReadWrite:
  936. //
  937. // The only file allowed to be opened with write access is the hiberfil
  938. //
  939. if (!strstr(FileName, "\\hiberfil.sys") &&
  940. !strstr(FileName, BSD_FILE_NAME)) {
  941. return EROFS;
  942. }
  943. //
  944. // To open the file we will lookup the $data as our file context and then
  945. // increment the appropriate counters.
  946. //
  947. LookupAttribute( StructureContext,
  948. FileRecord,
  949. $DATA,
  950. &Found,
  951. FileContext );
  952. if (!Found) { return EBADF; }
  953. FileTableEntry->Flags.Open = 1;
  954. FileTableEntry->Flags.Read = 1;
  955. FileTableEntry->Flags.Write = 1;
  956. return ESUCCESS;
  957. case ArcOpenReadOnly:
  958. //
  959. // To open the file we will lookup the $data as our file context and then
  960. // increment the appropriate counters.
  961. //
  962. LookupAttribute( StructureContext,
  963. FileRecord,
  964. $DATA,
  965. &Found,
  966. FileContext );
  967. if (!Found) { return EBADF; }
  968. FileTableEntry->Flags.Open = 1;
  969. FileTableEntry->Flags.Read = 1;
  970. return ESUCCESS;
  971. case ArcOpenDirectory:
  972. return ENOTDIR;
  973. default:
  974. return EROFS;
  975. }
  976. }
  977. ARC_STATUS
  978. NtfsRead (
  979. IN ULONG FileId,
  980. OUT VOID * FIRMWARE_PTR Buffer,
  981. IN ULONG Length,
  982. OUT ULONG * FIRMWARE_PTR Transfer
  983. )
  984. /*++
  985. Routine Description:
  986. This routine reads data from the specified file.
  987. Arguments:
  988. FileId - Supplies the file table index.
  989. Buffer - Supplies a pointer to the buffer that receives the data
  990. read.
  991. Length - Supplies the number of bytes that are to be read.
  992. Transfer - Supplies a pointer to a variable that receives the number
  993. of bytes actually transfered.
  994. Return Value:
  995. ESUCCESS is returned if the read operation is successful. Otherwise,
  996. an unsuccessful status is returned that describes the reason for failure.
  997. --*/
  998. {
  999. PBL_FILE_TABLE FileTableEntry;
  1000. PNTFS_STRUCTURE_CONTEXT StructureContext;
  1001. PNTFS_FILE_CONTEXT FileContext;
  1002. LONGLONG AmountLeft;
  1003. //
  1004. // Setup some local references
  1005. //
  1006. FileTableEntry = &BlFileTable[FileId];
  1007. StructureContext = (PNTFS_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  1008. FileContext = &FileTableEntry->u.NtfsFileContext;
  1009. //
  1010. // Compute the amount left in the file and then from that we compute the amount
  1011. // for the transfer
  1012. //
  1013. AmountLeft = /*xxSub*/( FileContext->DataSize - FileTableEntry->Position.QuadPart);
  1014. if (/*xxLeq*/(/*xxFromUlong*/(Length) <= AmountLeft)) {
  1015. *Transfer = Length;
  1016. } else {
  1017. *Transfer = ((ULONG)AmountLeft);
  1018. }
  1019. //
  1020. // Now issue the read attribute
  1021. //
  1022. ReadAttribute( StructureContext,
  1023. FileContext,
  1024. FileTableEntry->Position.QuadPart,
  1025. *Transfer,
  1026. Buffer );
  1027. //
  1028. // Update the current position, and return to our caller
  1029. //
  1030. FileTableEntry->Position.QuadPart = /*xxAdd*/(FileTableEntry->Position.QuadPart + /*xxFromUlong*/(*Transfer));
  1031. return ESUCCESS;
  1032. }
  1033. ARC_STATUS
  1034. NtfsSeek (
  1035. IN ULONG FileId,
  1036. IN LARGE_INTEGER * FIRMWARE_PTR Offset,
  1037. IN SEEK_MODE SeekMode
  1038. )
  1039. /*++
  1040. Routine Description:
  1041. This routine seeks to the specified position for the file specified
  1042. by the file id.
  1043. Arguments:
  1044. FileId - Supplies the file table index.
  1045. Offset - Supplies the offset in the file to position to.
  1046. SeekMode - Supplies the mode of the seek operation.
  1047. Return Value:
  1048. ESUCCESS if returned as the function value.
  1049. --*/
  1050. {
  1051. PBL_FILE_TABLE FileTableEntry;
  1052. LONGLONG NewPosition;
  1053. //
  1054. // Load our local variables
  1055. //
  1056. FileTableEntry = &BlFileTable[FileId];
  1057. //
  1058. // Compute the new position
  1059. //
  1060. if (SeekMode == SeekAbsolute) {
  1061. NewPosition = Offset->QuadPart;
  1062. } else {
  1063. NewPosition = /*xxAdd*/(FileTableEntry->Position.QuadPart + Offset->QuadPart);
  1064. }
  1065. //
  1066. // If the new position is greater than the file size then return an error
  1067. //
  1068. if (/*xxGtr*/(NewPosition > FileTableEntry->u.NtfsFileContext.DataSize)) {
  1069. return EINVAL;
  1070. }
  1071. //
  1072. // Otherwise set the new position and return to our caller
  1073. //
  1074. FileTableEntry->Position.QuadPart = NewPosition;
  1075. return ESUCCESS;
  1076. }
  1077. ARC_STATUS
  1078. NtfsSetFileInformation (
  1079. IN ULONG FileId,
  1080. IN ULONG AttributeFlags,
  1081. IN ULONG AttributeMask
  1082. )
  1083. /*++
  1084. Routine Description:
  1085. This routine sets the file attributes of the indicated file
  1086. Arguments:
  1087. FileId - Supplies the File Id for the operation
  1088. AttributeFlags - Supplies the value (on or off) for each attribute being modified
  1089. AttributeMask - Supplies a mask of the attributes being altered. All other
  1090. file attributes are left alone.
  1091. Return Value:
  1092. ESUCCESS is returned if the read operation is successful. Otherwise,
  1093. an unsuccessful status is returned that describes the reason for failure.
  1094. --*/
  1095. {
  1096. UNREFERENCED_PARAMETER( FileId );
  1097. UNREFERENCED_PARAMETER( AttributeFlags );
  1098. UNREFERENCED_PARAMETER( AttributeMask );
  1099. return EROFS;
  1100. }
  1101. ARC_STATUS
  1102. NtfsWrite (
  1103. IN ULONG FileId,
  1104. IN VOID * FIRMWARE_PTR Buffer,
  1105. IN ULONG Length,
  1106. OUT ULONG * FIRMWARE_PTR Transfer
  1107. )
  1108. /*++
  1109. Routine Description:
  1110. This routine writes data to the specified file.
  1111. Arguments:
  1112. FileId - Supplies the file table index.
  1113. Buffer - Supplies a pointer to the buffer that contains the data
  1114. written.
  1115. Length - Supplies the number of bytes that are to be written.
  1116. Transfer - Supplies a pointer to a variable that receives the number
  1117. of bytes actually transfered.
  1118. Return Value:
  1119. ESUCCESS is returned if the write operation is successful. Otherwise,
  1120. an unsuccessful status is returned that describes the reason for failure.
  1121. --*/
  1122. {
  1123. PBL_FILE_TABLE FileTableEntry;
  1124. PNTFS_STRUCTURE_CONTEXT StructureContext;
  1125. PNTFS_FILE_CONTEXT FileContext;
  1126. LONGLONG AmountLeft;
  1127. ULONG Status;
  1128. //
  1129. // Setup some local references
  1130. //
  1131. FileTableEntry = &BlFileTable[FileId];
  1132. StructureContext = (PNTFS_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  1133. FileContext = &FileTableEntry->u.NtfsFileContext;
  1134. //
  1135. // Compute the amount left in the file and then from that we compute the amount
  1136. // for the transfer
  1137. //
  1138. AmountLeft = /*xxSub*/( FileContext->DataSize - FileTableEntry->Position.QuadPart);
  1139. if (Length <= AmountLeft) {
  1140. *Transfer = Length;
  1141. } else {
  1142. *Transfer = ((ULONG)AmountLeft);
  1143. }
  1144. //
  1145. // Now issue the write attribute
  1146. //
  1147. if (FileContext->IsAttributeResident) {
  1148. return EROFS;
  1149. }
  1150. Status = NtfsWriteNonresidentAttribute(
  1151. StructureContext,
  1152. FileContext,
  1153. FileTableEntry->Position.QuadPart,
  1154. *Transfer,
  1155. Buffer
  1156. );
  1157. if (Status != ESUCCESS) {
  1158. return Status;
  1159. }
  1160. //
  1161. // Update the current position, and return to our caller
  1162. //
  1163. FileTableEntry->Position.QuadPart += *Transfer;
  1164. return ESUCCESS;
  1165. }
  1166. ARC_STATUS
  1167. NtfsInitialize (
  1168. VOID
  1169. )
  1170. /*++
  1171. Routine Description:
  1172. This routine initializes the ntfs boot filesystem.
  1173. Currently this is a no-op.
  1174. Arguments:
  1175. None.
  1176. Return Value:
  1177. ESUCCESS.
  1178. --*/
  1179. {
  1180. //
  1181. // The first time we will zero out the file record buffer and allocate
  1182. // a few buffers for read in data.
  1183. //
  1184. ARC_STATUS Status = ESUCCESS;
  1185. ULONG Index = 0;
  1186. RtlZeroMemory(NtfsLinkCache, sizeof(NtfsLinkCache));
  1187. for (Index=0; Index < MAX_CACHE_ENTRIES; Index++) {
  1188. NtfsLinkCache[Index].DeviceId = UNINITIALIZED_DEVICE_ID;
  1189. }
  1190. RtlZeroMemory( NtfsFileRecordBuffer, sizeof(NtfsFileRecordBuffer));
  1191. NtfsFileRecordBuffer[0] = ALIGN_BUFFER(NtfsBuffer0);
  1192. NtfsFileRecordBuffer[1] = ALIGN_BUFFER(NtfsBuffer1);
  1193. NtfsIndexAllocationBuffer = ALIGN_BUFFER(NtfsBuffer2);
  1194. NtfsCompressedBuffer = ALIGN_BUFFER(NtfsBuffer3);
  1195. NtfsUncompressedBuffer = ALIGN_BUFFER(NtfsBuffer4);
  1196. #ifdef CACHE_DEVINFO
  1197. Status = ArcRegisterForDeviceClose(NtfsInvalidateCacheEntries);
  1198. #endif // for CACHE_DEV_INFO
  1199. return Status;
  1200. }
  1201. //
  1202. // Local support routine
  1203. //
  1204. ARC_STATUS
  1205. NtfsReadDisk (
  1206. IN ULONG DeviceId,
  1207. IN LONGLONG Lbo,
  1208. IN ULONG ByteCount,
  1209. IN OUT PVOID Buffer,
  1210. IN BOOLEAN CacheNewData
  1211. )
  1212. /*++
  1213. Routine Description:
  1214. This routine reads in zero or more bytes from the specified device.
  1215. Arguments:
  1216. DeviceId - Supplies the device id to use in the arc calls.
  1217. Lbo - Supplies the LBO to start reading from.
  1218. ByteCount - Supplies the number of bytes to read.
  1219. Buffer - Supplies a pointer to the buffer to read the bytes into.
  1220. CacheNewData - Whether to cache new data read from the disk.
  1221. Return Value:
  1222. ESUCCESS is returned if the read operation is successful. Otherwise,
  1223. an unsuccessful status is returned that describes the reason for failure.
  1224. --*/
  1225. {
  1226. ARC_STATUS Status;
  1227. ULONG i;
  1228. //
  1229. // Special case the zero byte read request
  1230. //
  1231. if (ByteCount == 0) {
  1232. return ESUCCESS;
  1233. }
  1234. //
  1235. // Issue the read through the cache.
  1236. //
  1237. Status = BlDiskCacheRead(DeviceId,
  1238. (PLARGE_INTEGER)&Lbo,
  1239. Buffer,
  1240. ByteCount,
  1241. &i,
  1242. CacheNewData);
  1243. if (Status != ESUCCESS) {
  1244. return Status;
  1245. }
  1246. //
  1247. // Make sure we got back the amount requested
  1248. //
  1249. if (ByteCount != i) {
  1250. return EIO;
  1251. }
  1252. //
  1253. // Everything is fine so return success to our caller
  1254. //
  1255. return ESUCCESS;
  1256. }
  1257. //
  1258. // Local support routine
  1259. //
  1260. ARC_STATUS
  1261. NtfsWriteDisk (
  1262. IN ULONG DeviceId,
  1263. IN LONGLONG Lbo,
  1264. IN ULONG ByteCount,
  1265. IN OUT PVOID Buffer
  1266. )
  1267. /*++
  1268. Routine Description:
  1269. This routine Writes in zero or more bytes from the specified device.
  1270. Arguments:
  1271. DeviceId - Supplies the device id to use in the arc calls.
  1272. Lbo - Supplies the LBO to start Writeing from.
  1273. ByteCount - Supplies the number of bytes to Write.
  1274. Buffer - Supplies a pointer to the buffer to Write the bytes into.
  1275. Return Value:
  1276. ESUCCESS is returned if the Write operation is successful. Otherwise,
  1277. an unsuccessful status is returned that describes the reason for failure.
  1278. --*/
  1279. {
  1280. ARC_STATUS Status;
  1281. ULONG i;
  1282. //
  1283. // Special case the zero byte Write request
  1284. //
  1285. if (ByteCount == 0) {
  1286. return ESUCCESS;
  1287. }
  1288. //
  1289. // Issue the write through the cache.
  1290. //
  1291. Status = BlDiskCacheWrite (DeviceId,
  1292. (PLARGE_INTEGER) &Lbo,
  1293. Buffer,
  1294. ByteCount,
  1295. &i);
  1296. if (Status != ESUCCESS) {
  1297. return Status;
  1298. }
  1299. //
  1300. // Make sure we got back the amount requested
  1301. //
  1302. if (ByteCount != i) {
  1303. return EIO;
  1304. }
  1305. //
  1306. // Everything is fine so return success to our caller
  1307. //
  1308. return ESUCCESS;
  1309. }
  1310. //
  1311. // Local support routine
  1312. //
  1313. ARC_STATUS
  1314. NtfsLookupAttribute (
  1315. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  1316. IN LONGLONG FileRecord,
  1317. IN ATTRIBUTE_TYPE_CODE TypeCode,
  1318. OUT PBOOLEAN FoundAttribute,
  1319. OUT PNTFS_ATTRIBUTE_CONTEXT AttributeContext
  1320. )
  1321. /*++
  1322. Routine Description:
  1323. This routine search the input file record for the indicated
  1324. attribute record. It will search through multiple related
  1325. file records to find the attribute. If the type code is for $data
  1326. then the attribute we look for must be unnamed otherwise we will
  1327. ignore the names of the attributes and return the first attriubute
  1328. of the indicated type.
  1329. Arguments:
  1330. StructureContext - Supplies the volume structure for this operation
  1331. FileRecord - Supplies the file record to start searching from. This need
  1332. not be the base file record.
  1333. TypeCode - Supplies the attribute type that we are looking for
  1334. FoundAttribute - Receives an indicating if the attribute was located
  1335. AttributeContext - Receives the attribute context for the found attribute
  1336. Return Value:
  1337. ESUCCESS is returned if the operation is successful. Otherwise,
  1338. an unsuccessful status is returned that describes the reason for failure.
  1339. --*/
  1340. {
  1341. PATTRIBUTE_RECORD_HEADER AttributeHeader;
  1342. NTFS_ATTRIBUTE_CONTEXT AttributeContext1;
  1343. PNTFS_ATTRIBUTE_CONTEXT AttributeList;
  1344. LONGLONG li;
  1345. ATTRIBUTE_LIST_ENTRY AttributeListEntry;
  1346. ULONG BufferIndex;
  1347. //
  1348. // Unless other noted we will assume we haven't found the attribute
  1349. //
  1350. *FoundAttribute = FALSE;
  1351. //
  1352. // Read in the file record and if necessary move ourselves up to the base file
  1353. // record
  1354. //
  1355. ReadAndDecodeFileRecord( StructureContext,
  1356. FileRecord,
  1357. &BufferIndex );
  1358. if (/*!xxEqlZero*/(*((PLONGLONG)&(NtfsFileRecordBuffer[BufferIndex]->BaseFileRecordSegment)) != 0)) {
  1359. //
  1360. // This isn't the base file record so now extract the base file record
  1361. // number and read it in
  1362. //
  1363. FileReferenceToLargeInteger( NtfsFileRecordBuffer[BufferIndex]->BaseFileRecordSegment,
  1364. &FileRecord );
  1365. DereferenceFileRecord( BufferIndex );
  1366. ReadAndDecodeFileRecord( StructureContext,
  1367. FileRecord,
  1368. &BufferIndex );
  1369. }
  1370. //
  1371. // Now we have read in the base file record so search for the target attribute
  1372. // type code and also remember if we find the attribute list attribute
  1373. //
  1374. AttributeList = NULL;
  1375. for (AttributeHeader = NtfsFirstAttribute( NtfsFileRecordBuffer[BufferIndex] );
  1376. AttributeHeader->TypeCode != $END;
  1377. AttributeHeader = NtfsGetNextRecord( AttributeHeader )) {
  1378. //
  1379. // We have located the attribute in question if the type code match and if
  1380. // it is either not the data attribute or if it is the data attribute then
  1381. // it is also unnamed
  1382. //
  1383. if ((AttributeHeader->TypeCode == TypeCode)
  1384. &&
  1385. ((TypeCode != $DATA) ||
  1386. ((TypeCode == $DATA) && (AttributeHeader->NameLength == 0)))
  1387. &&
  1388. ((AttributeHeader->FormCode != NONRESIDENT_FORM) ||
  1389. (AttributeHeader->Form.Nonresident.LowestVcn == 0))) {
  1390. //
  1391. // Indicate that we have found the attribute and setup the output
  1392. // attribute context and then return to our caller
  1393. //
  1394. *FoundAttribute = TRUE;
  1395. InitializeAttributeContext( StructureContext,
  1396. NtfsFileRecordBuffer[BufferIndex],
  1397. AttributeHeader,
  1398. FileRecord,
  1399. AttributeContext );
  1400. DereferenceFileRecord( BufferIndex );
  1401. return ESUCCESS;
  1402. }
  1403. //
  1404. // Check if this is the attribute list attribute and if so then setup a
  1405. // local attribute context to use just in case we don't find the attribute
  1406. // we're after in the base file record
  1407. //
  1408. if (AttributeHeader->TypeCode == $ATTRIBUTE_LIST) {
  1409. InitializeAttributeContext( StructureContext,
  1410. NtfsFileRecordBuffer[BufferIndex],
  1411. AttributeHeader,
  1412. FileRecord,
  1413. AttributeList = &AttributeContext1 );
  1414. }
  1415. }
  1416. //
  1417. // If we reach this point then the attribute has not been found in the base file
  1418. // record so check if we have located an attribute list. If not then the search
  1419. // has not been successful
  1420. //
  1421. if (AttributeList == NULL) {
  1422. DereferenceFileRecord( BufferIndex );
  1423. return ESUCCESS;
  1424. }
  1425. //
  1426. // Now that we've located the attribute list we need to continue our search. So
  1427. // what this outer loop does is search down the attribute list looking for a
  1428. // match.
  1429. //
  1430. for (li = 0;
  1431. /*xxLtr*/(li < AttributeList->DataSize);
  1432. li = /*xxAdd*/(li + /*xxFromUlong*/(AttributeListEntry.RecordLength))) {
  1433. //
  1434. // Read in the attribute list entry. We don't need to read in the name,
  1435. // just the first part of the list entry.
  1436. //
  1437. ReadAttribute( StructureContext,
  1438. AttributeList,
  1439. li,
  1440. sizeof(ATTRIBUTE_LIST_ENTRY),
  1441. &AttributeListEntry );
  1442. //
  1443. // Now check if the attribute matches, and it is the first of multiple
  1444. // segments, and either it is not $data or if it is $data then it is unnamed
  1445. //
  1446. if ((AttributeListEntry.AttributeTypeCode == TypeCode)
  1447. &&
  1448. /*xxEqlZero*/(AttributeListEntry.LowestVcn == 0)
  1449. &&
  1450. ((TypeCode != $DATA) ||
  1451. ((TypeCode == $DATA) && (AttributeListEntry.AttributeNameLength == 0)))) {
  1452. //
  1453. // We found a match so now compute the file record containing the
  1454. // attribute we're after and read in the file record
  1455. //
  1456. FileReferenceToLargeInteger( AttributeListEntry.SegmentReference,
  1457. &FileRecord );
  1458. DereferenceFileRecord( BufferIndex );
  1459. ReadAndDecodeFileRecord( StructureContext,
  1460. FileRecord,
  1461. &BufferIndex );
  1462. //
  1463. // Now search down the file record for our matching attribute, and it
  1464. // better be there otherwise the attribute list is wrong.
  1465. //
  1466. for (AttributeHeader = NtfsFirstAttribute( NtfsFileRecordBuffer[BufferIndex] );
  1467. AttributeHeader->TypeCode != $END;
  1468. AttributeHeader = NtfsGetNextRecord( AttributeHeader )) {
  1469. //
  1470. // We have located the attribute in question if the type code match
  1471. // and if it is either not the data attribute or if it is the data
  1472. // attribute then it is also unnamed
  1473. //
  1474. if ((AttributeHeader->TypeCode == TypeCode)
  1475. &&
  1476. ((TypeCode != $DATA) ||
  1477. ((TypeCode == $DATA) && (AttributeHeader->NameLength == 0)))) {
  1478. //
  1479. // Indicate that we have found the attribute and setup the
  1480. // output attribute context and return to our caller
  1481. //
  1482. *FoundAttribute = TRUE;
  1483. InitializeAttributeContext( StructureContext,
  1484. NtfsFileRecordBuffer[BufferIndex],
  1485. AttributeHeader,
  1486. FileRecord,
  1487. AttributeContext );
  1488. DereferenceFileRecord( BufferIndex );
  1489. return ESUCCESS;
  1490. }
  1491. }
  1492. DereferenceFileRecord( BufferIndex );
  1493. return EBADF;
  1494. }
  1495. }
  1496. //
  1497. // If we reach this point we've exhausted the attribute list without finding the
  1498. // attribute
  1499. //
  1500. DereferenceFileRecord( BufferIndex );
  1501. return ESUCCESS;
  1502. }
  1503. //
  1504. // Local support routine
  1505. //
  1506. ARC_STATUS
  1507. NtfsReadResidentAttribute (
  1508. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  1509. IN PCNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  1510. IN VBO Vbo,
  1511. IN ULONG Length,
  1512. IN PVOID Buffer
  1513. )
  1514. /*++
  1515. Routine Description:
  1516. This routine reads in the value of a resident attribute. The attribute
  1517. must be resident.
  1518. Arguments:
  1519. StructureContext - Supplies the volume structure for this operation
  1520. AttributeContext - Supplies the attribute being read.
  1521. Vbo - Supplies the offset within the value to return
  1522. Length - Supplies the number of bytes to return
  1523. Buffer - Supplies a pointer to the output buffer for storing the data
  1524. Return Value:
  1525. ESUCCESS is returned if the read operation is successful. Otherwise,
  1526. an unsuccessful status is returned that describes the reason for failure.
  1527. --*/
  1528. {
  1529. PATTRIBUTE_RECORD_HEADER AttributeHeader;
  1530. ULONG BufferIndex;
  1531. //
  1532. // Read in the file record containing the resident attribute
  1533. //
  1534. ReadAndDecodeFileRecord( StructureContext,
  1535. AttributeContext->FileRecord,
  1536. &BufferIndex );
  1537. //
  1538. // Get a pointer to the attribute header
  1539. //
  1540. AttributeHeader = Add2Ptr( NtfsFileRecordBuffer[BufferIndex],
  1541. AttributeContext->FileRecordOffset );
  1542. //
  1543. // Copy the amount of data the user asked for starting with the proper offset
  1544. //
  1545. RtlMoveMemory( Buffer,
  1546. Add2Ptr(NtfsGetValue(AttributeHeader), ((ULONG)Vbo)),
  1547. Length );
  1548. //
  1549. // And return to our caller
  1550. //
  1551. DereferenceFileRecord( BufferIndex );
  1552. return ESUCCESS;
  1553. }
  1554. //
  1555. // Local support routine
  1556. //
  1557. ARC_STATUS
  1558. NtfsReadNonresidentAttribute (
  1559. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  1560. IN PCNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  1561. IN VBO Vbo,
  1562. IN ULONG Length,
  1563. IN PVOID Buffer
  1564. )
  1565. /*++
  1566. Routine Description:
  1567. This routine reads in the value of a Nonresident attribute. The attribute
  1568. must be Nonresident.
  1569. Arguments:
  1570. StructureContext - Supplies the volume structure for this operation
  1571. AttributeContext - Supplies the attribute being read.
  1572. Vbo - Supplies the offset within the value to return
  1573. Length - Supplies the number of bytes to return
  1574. Buffer - Supplies a pointer to the output buffer for storing the data
  1575. Return Value:
  1576. ESUCCESS is returned if the read operation is successful. Otherwise,
  1577. an unsuccessful status is returned that describes the reason for failure.
  1578. --*/
  1579. {
  1580. BOOLEAN bCacheNewData;
  1581. //
  1582. // We want to cache new data read from the disk to satisfy this
  1583. // request only if we are reading the MFT, or $INDEX_ROOT,
  1584. // $BITMAP or $INDEX_ALLOCATION attributes for directory look
  1585. // up. $INDEX_ROOT is supposed to be resident in the file record
  1586. // but we want cache a read we make for it otherwise.
  1587. //
  1588. if ((AttributeContext == &StructureContext->MftAttributeContext) ||
  1589. (AttributeContext->TypeCode == $INDEX_ROOT) ||
  1590. (AttributeContext->TypeCode == $INDEX_ALLOCATION) ||
  1591. (AttributeContext->TypeCode == $BITMAP)) {
  1592. bCacheNewData = CACHE_NEW_DATA;
  1593. } else {
  1594. bCacheNewData = DONT_CACHE_NEW_DATA;
  1595. }
  1596. //
  1597. // Check if we are reading a compressed attribute
  1598. //
  1599. if (AttributeContext->CompressionFormat != 0) {
  1600. //
  1601. // While there is still some more to copy into the
  1602. // caller's buffer, we will load the cached compressed buffers
  1603. // and then copy out the data
  1604. //
  1605. while (Length > 0) {
  1606. ULONG ByteCount;
  1607. //
  1608. // Load up the cached compressed buffers with the
  1609. // the proper data. First check if the buffer is
  1610. // already (i.e., the file record and offset match and
  1611. // the vbo we're after is within the buffers range)
  1612. //
  1613. if (/*xxNeq*/(NtfsCompressedFileRecord != AttributeContext->FileRecord) ||
  1614. (NtfsCompressedOffset != AttributeContext->FileRecordOffset) ||
  1615. (((ULONG)Vbo) < NtfsCompressedVbo) ||
  1616. (((ULONG)Vbo) >= (NtfsCompressedVbo + AttributeContext->CompressionUnit))) {
  1617. ULONG i;
  1618. LBO Lbo;
  1619. //
  1620. // Load up the cached identification information
  1621. //
  1622. NtfsCompressedFileRecord = AttributeContext->FileRecord;
  1623. NtfsCompressedOffset = AttributeContext->FileRecordOffset;
  1624. NtfsCompressedVbo = ((ULONG)Vbo) & ~(AttributeContext->CompressionUnit - 1);
  1625. //
  1626. // Now load up the compressed buffer with data. We keep on
  1627. // loading until we're done loading or the Lbo we get back is
  1628. // zero.
  1629. //
  1630. for (i = 0; i < AttributeContext->CompressionUnit; i += ByteCount) {
  1631. VboToLbo( StructureContext,
  1632. AttributeContext,
  1633. /*xxFromUlong*/(NtfsCompressedVbo + i),
  1634. &Lbo,
  1635. &ByteCount );
  1636. if (/*xxEqlZero*/(Lbo == 0)) { break; }
  1637. //
  1638. // Trim the byte count down to a compression unit and we'll catch the
  1639. // excess the next time through the loop
  1640. //
  1641. if ((i + ByteCount) > AttributeContext->CompressionUnit) {
  1642. ByteCount = AttributeContext->CompressionUnit - i;
  1643. }
  1644. ReadDisk( StructureContext->DeviceId, Lbo, ByteCount, &NtfsCompressedBuffer[i], bCacheNewData );
  1645. }
  1646. //
  1647. // If the index for the preceding loop is zero then we know
  1648. // that there isn't any data on disk for the compression unit
  1649. // and in-fact the compression unit is all zeros
  1650. //
  1651. if (i == 0) {
  1652. RtlZeroMemory( NtfsUncompressedBuffer, AttributeContext->CompressionUnit );
  1653. //
  1654. // Otherwise the unit we just read in cannot be compressed
  1655. // because it completely fills up the compression unit
  1656. //
  1657. } else if (i >= AttributeContext->CompressionUnit) {
  1658. RtlMoveMemory( NtfsUncompressedBuffer,
  1659. NtfsCompressedBuffer,
  1660. AttributeContext->CompressionUnit );
  1661. //
  1662. // If the index for the preceding loop is less then the
  1663. // compression unit size then we know that the data we
  1664. // read in is less than the compression unit and we hit
  1665. // a zero lbo. So the unit must be compressed.
  1666. //
  1667. } else {
  1668. NTSTATUS Status;
  1669. Status = RtlDecompressBuffer( AttributeContext->CompressionFormat,
  1670. NtfsUncompressedBuffer,
  1671. AttributeContext->CompressionUnit,
  1672. NtfsCompressedBuffer,
  1673. i,
  1674. &ByteCount );
  1675. if (!NT_SUCCESS(Status)) {
  1676. return EINVAL;
  1677. }
  1678. //
  1679. // Check if the decompressed buffer doesn't fill up the
  1680. // compression unit and if so then zero out the remainder
  1681. // of the uncompressed buffer
  1682. //
  1683. if (ByteCount < AttributeContext->CompressionUnit) {
  1684. RtlZeroMemory( &NtfsUncompressedBuffer[ByteCount],
  1685. AttributeContext->CompressionUnit - ByteCount );
  1686. }
  1687. }
  1688. }
  1689. //
  1690. // Now copy off the data from the compressed buffer to the
  1691. // user buffer and continue the loop until the length is zero.
  1692. // The amount of data we need to copy is the smaller of the
  1693. // length the user wants back or the number of bytes left in
  1694. // the uncompressed buffer from the requested vbo to the end
  1695. // of the buffer.
  1696. //
  1697. ByteCount = Minimum( Length,
  1698. NtfsCompressedVbo + AttributeContext->CompressionUnit - ((ULONG)Vbo) );
  1699. RtlMoveMemory( Buffer,
  1700. &NtfsUncompressedBuffer[ ((ULONG)Vbo) - NtfsCompressedVbo ],
  1701. ByteCount );
  1702. //
  1703. // Update the length to be what the user now needs read in,
  1704. // also update the Vbo and Buffer to be the next locations
  1705. // to be read in.
  1706. //
  1707. Length -= ByteCount;
  1708. Vbo = /*xxAdd*/( Vbo + /*xxFromUlong*/(ByteCount));
  1709. Buffer = (PCHAR)Buffer + ByteCount;
  1710. }
  1711. return ESUCCESS;
  1712. }
  1713. //
  1714. // Read in runs of data until the byte count goes to zero
  1715. //
  1716. while (Length > 0) {
  1717. LBO Lbo;
  1718. ULONG CurrentRunByteCount;
  1719. //
  1720. // Lookup the corresponding Lbo and run length for the current position
  1721. // (i.e., vbo)
  1722. //
  1723. VboToLbo( StructureContext,
  1724. AttributeContext,
  1725. Vbo,
  1726. &Lbo,
  1727. &CurrentRunByteCount );
  1728. //
  1729. // While there are bytes to be read in from the current run length and we
  1730. // haven't exhausted the request we loop reading in bytes. The biggest
  1731. // request we'll handle is only 32KB contiguous bytes per physical read.
  1732. // So we might need to loop through the run
  1733. //
  1734. while ((Length > 0) && (CurrentRunByteCount > 0)) {
  1735. LONG SingleReadSize;
  1736. //
  1737. // Compute the size of the next physical read
  1738. //
  1739. SingleReadSize = Minimum(Length, 32*1024);
  1740. SingleReadSize = Minimum((ULONG)SingleReadSize, CurrentRunByteCount);
  1741. //
  1742. // Don't read beyond the data size
  1743. //
  1744. if (/*xxGtr*/(/*xxAdd*/(Vbo + /*xxFromUlong*/(SingleReadSize)) > AttributeContext->DataSize )) {
  1745. SingleReadSize = ((ULONG)(/*xxSub*/(AttributeContext->DataSize - Vbo)));
  1746. //
  1747. // If the readjusted read length is now zero then we're done
  1748. //
  1749. if (SingleReadSize <= 0) {
  1750. return ESUCCESS;
  1751. }
  1752. //
  1753. // By also setting length we'll make sure that this is our last read
  1754. //
  1755. Length = SingleReadSize;
  1756. }
  1757. //
  1758. // Issue the read
  1759. //
  1760. ReadDisk( StructureContext->DeviceId, Lbo, SingleReadSize, Buffer, bCacheNewData );
  1761. //
  1762. // Update the remaining length, current run byte count, and new lbo
  1763. // offset
  1764. //
  1765. Length -= SingleReadSize;
  1766. CurrentRunByteCount -= SingleReadSize;
  1767. Lbo = /*xxAdd*/(Lbo + /*xxFromUlong*/(SingleReadSize));
  1768. Vbo = /*xxAdd*/(Vbo + /*xxFromUlong*/(SingleReadSize));
  1769. //
  1770. // Update the buffer to point to the next byte location to fill in
  1771. //
  1772. Buffer = (PCHAR)Buffer + SingleReadSize;
  1773. }
  1774. }
  1775. //
  1776. // If we get here then the remaining byte count is zero so we can return success
  1777. // to our caller
  1778. //
  1779. return ESUCCESS;
  1780. }
  1781. //
  1782. // Local support routine
  1783. //
  1784. ARC_STATUS
  1785. NtfsWriteNonresidentAttribute (
  1786. IN PNTFS_STRUCTURE_CONTEXT StructureContext,
  1787. IN PNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  1788. IN VBO Vbo,
  1789. IN ULONG Length,
  1790. IN PVOID Buffer
  1791. )
  1792. /*++
  1793. Routine Description:
  1794. This routine write in the value of a Nonresident attribute.
  1795. Arguments:
  1796. StructureContext - Supplies the volume structure for this operation
  1797. AttributeContext - Supplies the attribute being written
  1798. Vbo - Supplies the offset within the value to return
  1799. Length - Supplies the number of bytes to return
  1800. Buffer - Supplies a pointer to the output buffer for storing the data
  1801. Return Value:
  1802. ESUCCESS is returned if the write operation is successful. Otherwise,
  1803. an unsuccessful status is returned that describes the reason for failure.
  1804. --*/
  1805. {
  1806. //
  1807. // Check if we are writing a compressed attribute
  1808. //
  1809. if (AttributeContext->CompressionFormat != 0) {
  1810. return EROFS;
  1811. }
  1812. //
  1813. // Write in runs of data until the byte count goes to zero
  1814. //
  1815. while (Length > 0) {
  1816. LBO Lbo;
  1817. ULONG CurrentRunByteCount;
  1818. //
  1819. // Lookup the corresponding Lbo and run length for the current position
  1820. // (i.e., vbo)
  1821. //
  1822. VboToLbo( StructureContext,
  1823. AttributeContext,
  1824. Vbo,
  1825. &Lbo,
  1826. &CurrentRunByteCount );
  1827. //
  1828. // While there are bytes to be written in from the current run length and we
  1829. // haven't exhausted the request we loop writing in bytes. The biggest
  1830. // request we'll handle is only 32KB contiguous bytes per physical write.
  1831. // So we might need to loop through the run
  1832. //
  1833. while ((Length > 0) && (CurrentRunByteCount > 0)) {
  1834. LONG SingleWriteSize;
  1835. //
  1836. // Compute the size of the next physical written
  1837. //
  1838. SingleWriteSize = Minimum(Length, 32*1024);
  1839. SingleWriteSize = Minimum((ULONG)SingleWriteSize, CurrentRunByteCount);
  1840. //
  1841. // Don't write beyond the data size
  1842. //
  1843. if (/*xxGtr*/(/*xxAdd*/(Vbo + /*xxFromUlong*/(SingleWriteSize)) > AttributeContext->DataSize )) {
  1844. SingleWriteSize = ((ULONG)(/*xxSub*/(AttributeContext->DataSize - Vbo)));
  1845. //
  1846. // If the adjusted write length is now zero then we're done
  1847. //
  1848. if (SingleWriteSize <= 0) {
  1849. return ESUCCESS;
  1850. }
  1851. //
  1852. // By also setting length we'll make sure that this is our last write
  1853. //
  1854. Length = SingleWriteSize;
  1855. }
  1856. //
  1857. // Issue the write
  1858. //
  1859. WriteDisk( StructureContext->DeviceId, Lbo, SingleWriteSize, Buffer );
  1860. //
  1861. // Update the remaining length, current run byte count, and new lbo
  1862. // offset
  1863. //
  1864. Length -= SingleWriteSize;
  1865. CurrentRunByteCount -= SingleWriteSize;
  1866. Lbo = /*xxAdd*/(Lbo + /*xxFromUlong*/(SingleWriteSize));
  1867. Vbo = /*xxAdd*/(Vbo + /*xxFromUlong*/(SingleWriteSize));
  1868. //
  1869. // Update the buffer to point to the next byte location to fill in
  1870. //
  1871. Buffer = (PCHAR)Buffer + SingleWriteSize;
  1872. }
  1873. }
  1874. //
  1875. // If we get here then the remaining byte count is zero so we can return success
  1876. // to our caller
  1877. //
  1878. return ESUCCESS;
  1879. }
  1880. //
  1881. // Local support routine
  1882. //
  1883. ARC_STATUS
  1884. NtfsReadAndDecodeFileRecord (
  1885. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  1886. IN LONGLONG FileRecord,
  1887. OUT PULONG Index
  1888. )
  1889. /*++
  1890. Routine Description:
  1891. This routine reads in the specified file record into the indicated
  1892. ntfs file record buffer index provided that the buffer is not pinned.
  1893. It will also look at the current buffers and see if any will already
  1894. satisfy the request or assign an unused buffer if necessary and
  1895. fix Index to point to the right buffer
  1896. Arguments:
  1897. StructureContext - Supplies the volume structure for this operation
  1898. FileRecord - Supplies the file record number being read
  1899. Index - Receives the index of where we put the buffer. After this
  1900. call the buffer is pinned and will need to be unpinned if it is
  1901. to be reused.
  1902. Return Value:
  1903. ESUCCESS is returned if the operation is successful. Otherwise,
  1904. an unsuccessful status is returned that describes the reason for failure.
  1905. --*/
  1906. {
  1907. ARC_STATUS Status;
  1908. //
  1909. // For each buffer that is not null check if we have a hit on the
  1910. // file record and if so then increment the pin count and return
  1911. // that index
  1912. //
  1913. for (*Index = 0; (*Index < BUFFER_COUNT) && (NtfsFileRecordBuffer[*Index] != NULL); *Index += 1) {
  1914. if (NtfsFileRecordBufferVbo[*Index] == FileRecord) {
  1915. NtfsFileRecordBufferPinned[*Index] += 1;
  1916. return ESUCCESS;
  1917. }
  1918. }
  1919. //
  1920. // Check for the first unpinned buffer and make sure we haven't exhausted the
  1921. // array
  1922. //
  1923. for (*Index = 0; (*Index < BUFFER_COUNT) && (NtfsFileRecordBufferPinned[*Index] != 0); *Index += 1) {
  1924. NOTHING;
  1925. }
  1926. if (*Index == BUFFER_COUNT) { return E2BIG; }
  1927. //
  1928. // We have an unpinned buffer that we want to use, check if we need to
  1929. // allocate a buffer to actually hold the data
  1930. //
  1931. PausedPrint(( "Reusing index %x for %I64x\r\n", *Index, FileRecord ));
  1932. if (NtfsFileRecordBuffer[*Index] == NULL) {
  1933. NtfsFileRecordBuffer[*Index] = BlAllocateHeapAligned(MAXIMUM_FILE_RECORD_SIZE);
  1934. }
  1935. //
  1936. // Pin the buffer and then read in the data
  1937. //
  1938. NtfsFileRecordBufferPinned[*Index] += 1;
  1939. if ((Status = NtfsReadNonresidentAttribute( StructureContext,
  1940. &StructureContext->MftAttributeContext,
  1941. FileRecord * StructureContext->BytesPerFileRecord,
  1942. StructureContext->BytesPerFileRecord,
  1943. NtfsFileRecordBuffer[*Index] )) != ESUCCESS) {
  1944. return Status;
  1945. }
  1946. //
  1947. // Decode the usa
  1948. //
  1949. if ((Status = NtfsDecodeUsa( NtfsFileRecordBuffer[*Index],
  1950. StructureContext->BytesPerFileRecord )) != ESUCCESS) {
  1951. return Status;
  1952. }
  1953. //
  1954. // And set the file record so that we know where it came from
  1955. //
  1956. NtfsFileRecordBufferVbo[*Index] = FileRecord;
  1957. return ESUCCESS;
  1958. }
  1959. //
  1960. // Local support routine
  1961. //
  1962. ARC_STATUS
  1963. NtfsDecodeUsa (
  1964. IN PVOID UsaBuffer,
  1965. IN ULONG Length
  1966. )
  1967. /*++
  1968. Routine Description:
  1969. This routine takes as input file record or index buffer and applies the
  1970. usa transformation to get it back into a state that we can use it.
  1971. Arguments:
  1972. UsaBuffer - Supplies the buffer used in this operation
  1973. Length - Supplies the length of the buffer in bytes
  1974. Return Value:
  1975. ESUCCESS is returned if the operation is successful. Otherwise,
  1976. an unsuccessful status is returned that describes the reason for failure.
  1977. --*/
  1978. {
  1979. PMULTI_SECTOR_HEADER MultiSectorHeader;
  1980. PUSHORT UsaOffset;
  1981. ULONG UsaSize;
  1982. ULONG i;
  1983. PUSHORT ProtectedUshort;
  1984. UNREFERENCED_PARAMETER( Length );
  1985. //
  1986. // Setup our local variables
  1987. //
  1988. MultiSectorHeader = (PMULTI_SECTOR_HEADER)UsaBuffer;
  1989. UsaOffset = Add2Ptr(UsaBuffer, MultiSectorHeader->UpdateSequenceArrayOffset);
  1990. UsaSize = MultiSectorHeader->UpdateSequenceArraySize;
  1991. //
  1992. // For every entry in the usa we need to compute the address of the protected
  1993. // ushort and then check that the protected ushort is equal to the current
  1994. // sequence number (i.e., the number at UsaOffset[0]) and then replace the
  1995. // protected ushort number with the saved ushort in the usa.
  1996. //
  1997. for (i = 1; i < UsaSize; i += 1) {
  1998. ProtectedUshort = Add2Ptr( UsaBuffer,
  1999. (SEQUENCE_NUMBER_STRIDE * i) - sizeof(USHORT));
  2000. if (*ProtectedUshort != UsaOffset[0]) {
  2001. // NtfsPrint( "USA Failure\r\n" );
  2002. return EBADF;
  2003. }
  2004. *ProtectedUshort = UsaOffset[i];
  2005. }
  2006. //
  2007. // And return to our caller
  2008. //
  2009. return ESUCCESS;
  2010. }
  2011. //
  2012. // Local support routine
  2013. //
  2014. BOOLEAN
  2015. NtfsIsNameCached (
  2016. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  2017. IN CSTRING FileName,
  2018. IN OUT PLONGLONG FileRecord,
  2019. OUT PBOOLEAN Found,
  2020. OUT PBOOLEAN IsDirectory
  2021. )
  2022. /*++
  2023. Routine Description:
  2024. This routine consults the cache for the given link.
  2025. Arguments:
  2026. StructureContext - Supplies the volume structure for this operation
  2027. FileName - name of entry to look up
  2028. FileRecord - IN file record of parent directory, OUT file record of child
  2029. Found - whether we found this in the cache or not
  2030. Return Value:
  2031. TRUE if the name was found in the cache.
  2032. --*/
  2033. {
  2034. ULONG i, j;
  2035. *Found = FALSE;
  2036. #ifdef CACHE_DEVINFO
  2037. // NtfsPrint( "Cache probe on %04x %I64x '%.*s'\r\n",
  2038. // StructureContext->DeviceId,
  2039. // *FileRecord,
  2040. // FileName.Length,
  2041. // FileName.Buffer );
  2042. for (i = 0; i < MAX_CACHE_ENTRIES; i++) {
  2043. // NtfsPrint( "Cache comparing to %04x %I64x '%.*s'\r\n",
  2044. // NtfsLinkCache[i].DeviceId,
  2045. // NtfsLinkCache[i].ParentFileRecord,
  2046. // NtfsLinkCache[i].NameLength,
  2047. // NtfsLinkCache[i].RelativeName );
  2048. if (NtfsLinkCache[i].DeviceId == StructureContext->DeviceId &&
  2049. NtfsLinkCache[i].ParentFileRecord == *FileRecord &&
  2050. NtfsLinkCache[i].NameLength == FileName.Length) {
  2051. // NtfsPrint( "Comparing names\r\n" );
  2052. for (j = 0; j < FileName.Length; j++ ) {
  2053. if (NtfsLinkCache[i].RelativeName[j] != ToUpper( (USHORT) FileName.Buffer[j] )) {
  2054. break;
  2055. }
  2056. }
  2057. if (j == FileName.Length) {
  2058. //
  2059. // Match
  2060. //
  2061. // NtfsPrint( "Cache hit\r\n" );
  2062. *Found = TRUE;
  2063. *FileRecord = NtfsLinkCache[i].ChildFileRecord;
  2064. *IsDirectory = TRUE;
  2065. break;
  2066. }
  2067. }
  2068. }
  2069. #endif // CACHE_DEVINFO
  2070. return *Found;
  2071. }
  2072. //
  2073. // Local support routine
  2074. //
  2075. #ifdef CACHE_DEVINFO
  2076. VOID
  2077. NtfsInvalidateCacheEntries(
  2078. IN ULONG DeviceId
  2079. )
  2080. {
  2081. ULONG i, Count = 0;
  2082. #if 0
  2083. BlPrint("NtfsInvalidateCacheEntries() called for %d(%d)\r\n",
  2084. DeviceId,
  2085. NtfsLinkCacheCount);
  2086. while (!BlGetKey());
  2087. #endif
  2088. for (i = 0; i < MAX_CACHE_ENTRIES; i++) {
  2089. if (NtfsLinkCache[i].DeviceId == DeviceId) {
  2090. NtfsLinkCache[i].DeviceId = UNINITIALIZED_DEVICE_ID;
  2091. Count++;
  2092. }
  2093. }
  2094. if (NtfsLinkCacheCount >= Count) {
  2095. NtfsLinkCacheCount -= Count;
  2096. } else {
  2097. NtfsLinkCacheCount = 0;
  2098. }
  2099. #if 0
  2100. BlPrint("NtfsInvalidateCacheEntries() called for %d(%d)\r\n",
  2101. DeviceId,
  2102. NtfsLinkCacheCount);
  2103. while (!BlGetKey());
  2104. #endif
  2105. }
  2106. #endif // CACHE_DEV_INFO
  2107. VOID
  2108. NtfsAddNameToCache (
  2109. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  2110. IN CSTRING FileName,
  2111. IN LONGLONG ParentFileRecord,
  2112. IN LONGLONG FileRecord
  2113. )
  2114. /*++
  2115. Routine Description:
  2116. This routine adds a name and link to the name cache
  2117. Arguments:
  2118. StructureContext - Supplies the volume structure for this operation
  2119. FileName - Supplies the file name being cached (in ansi).
  2120. ParentFileRecord - the file record of the parent
  2121. FileRecord - file record associated with the name
  2122. Return Value:
  2123. None.
  2124. --*/
  2125. {
  2126. #ifdef CACHE_DEVINFO
  2127. if (NtfsLinkCacheCount < MAX_CACHE_ENTRIES) {
  2128. ULONG i;
  2129. ULONG Index;
  2130. for (Index = 0; Index < MAX_CACHE_ENTRIES; Index++) {
  2131. if (NtfsLinkCache[Index].DeviceId == UNINITIALIZED_DEVICE_ID) {
  2132. break;
  2133. }
  2134. }
  2135. if (Index < MAX_CACHE_ENTRIES) {
  2136. NtfsLinkCache[Index].DeviceId = StructureContext->DeviceId;
  2137. NtfsLinkCache[Index].ParentFileRecord = ParentFileRecord;
  2138. NtfsLinkCache[Index].NameLength = FileName.Length;
  2139. for (i = 0; i < FileName.Length; i++) {
  2140. NtfsLinkCache[Index].RelativeName[i] = ToUpper( FileName.Buffer[i] );
  2141. }
  2142. NtfsLinkCache[Index].ChildFileRecord = FileRecord;
  2143. NtfsLinkCacheCount++;
  2144. PausedPrint( ("Caching %04x %I64x %.*s %I64X\r\n",
  2145. StructureContext->DeviceId,
  2146. ParentFileRecord,
  2147. FileName.Length,
  2148. FileName.Buffer,
  2149. FileRecord ));
  2150. }
  2151. } else {
  2152. // NtfsPrint( "Cache is full at %I64x %.*s %I64X\r\n",
  2153. // ParentFileRecord,
  2154. // FileName.Length,
  2155. // FileName.Buffer,
  2156. // FileRecord );
  2157. // Pause;
  2158. }
  2159. #endif
  2160. }
  2161. //
  2162. // Local support routine
  2163. //
  2164. ARC_STATUS
  2165. NtfsSearchForFileName (
  2166. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  2167. IN CSTRING FileName,
  2168. IN OUT PLONGLONG FileRecord,
  2169. OUT PBOOLEAN Found,
  2170. OUT PBOOLEAN IsDirectory
  2171. )
  2172. /*++
  2173. Routine Description:
  2174. This routine searches a given index root and allocation for the specified
  2175. file name.
  2176. Arguments:
  2177. StructureContext - Supplies the volume structure for this operation
  2178. FileName - Supplies the file name being searched for (in ansi).
  2179. FileRecord - Receives the file record for the entry if one was located.
  2180. Found - Receives a value to indicate if we found the specified
  2181. file name in the directory
  2182. IsDirectory - Receives a value to indicate if the found index is itself
  2183. a directory
  2184. Return Value:
  2185. ESUCCESS is returned if the operation is successful. Otherwise,
  2186. an unsuccessful status is returned that describes the reason for failure.
  2187. --*/
  2188. {
  2189. LONGLONG ParentFileRecord;
  2190. //
  2191. // Test to see if the file name is cached
  2192. //
  2193. if (NtfsIsNameCached( StructureContext, FileName, FileRecord, Found, IsDirectory )) {
  2194. return ESUCCESS;
  2195. }
  2196. ParentFileRecord = *FileRecord;
  2197. InexactSortedDirectoryScan( StructureContext, FileName, FileRecord, Found, IsDirectory );
  2198. if (!*Found) {
  2199. LinearDirectoryScan( StructureContext, FileName, FileRecord, Found, IsDirectory );
  2200. }
  2201. //
  2202. // If we have a directory entry, then add it to the cache
  2203. //
  2204. if (*Found && *IsDirectory) {
  2205. NtfsAddNameToCache( StructureContext, FileName, ParentFileRecord, *FileRecord );
  2206. }
  2207. return ESUCCESS;
  2208. }
  2209. //
  2210. // Local support routine
  2211. //
  2212. ARC_STATUS
  2213. NtfsInexactSortedDirectoryScan (
  2214. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  2215. IN CSTRING FileName,
  2216. IN OUT PLONGLONG FileRecord,
  2217. OUT PBOOLEAN Found,
  2218. OUT PBOOLEAN IsDirectory
  2219. )
  2220. /*++
  2221. Routine Description:
  2222. This routine searches a given index root and allocation for the specified
  2223. file name by performing simple uppercasing and using that to wander through
  2224. the directory tree.
  2225. Arguments:
  2226. StructureContext - Supplies the volume structure for this operation
  2227. FileName - Supplies the file name being searched for (in ansi).
  2228. FileRecord - Receives the file record for the entry if one was located.
  2229. Found - Receives a value to indicate if we found the specified
  2230. file name in the directory
  2231. IsDirectory - Receives a value to indicate if the found index is itself
  2232. a directory
  2233. Return Value:
  2234. ESUCCESS is returned if the operation is successful. Otherwise,
  2235. an unsuccessful status is returned that describes the reason for failure.
  2236. --*/
  2237. {
  2238. PATTRIBUTE_RECORD_HEADER IndexAttributeHeader;
  2239. PINDEX_ROOT IndexRootValue;
  2240. PINDEX_HEADER IndexHeader;
  2241. NTFS_ATTRIBUTE_CONTEXT AttributeContext1;
  2242. NTFS_ATTRIBUTE_CONTEXT AttributeContext2;
  2243. NTFS_ATTRIBUTE_CONTEXT AttributeContext3;
  2244. PNTFS_ATTRIBUTE_CONTEXT IndexRoot;
  2245. PNTFS_ATTRIBUTE_CONTEXT IndexAllocation;
  2246. PNTFS_ATTRIBUTE_CONTEXT AllocationBitmap;
  2247. ULONG NextIndexBuffer;
  2248. ULONG BytesPerIndexBuffer;
  2249. ULONG BufferIndex;
  2250. //
  2251. // The current file record must be a directory so now lookup the index root,
  2252. // allocation and bitmap for the directory and then we can do our search.
  2253. //
  2254. // NtfsPrint( "InexactSortedDirectoryScan %04x %I64x for '%.*s'\r\n",
  2255. // StructureContext->DeviceId,
  2256. // *FileRecord, FileName.Length, FileName.Buffer );
  2257. // Pause;
  2258. IndexRoot = &AttributeContext1;
  2259. LookupAttribute( StructureContext,
  2260. *FileRecord,
  2261. $INDEX_ROOT,
  2262. Found,
  2263. IndexRoot);
  2264. if (!*Found) { return EBADF; }
  2265. IndexAllocation = &AttributeContext2;
  2266. LookupAttribute( StructureContext,
  2267. *FileRecord,
  2268. $INDEX_ALLOCATION,
  2269. Found,
  2270. IndexAllocation);
  2271. if (!*Found) { IndexAllocation = NULL; }
  2272. AllocationBitmap = &AttributeContext3;
  2273. LookupAttribute( StructureContext,
  2274. *FileRecord,
  2275. $BITMAP,
  2276. Found,
  2277. AllocationBitmap);
  2278. if (!*Found) { AllocationBitmap = NULL; }
  2279. //
  2280. // unless otherwise set we will assume that our search has failed
  2281. //
  2282. *Found = FALSE;
  2283. //
  2284. // First read in and search the index root for the file name. We know the index
  2285. // root is resident so we'll save some buffering and just read in file record
  2286. // with the index root directly
  2287. //
  2288. ReadAndDecodeFileRecord( StructureContext,
  2289. IndexRoot->FileRecord,
  2290. &BufferIndex );
  2291. IndexAttributeHeader = Add2Ptr( NtfsFileRecordBuffer[BufferIndex],
  2292. IndexRoot->FileRecordOffset );
  2293. IndexRootValue = NtfsGetValue( IndexAttributeHeader );
  2294. IndexHeader = &IndexRootValue->IndexHeader;
  2295. //
  2296. // We also setup ourselves so that if the current index does not contain a match
  2297. // we will read in the next index and continue our search
  2298. //
  2299. BytesPerIndexBuffer = IndexRootValue->BytesPerIndexBuffer;
  2300. //
  2301. // Now we'll just continue looping intil we either find a match or exhaust all
  2302. // of the index buffer
  2303. //
  2304. NextIndexBuffer = UNINITIALIZED_DEVICE_ID;
  2305. while (TRUE) {
  2306. PINDEX_ENTRY IndexEntry;
  2307. VBO Vbo;
  2308. // NtfsPrint( "Searching IndexBuffer %x\r\n", NextIndexBuffer );
  2309. //
  2310. // Search the current index buffer (from index header looking for a match
  2311. //
  2312. for (IndexEntry = Add2Ptr(IndexHeader, IndexHeader->FirstIndexEntry);
  2313. !FlagOn(IndexEntry->Flags, INDEX_ENTRY_END);
  2314. IndexEntry = Add2Ptr(IndexEntry, IndexEntry->Length)) {
  2315. PFILE_NAME FileNameEntry;
  2316. UNICODE_STRING UnicodeFileName;
  2317. int Result;
  2318. //
  2319. // Get the FileName for this index entry
  2320. //
  2321. FileNameEntry = Add2Ptr(IndexEntry, sizeof(INDEX_ENTRY));
  2322. UnicodeFileName.Length = FileNameEntry->FileNameLength * 2;
  2323. UnicodeFileName.Buffer = &FileNameEntry->FileName[0];
  2324. //
  2325. // Check if this the name we're after if it is then say we found it and
  2326. // setup the output variables
  2327. //
  2328. Result = NtfsCompareName( FileName, UnicodeFileName );
  2329. if (Result == 0) {
  2330. FileReferenceToLargeInteger( IndexEntry->FileReference,
  2331. FileRecord );
  2332. *Found = TRUE;
  2333. *IsDirectory = FlagOn( FileNameEntry->Info.FileAttributes,
  2334. DUP_FILE_NAME_INDEX_PRESENT);
  2335. // NtfsPrint( "Found Entry %I64x\r\n", *FileRecord );
  2336. DereferenceFileRecord( BufferIndex );
  2337. return ESUCCESS;
  2338. } else if (Result < 0) {
  2339. // NtfsPrint( "Found > entry '%.*ws'\r\n", UnicodeFileName.Length, UnicodeFileName.Buffer );
  2340. break;
  2341. }
  2342. }
  2343. //
  2344. // At this point, we've either hit the end of the index or we have
  2345. // found the first entry larger than the name we're looking for. In either case
  2346. // we may have a downpointer to examine. If not, then there is no entry here.
  2347. //
  2348. //
  2349. // If no down pointer then release the file record buffer and quit
  2350. //
  2351. if (!FlagOn( IndexEntry->Flags, INDEX_ENTRY_NODE )) {
  2352. DereferenceFileRecord( BufferIndex );
  2353. // NtfsPrint( "No down pointer\r\n" );
  2354. return ESUCCESS;
  2355. }
  2356. //
  2357. // At this point we've searched one index header and need to read in another
  2358. // one to check. But first make sure there are additional index buffers
  2359. //
  2360. if (!ARGUMENT_PRESENT(IndexAllocation) ||
  2361. !ARGUMENT_PRESENT(AllocationBitmap)) {
  2362. // NtfsPrint( "No index allocation\r\n" );
  2363. DereferenceFileRecord( BufferIndex );
  2364. return ESUCCESS;
  2365. }
  2366. NextIndexBuffer = (ULONG)NtfsIndexEntryBlock( IndexEntry ) ;
  2367. Vbo = NextIndexBuffer * StructureContext->BytesPerCluster;
  2368. //
  2369. // Make sure the buffer offset is within the stream
  2370. //
  2371. if (Vbo >= IndexAllocation->DataSize) {
  2372. // NtfsPrint( "Beyond end of stream %I64x %x\r\n", IndexAllocation->DataSize, NextIndexBuffer );
  2373. DereferenceFileRecord( BufferIndex );
  2374. return ESUCCESS;
  2375. }
  2376. //
  2377. // At this point we've computed the next index allocation buffer to read in
  2378. // so read it in, decode it, and go back to the top of our loop
  2379. //
  2380. ReadAttribute( StructureContext,
  2381. IndexAllocation,
  2382. Vbo,
  2383. BytesPerIndexBuffer,
  2384. NtfsIndexAllocationBuffer );
  2385. DecodeUsa( NtfsIndexAllocationBuffer, BytesPerIndexBuffer );
  2386. IndexHeader = &NtfsIndexAllocationBuffer->IndexHeader;
  2387. }
  2388. }
  2389. //
  2390. // Local support routine
  2391. //
  2392. ARC_STATUS
  2393. NtfsLinearDirectoryScan (
  2394. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  2395. IN CSTRING FileName,
  2396. IN OUT PLONGLONG FileRecord,
  2397. OUT PBOOLEAN Found,
  2398. OUT PBOOLEAN IsDirectory
  2399. )
  2400. /*++
  2401. Routine Description:
  2402. This routine searches a given index root and allocation for the specified
  2403. file name by looking linearly through every entry.
  2404. Arguments:
  2405. StructureContext - Supplies the volume structure for this operation
  2406. FileName - Supplies the file name being searched for (in ansi).
  2407. FileRecord - Receives the file record for the entry if one was located.
  2408. Found - Receives a value to indicate if we found the specified
  2409. file name in the directory
  2410. IsDirectory - Receives a value to indicate if the found index is itself
  2411. a directory
  2412. Return Value:
  2413. ESUCCESS is returned if the operation is successful. Otherwise,
  2414. an unsuccessful status is returned that describes the reason for failure.
  2415. --*/
  2416. {
  2417. PATTRIBUTE_RECORD_HEADER IndexAttributeHeader;
  2418. PINDEX_ROOT IndexRootValue;
  2419. PINDEX_HEADER IndexHeader;
  2420. NTFS_ATTRIBUTE_CONTEXT AttributeContext1;
  2421. NTFS_ATTRIBUTE_CONTEXT AttributeContext2;
  2422. NTFS_ATTRIBUTE_CONTEXT AttributeContext3;
  2423. PNTFS_ATTRIBUTE_CONTEXT IndexRoot;
  2424. PNTFS_ATTRIBUTE_CONTEXT IndexAllocation;
  2425. PNTFS_ATTRIBUTE_CONTEXT AllocationBitmap;
  2426. ULONG NextIndexBuffer;
  2427. ULONG BytesPerIndexBuffer;
  2428. ULONG BufferIndex;
  2429. //
  2430. // The current file record must be a directory so now lookup the index root,
  2431. // allocation and bitmap for the directory and then we can do our search.
  2432. //
  2433. // NtfsPrint( "LinearSearching %04x %I64x for %.*s\r\n",
  2434. // StructureContext->DeviceId,
  2435. // *FileRecord, FileName.Length, FileName.Buffer );
  2436. // Pause;
  2437. IndexRoot = &AttributeContext1;
  2438. LookupAttribute( StructureContext,
  2439. *FileRecord,
  2440. $INDEX_ROOT,
  2441. Found,
  2442. IndexRoot);
  2443. if (!*Found) { return EBADF; }
  2444. IndexAllocation = &AttributeContext2;
  2445. LookupAttribute( StructureContext,
  2446. *FileRecord,
  2447. $INDEX_ALLOCATION,
  2448. Found,
  2449. IndexAllocation);
  2450. if (!*Found) { IndexAllocation = NULL; }
  2451. AllocationBitmap = &AttributeContext3;
  2452. LookupAttribute( StructureContext,
  2453. *FileRecord,
  2454. $BITMAP,
  2455. Found,
  2456. AllocationBitmap);
  2457. if (!*Found) { AllocationBitmap = NULL; }
  2458. //
  2459. // unless otherwise set we will assume that our search has failed
  2460. //
  2461. *Found = FALSE;
  2462. //
  2463. // First read in and search the index root for the file name. We know the index
  2464. // root is resident so we'll save some buffering and just read in file record
  2465. // with the index root directly
  2466. //
  2467. ReadAndDecodeFileRecord( StructureContext,
  2468. IndexRoot->FileRecord,
  2469. &BufferIndex );
  2470. IndexAttributeHeader = Add2Ptr( NtfsFileRecordBuffer[BufferIndex],
  2471. IndexRoot->FileRecordOffset );
  2472. IndexRootValue = NtfsGetValue( IndexAttributeHeader );
  2473. IndexHeader = &IndexRootValue->IndexHeader;
  2474. //
  2475. // We also setup ourselves so that if the current index does not contain a match
  2476. // we will read in the next index and continue our search
  2477. //
  2478. NextIndexBuffer = 0;
  2479. BytesPerIndexBuffer = IndexRootValue->BytesPerIndexBuffer;
  2480. //
  2481. // Now we'll just continue looping intil we either find a match or exhaust all
  2482. // of the index buffer
  2483. //
  2484. while (TRUE) {
  2485. PINDEX_ENTRY IndexEntry;
  2486. BOOLEAN IsAllocated;
  2487. VBO Vbo = 0;
  2488. //
  2489. // Search the current index buffer (from index header looking for a match
  2490. //
  2491. for (IndexEntry = Add2Ptr(IndexHeader, IndexHeader->FirstIndexEntry);
  2492. !FlagOn(IndexEntry->Flags, INDEX_ENTRY_END);
  2493. IndexEntry = Add2Ptr(IndexEntry, IndexEntry->Length)) {
  2494. PFILE_NAME FileNameEntry;
  2495. UNICODE_STRING UnicodeFileName;
  2496. //
  2497. // Get the FileName for this index entry
  2498. //
  2499. FileNameEntry = Add2Ptr(IndexEntry, sizeof(INDEX_ENTRY));
  2500. UnicodeFileName.Length = FileNameEntry->FileNameLength * 2;
  2501. UnicodeFileName.Buffer = &FileNameEntry->FileName[0];
  2502. //
  2503. // Check if this the name we're after if it is then say we found it and
  2504. // setup the output variables
  2505. //
  2506. if (NtfsCompareName( FileName, UnicodeFileName ) == 0) {
  2507. FileReferenceToLargeInteger( IndexEntry->FileReference,
  2508. FileRecord );
  2509. *Found = TRUE;
  2510. *IsDirectory = FlagOn( FileNameEntry->Info.FileAttributes,
  2511. DUP_FILE_NAME_INDEX_PRESENT);
  2512. DereferenceFileRecord( BufferIndex );
  2513. return ESUCCESS;
  2514. }
  2515. }
  2516. //
  2517. // At this point we've searched one index header and need to read in another
  2518. // one to check. But first make sure there are additional index buffers
  2519. //
  2520. if (!ARGUMENT_PRESENT(IndexAllocation) ||
  2521. !ARGUMENT_PRESENT(AllocationBitmap)) {
  2522. DereferenceFileRecord( BufferIndex );
  2523. return ESUCCESS;
  2524. }
  2525. //
  2526. // Now the following loop reads in the valid index buffer. The variable
  2527. // next index buffer denotes the buffer we want to read in. The idea is to
  2528. // first check that the buffer is part of the index allocation otherwise
  2529. // we've exhausted the list without finding a match. Once we know the
  2530. // allocation exists then we check if the record is really allocated if it
  2531. // is not allocated we try the next buffer and so on.
  2532. //
  2533. IsAllocated = FALSE;
  2534. while (!IsAllocated) {
  2535. //
  2536. // Compute the starting vbo of the next index buffer and check if it is
  2537. // still within the data size.
  2538. //
  2539. Vbo = (BytesPerIndexBuffer * NextIndexBuffer);
  2540. if (Vbo >= IndexAllocation->DataSize) {
  2541. DereferenceFileRecord( BufferIndex );
  2542. return ESUCCESS;
  2543. }
  2544. //
  2545. // Now check if the index buffer is in use
  2546. //
  2547. IsRecordAllocated( StructureContext,
  2548. AllocationBitmap,
  2549. NextIndexBuffer,
  2550. &IsAllocated );
  2551. NextIndexBuffer += 1;
  2552. }
  2553. //
  2554. // At this point we've computed the next index allocation buffer to read in
  2555. // so read it in, decode it, and go back to the top of our loop
  2556. //
  2557. ReadAttribute( StructureContext,
  2558. IndexAllocation,
  2559. Vbo,
  2560. BytesPerIndexBuffer,
  2561. NtfsIndexAllocationBuffer );
  2562. DecodeUsa( NtfsIndexAllocationBuffer, BytesPerIndexBuffer );
  2563. IndexHeader = &NtfsIndexAllocationBuffer->IndexHeader;
  2564. }
  2565. }
  2566. //
  2567. // Local support routine
  2568. //
  2569. ARC_STATUS
  2570. NtfsIsRecordAllocated (
  2571. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  2572. IN PCNTFS_ATTRIBUTE_CONTEXT AllocationBitmap,
  2573. IN ULONG BitOffset,
  2574. OUT PBOOLEAN IsAllocated
  2575. )
  2576. /*++
  2577. Routine Description:
  2578. This routine indicates to the caller if the specified index allocation record
  2579. is in use (i.e., its bit is 1).
  2580. Arguments:
  2581. StructureContext - Supplies the volume structure for this operation
  2582. AllocationBitmap - Supplies the attribute context for the index allocation bitmap
  2583. BitOffset - Supplies the offset (zero based) being checked
  2584. IsAllocated - Recieves an value indicating if the record is allocated or not
  2585. Return Value:
  2586. ESUCCESS is returned if the operation is successful. Otherwise,
  2587. an unsuccessful status is returned that describes the reason for failure.
  2588. --*/
  2589. {
  2590. ULONG ByteIndex;
  2591. ULONG BitIndex;
  2592. UCHAR LocalByte;
  2593. //
  2594. // This routine is rather dumb in that it only reads in the byte that contains
  2595. // the bit we're interested in and doesn't keep any state information between
  2596. // calls. We first break down the bit offset into the byte and bit within
  2597. // the byte that we need to check
  2598. //
  2599. ByteIndex = BitOffset / 8;
  2600. BitIndex = BitOffset % 8;
  2601. //
  2602. // Read in a single byte containing the bit we need to check
  2603. //
  2604. ReadAttribute( StructureContext,
  2605. AllocationBitmap,
  2606. /*xxFromUlong*/(ByteIndex),
  2607. 1,
  2608. &LocalByte );
  2609. //
  2610. // Shift over the local byte so that the bit we want is in the low order bit and
  2611. // then mask it out to see if the bit is set
  2612. //
  2613. if (FlagOn(LocalByte >> BitIndex, 0x01)) {
  2614. *IsAllocated = TRUE;
  2615. } else {
  2616. *IsAllocated = FALSE;
  2617. }
  2618. //
  2619. // And return to our caller
  2620. //
  2621. return ESUCCESS;
  2622. }
  2623. //
  2624. // Local support routine
  2625. //
  2626. ARC_STATUS
  2627. NtfsLoadMcb (
  2628. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  2629. IN PCNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  2630. IN VBO Vbo,
  2631. IN PNTFS_MCB Mcb
  2632. )
  2633. /*++
  2634. Routine Description:
  2635. This routine loads into one of the cached mcbs the retrival information for the
  2636. starting vbo.
  2637. Arguments:
  2638. StructureContext - Supplies the volume structure for this operation
  2639. AttributeContext - Supplies the Nonresident attribute being queried
  2640. Vbo - Supplies the starting Vbo to use when loading the mcb
  2641. Mcb - Supplies the mcb that we should be loading
  2642. Return Value:
  2643. ESUCCESS is returned if the operation is successful. Otherwise,
  2644. an unsuccessful status is returned that describes the reason for failure.
  2645. --*/
  2646. {
  2647. PATTRIBUTE_RECORD_HEADER AttributeHeader;
  2648. ULONG BytesPerCluster;
  2649. VBO LowestVbo;
  2650. VBO HighestVbo;
  2651. LONGLONG FileRecord;
  2652. NTFS_ATTRIBUTE_CONTEXT AttributeContext1;
  2653. PNTFS_ATTRIBUTE_CONTEXT AttributeList;
  2654. LONGLONG li;
  2655. LONGLONG Previousli;
  2656. ATTRIBUTE_LIST_ENTRY AttributeListEntry;
  2657. ATTRIBUTE_TYPE_CODE TypeCode;
  2658. ULONG BufferIndex;
  2659. ULONG SavedBufferIndex;
  2660. //
  2661. // Load our local variables
  2662. //
  2663. BytesPerCluster = StructureContext->BytesPerCluster;
  2664. //
  2665. // Setup a pointer to the cached mcb, indicate the attribute context that is will
  2666. // now own the cached mcb, and zero out the mcb
  2667. //
  2668. Mcb->InUse = 0;
  2669. //
  2670. // Read in the file record that contains the non-resident attribute and get a
  2671. // pointer to the attribute header
  2672. //
  2673. ReadAndDecodeFileRecord( StructureContext,
  2674. AttributeContext->FileRecord,
  2675. &BufferIndex );
  2676. AttributeHeader = Add2Ptr( NtfsFileRecordBuffer[BufferIndex],
  2677. AttributeContext->FileRecordOffset );
  2678. //
  2679. // Compute the lowest and highest vbo that is described by this attribute header
  2680. //
  2681. LowestVbo = AttributeHeader->Form.Nonresident.LowestVcn * BytesPerCluster;
  2682. HighestVbo = ((AttributeHeader->Form.Nonresident.HighestVcn + 1) * BytesPerCluster) - 1;
  2683. //
  2684. // Now check if the vbo we are after is within the range of this attribute header
  2685. // and if so then decode the retrieval information and return to our caller
  2686. //
  2687. if ((LowestVbo <= Vbo) && (Vbo <= HighestVbo)) {
  2688. DecodeRetrievalInformation( StructureContext, Mcb, Vbo, AttributeHeader );
  2689. DereferenceFileRecord( BufferIndex );
  2690. return ESUCCESS;
  2691. }
  2692. //
  2693. // At this point the attribute header does not contain the range we need so read
  2694. // in the base file record and we'll search the attribute list for a attribute
  2695. // header that we need. We need to make sure that we don't already have the base FRS.
  2696. // If we do, then we just continue using it.
  2697. //
  2698. if (/*!xxEqlZero*/(*((PLONGLONG)&(NtfsFileRecordBuffer[BufferIndex]->BaseFileRecordSegment)) != 0)) {
  2699. FileReferenceToLargeInteger( NtfsFileRecordBuffer[BufferIndex]->BaseFileRecordSegment,
  2700. &FileRecord );
  2701. DereferenceFileRecord( BufferIndex );
  2702. ReadAndDecodeFileRecord( StructureContext,
  2703. FileRecord,
  2704. &BufferIndex );
  2705. } else {
  2706. FileRecord = NtfsFileRecordBufferVbo[BufferIndex];
  2707. }
  2708. //
  2709. // Now we have read in the base file record so search for the attribute list
  2710. // attribute
  2711. //
  2712. AttributeList = NULL;
  2713. for (AttributeHeader = NtfsFirstAttribute( NtfsFileRecordBuffer[BufferIndex] );
  2714. AttributeHeader->TypeCode != $END;
  2715. AttributeHeader = NtfsGetNextRecord( AttributeHeader )) {
  2716. //
  2717. // Check if this is the attribute list attribute and if so then setup a local
  2718. // attribute context
  2719. //
  2720. if (AttributeHeader->TypeCode == $ATTRIBUTE_LIST) {
  2721. InitializeAttributeContext( StructureContext,
  2722. NtfsFileRecordBuffer[BufferIndex],
  2723. AttributeHeader,
  2724. FileRecord,
  2725. AttributeList = &AttributeContext1 );
  2726. }
  2727. }
  2728. //
  2729. // We have better located an attribute list otherwise we're in trouble
  2730. //
  2731. if (AttributeList == NULL) {
  2732. DereferenceFileRecord( BufferIndex );
  2733. return EINVAL;
  2734. }
  2735. //
  2736. // Setup a local for the type code
  2737. //
  2738. TypeCode = AttributeContext->TypeCode;
  2739. //
  2740. // Now that we've located the attribute list we need to continue our search. So
  2741. // what this outer loop does is search down the attribute list looking for a
  2742. // match.
  2743. //
  2744. NtfsFileRecordBufferPinned[SavedBufferIndex = BufferIndex] += 1;
  2745. for (Previousli = li = 0;
  2746. /*xxLtr*/(li < AttributeList->DataSize);
  2747. li = /*xxAdd*/(li + /*xxFromUlong*/(AttributeListEntry.RecordLength))) {
  2748. //
  2749. // Read in the attribute list entry. We don't need to read in the name,
  2750. // just the first part of the list entry.
  2751. //
  2752. ReadAttribute( StructureContext,
  2753. AttributeList,
  2754. li,
  2755. sizeof(ATTRIBUTE_LIST_ENTRY),
  2756. &AttributeListEntry );
  2757. //
  2758. // Now check if the attribute matches, and either it is not $data or if it
  2759. // is $data then it is unnamed
  2760. //
  2761. if ((AttributeListEntry.AttributeTypeCode == TypeCode)
  2762. &&
  2763. ((TypeCode != $DATA) ||
  2764. ((TypeCode == $DATA) && (AttributeListEntry.AttributeNameLength == 0)))) {
  2765. //
  2766. // If the lowest vcn is is greater than the vbo we've after then
  2767. // we are done and can use previous li otherwise set previous li accordingly.
  2768. if (Vbo < AttributeListEntry.LowestVcn * BytesPerCluster) {
  2769. break;
  2770. }
  2771. Previousli = li;
  2772. }
  2773. }
  2774. //
  2775. // Now we should have found the offset for the attribute list entry
  2776. // so read it in and verify that it is correct
  2777. //
  2778. ReadAttribute( StructureContext,
  2779. AttributeList,
  2780. Previousli,
  2781. sizeof(ATTRIBUTE_LIST_ENTRY),
  2782. &AttributeListEntry );
  2783. if ((AttributeListEntry.AttributeTypeCode == TypeCode)
  2784. &&
  2785. ((TypeCode != $DATA) ||
  2786. ((TypeCode == $DATA) && (AttributeListEntry.AttributeNameLength == 0)))) {
  2787. //
  2788. // We found a match so now compute the file record containing this
  2789. // attribute and read in the file record
  2790. //
  2791. FileReferenceToLargeInteger( AttributeListEntry.SegmentReference, &FileRecord );
  2792. DereferenceFileRecord( BufferIndex );
  2793. ReadAndDecodeFileRecord( StructureContext,
  2794. FileRecord,
  2795. &BufferIndex );
  2796. //
  2797. // Now search down the file record for our matching attribute, and it
  2798. // better be there otherwise the attribute list is wrong.
  2799. //
  2800. for (AttributeHeader = NtfsFirstAttribute( NtfsFileRecordBuffer[BufferIndex] );
  2801. AttributeHeader->TypeCode != $END;
  2802. AttributeHeader = NtfsGetNextRecord( AttributeHeader )) {
  2803. //
  2804. // As a quick check make sure that this attribute is non resident
  2805. //
  2806. if (AttributeHeader->FormCode == NONRESIDENT_FORM) {
  2807. //
  2808. // Compute the range of this attribute header
  2809. //
  2810. LowestVbo = AttributeHeader->Form.Nonresident.LowestVcn * BytesPerCluster;
  2811. HighestVbo = ((AttributeHeader->Form.Nonresident.HighestVcn + 1) * BytesPerCluster) - 1;
  2812. //
  2813. // We have located the attribute in question if the type code
  2814. // match, it is within the proper range, and if it is either not
  2815. // the data attribute or if it is the data attribute then it is
  2816. // also unnamed
  2817. //
  2818. if ((AttributeHeader->TypeCode == TypeCode)
  2819. &&
  2820. (LowestVbo <= Vbo) && (Vbo <= HighestVbo)
  2821. &&
  2822. ((TypeCode != $DATA) ||
  2823. ((TypeCode == $DATA) && (AttributeHeader->NameLength == 0)))) {
  2824. //
  2825. // We've located the attribute so now it is time to decode
  2826. // the retrieval information and return to our caller
  2827. //
  2828. DecodeRetrievalInformation( StructureContext,
  2829. Mcb,
  2830. Vbo,
  2831. AttributeHeader );
  2832. DereferenceFileRecord( BufferIndex );
  2833. DereferenceFileRecord( SavedBufferIndex );
  2834. return ESUCCESS;
  2835. }
  2836. }
  2837. }
  2838. }
  2839. DereferenceFileRecord( BufferIndex );
  2840. DereferenceFileRecord( SavedBufferIndex );
  2841. return EINVAL;
  2842. }
  2843. //
  2844. // Local support routine
  2845. //
  2846. ARC_STATUS
  2847. NtfsVboToLbo (
  2848. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  2849. IN PCNTFS_ATTRIBUTE_CONTEXT AttributeContext,
  2850. IN VBO Vbo,
  2851. OUT PLBO Lbo,
  2852. OUT PULONG ByteCount
  2853. )
  2854. /*++
  2855. Routine Description:
  2856. This routine computes the run denoted by the input vbo to into its
  2857. corresponding lbo and also returns the number of bytes remaining in
  2858. the run.
  2859. Arguments:
  2860. StructureContext - Supplies the volume structure for this operation
  2861. AttributeContext - Supplies the Nonresident attribute being queried
  2862. Vbo - Supplies the Vbo to match
  2863. Lbo - Recieves the corresponding Lbo
  2864. ByteCount - Receives the number of bytes remaining in the run
  2865. Return Value:
  2866. ESUCCESS is returned if the operation is successful. Otherwise,
  2867. an unsuccessful status is returned that describes the reason for failure.
  2868. --*/
  2869. {
  2870. PNTFS_MCB Mcb;
  2871. ULONG i;
  2872. //
  2873. // Check if we are doing the mft or some other attribute
  2874. //
  2875. Mcb = NULL;
  2876. if (AttributeContext == &StructureContext->MftAttributeContext) {
  2877. //
  2878. // For the mft we start with the base mcb but if the vbo is not in the mcb
  2879. // then we immediately switch over to the cached mcb
  2880. //
  2881. Mcb = (PNTFS_MCB)&StructureContext->MftBaseMcb;
  2882. if (/*xxLtr*/(Vbo < Mcb->Vbo[0]) || /*xxGeq*/(Vbo >= Mcb->Vbo[Mcb->InUse])) {
  2883. Mcb = NULL;
  2884. }
  2885. }
  2886. //
  2887. // If the Mcb is still null then we are to use the cached mcb, first find
  2888. // if one of the cached ones contains the range we're after
  2889. //
  2890. if (Mcb == NULL) {
  2891. for (i = 0; i < 16; i += 1) {
  2892. //
  2893. // check if we have a hit, on the same attribute and range
  2894. //
  2895. Mcb = (PNTFS_MCB)&StructureContext->CachedMcb[i];
  2896. if ((/*xxEql*/(AttributeContext->FileRecord == StructureContext->CachedMcbFileRecord[i]) &&
  2897. (AttributeContext->FileRecordOffset == StructureContext->CachedMcbFileRecordOffset[i]) &&
  2898. /*xxLeq*/(Mcb->Vbo[0] <= Vbo) && /*xxLtr*/(Vbo < Mcb->Vbo[Mcb->InUse]))) {
  2899. break;
  2900. }
  2901. Mcb = NULL;
  2902. }
  2903. //
  2904. // If we didn't get a hit then we need to load a new mcb we'll
  2905. // alternate through our two cached mcbs
  2906. //
  2907. if (Mcb == NULL) {
  2908. Mcb = (PNTFS_MCB)&StructureContext->CachedMcb[LastMcb % 16];
  2909. ((PNTFS_STRUCTURE_CONTEXT)StructureContext)->CachedMcbFileRecord[LastMcb % 16]
  2910. = AttributeContext->FileRecord;
  2911. ((PNTFS_STRUCTURE_CONTEXT)StructureContext)->CachedMcbFileRecordOffset[LastMcb % 16]
  2912. = AttributeContext->FileRecordOffset;
  2913. LastMcb += 1;
  2914. LoadMcb( StructureContext, AttributeContext, Vbo, Mcb );
  2915. }
  2916. }
  2917. //
  2918. // At this point the mcb contains the vbo asked for. So now search for the vbo.
  2919. // Note that we could also do binary search here but because the run count is
  2920. // probably small the extra overhead of a binary search doesn't buy us anything
  2921. //
  2922. for (i = 0; i < Mcb->InUse; i += 1) {
  2923. //
  2924. // We found our slot if the vbo we're after is less than the next mcb's vbo
  2925. //
  2926. if (/*xxLtr*/(Vbo < Mcb->Vbo[i+1])) {
  2927. //
  2928. // Compute the corresponding lbo which is the stored lbo plus the
  2929. // difference between the stored vbo and the vbo we're looking up.
  2930. // Also compute the byte count which is the difference between the
  2931. // current vbo we're looking up and the vbo for the next run
  2932. //
  2933. if (/*xxNeqZero*/(Mcb->Lbo[i] != 0)) {
  2934. *Lbo = /*xxAdd*/(Mcb->Lbo[i] + /*xxSub*/(Vbo - Mcb->Vbo[i]));
  2935. } else {
  2936. *Lbo = 0;
  2937. }
  2938. *ByteCount = ((ULONG)/*xxSub*/(Mcb->Vbo[i+1] - Vbo));
  2939. //
  2940. // And return to our caller
  2941. //
  2942. return ESUCCESS;
  2943. }
  2944. }
  2945. //
  2946. // If we really reach here we have an error. Most likely the file is not large
  2947. // enough for the requested vbo
  2948. //
  2949. return EINVAL;
  2950. }
  2951. //
  2952. // Local support routine
  2953. //
  2954. ARC_STATUS
  2955. NtfsDecodeRetrievalInformation (
  2956. IN PCNTFS_STRUCTURE_CONTEXT StructureContext,
  2957. IN PNTFS_MCB Mcb,
  2958. IN VBO Vbo,
  2959. IN PATTRIBUTE_RECORD_HEADER AttributeHeader
  2960. )
  2961. /*++
  2962. Routine Description:
  2963. This routine does the decode of the retrival information stored in a Nonresident
  2964. attribute header into the specified output mcb starting with the specified
  2965. Lbo.
  2966. Arguments:
  2967. StructureContext - Supplies the volume structure for this operation
  2968. Mcb - Supplies the Mcb used in this operation
  2969. Vbo - Supplies the starting vbo that must be stored in the mcb
  2970. AttributeHeader - Supplies the non resident attribute header that
  2971. we are to use in this operation
  2972. Return Value:
  2973. ESUCCESS is returned if the operation is successful. Otherwise,
  2974. an unsuccessful status is returned that describes the reason for failure.
  2975. --*/
  2976. {
  2977. ULONG BytesPerCluster;
  2978. VBO NextVbo;
  2979. LBO CurrentLbo;
  2980. VBO CurrentVbo;
  2981. LONGLONG Change;
  2982. PCHAR ch;
  2983. ULONG VboBytes;
  2984. ULONG LboBytes;
  2985. //
  2986. // Initialize our locals
  2987. //
  2988. BytesPerCluster = StructureContext->BytesPerCluster;
  2989. //
  2990. // Setup the next vbo and current lbo and ch for the following loop that decodes
  2991. // the retrieval information
  2992. //
  2993. NextVbo = /*xxXMul*/(AttributeHeader->Form.Nonresident.LowestVcn * BytesPerCluster);
  2994. CurrentLbo = 0;
  2995. ch = Add2Ptr( AttributeHeader,
  2996. AttributeHeader->Form.Nonresident.MappingPairsOffset );
  2997. Mcb->InUse = 0;
  2998. //
  2999. // Loop to process mapping pairs
  3000. //
  3001. while (!IsCharZero(*ch)) {
  3002. //
  3003. // Set current Vbo from initial value or last pass through loop
  3004. //
  3005. CurrentVbo = NextVbo;
  3006. //
  3007. // Extract the counts from the two nibbles of this byte
  3008. //
  3009. VboBytes = *ch & 0x0f;
  3010. LboBytes = *ch++ >> 4;
  3011. //
  3012. // Extract the Vbo change and update next vbo
  3013. //
  3014. Change = 0;
  3015. if (IsCharLtrZero(*(ch + VboBytes - 1))) {
  3016. return EINVAL;
  3017. }
  3018. RtlMoveMemory( &Change, ch, VboBytes );
  3019. ch += VboBytes;
  3020. NextVbo = /*xxAdd*/(NextVbo + /*xXMul*/(Change * BytesPerCluster));
  3021. //
  3022. // If we have reached the maximum for this mcb then it is time
  3023. // to return and not decipher any more retrieval information
  3024. //
  3025. if (Mcb->InUse >= MAXIMUM_NUMBER_OF_MCB_ENTRIES - 1) {
  3026. break;
  3027. }
  3028. //
  3029. // Now check if there is an lbo change. If there isn't
  3030. // then we only need to update the vbo, because this
  3031. // is sparse/compressed file.
  3032. //
  3033. if (LboBytes != 0) {
  3034. //
  3035. // Extract the Lbo change and update current lbo
  3036. //
  3037. Change = 0;
  3038. if (IsCharLtrZero(*(ch + LboBytes - 1))) {
  3039. Change = /*xxSub*/( Change - 1 );
  3040. }
  3041. RtlMoveMemory( &Change, ch, LboBytes );
  3042. ch += LboBytes;
  3043. CurrentLbo = /*xxAdd*/( CurrentLbo + /*xxXMul*/(Change * BytesPerCluster));
  3044. }
  3045. //
  3046. // Now check if the Next Vbo is greater than the Vbo we after
  3047. //
  3048. if (/*xxGeq*/(NextVbo >= Vbo)) {
  3049. //
  3050. // Load this entry into the mcb and advance our in use counter
  3051. //
  3052. Mcb->Vbo[Mcb->InUse] = CurrentVbo;
  3053. Mcb->Lbo[Mcb->InUse] = (LboBytes != 0 ? CurrentLbo : 0);
  3054. Mcb->Vbo[Mcb->InUse + 1] = NextVbo;
  3055. Mcb->InUse += 1;
  3056. }
  3057. }
  3058. return ESUCCESS;
  3059. }
  3060. //
  3061. // Local support routine
  3062. //
  3063. VOID
  3064. NtfsFirstComponent (
  3065. IN OUT PCSTRING String,
  3066. OUT PCSTRING FirstComponent
  3067. )
  3068. /*++
  3069. Routine Description:
  3070. This routine takes an input path name and separates it into its first
  3071. file name component and the remaining part.
  3072. Arguments:
  3073. String - Supplies the original string being dissected (in ansi). On return
  3074. this string will now point to the remaining part.
  3075. FirstComponent - Recieves the string representing the first file name in
  3076. the input string.
  3077. Return Value:
  3078. None.
  3079. --*/
  3080. {
  3081. ULONG Index;
  3082. //
  3083. // Copy over the string variable into the first component variable
  3084. //
  3085. *FirstComponent = *String;
  3086. //
  3087. // Now if the first character in the name is a backslash then
  3088. // simply skip over the backslash.
  3089. //
  3090. if (FirstComponent->Buffer[0] == '\\') {
  3091. FirstComponent->Buffer += 1;
  3092. FirstComponent->Length -= 1;
  3093. }
  3094. //
  3095. // Now search the name for a backslash
  3096. //
  3097. for (Index = 0; Index < FirstComponent->Length; Index += 1) {
  3098. if (FirstComponent->Buffer[Index] == '\\') {
  3099. break;
  3100. }
  3101. }
  3102. //
  3103. // At this point Index denotes a backslash or is equal to the length of the
  3104. // string. So update string to be the remaining part. Decrement the length of
  3105. // the first component by the approprate amount
  3106. //
  3107. String->Buffer = &FirstComponent->Buffer[Index];
  3108. String->Length = (SHORT)(FirstComponent->Length - Index);
  3109. FirstComponent->Length = (SHORT)Index;
  3110. //
  3111. // And return to our caller.
  3112. //
  3113. return;
  3114. }
  3115. //
  3116. // Local support routine
  3117. //
  3118. int
  3119. NtfsCompareName (
  3120. IN CSTRING AnsiString,
  3121. IN UNICODE_STRING UnicodeString
  3122. )
  3123. /*++
  3124. Routine Description:
  3125. This routine compares two names (one ansi and one unicode) for equality.
  3126. Arguments:
  3127. AnsiString - Supplies the ansi string to compare
  3128. UnicodeString - Supplies the unicode string to compare
  3129. Return Value:
  3130. < 0 if AnsiString is approximately < than UnicodeString
  3131. = 0 if AnsiString is approximately == UnicodeString
  3132. > 0 otherwise
  3133. --*/
  3134. {
  3135. ULONG i;
  3136. ULONG Length;
  3137. //
  3138. // Determine length for compare
  3139. //
  3140. if (AnsiString.Length * sizeof( WCHAR ) < UnicodeString.Length) {
  3141. Length = AnsiString.Length;
  3142. } else {
  3143. Length = UnicodeString.Length / sizeof( WCHAR );
  3144. }
  3145. i = 0;
  3146. while (i < Length) {
  3147. //
  3148. // If the current char is a mismatch, return the difference
  3149. //
  3150. if (ToUpper( (USHORT)AnsiString.Buffer[i] ) != ToUpper( UnicodeString.Buffer[i] )) {
  3151. return ToUpper( (USHORT)AnsiString.Buffer[i] ) - ToUpper( UnicodeString.Buffer[i] );
  3152. }
  3153. i++;
  3154. }
  3155. //
  3156. // We've compared equal up to the length of the shortest string. Return
  3157. // based on length comparison now.
  3158. //
  3159. return AnsiString.Length - UnicodeString.Length / sizeof( WCHAR );
  3160. }