Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4136 lines
105 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. fatboot.c
  5. Abstract:
  6. This module implements the FAT boot file system used by the operating
  7. system loader.
  8. Author:
  9. Gary Kimura (garyki) 29-Aug-1989
  10. Revision History:
  11. --*/
  12. #include "bootlib.h"
  13. #include "stdio.h"
  14. #include "blcache.h"
  15. BOOTFS_INFO FatBootFsInfo={L"fastfat"};
  16. //
  17. // Conditional debug print routine
  18. //
  19. #ifdef FATBOOTDBG
  20. #define FatDebugOutput(X,Y,Z) { \
  21. if (BlConsoleOutDeviceId) { \
  22. CHAR _b[128]; \
  23. ULONG _c; \
  24. sprintf(&_b[0], X, Y, Z); \
  25. ArcWrite(BlConsoleOutDeviceId, &_b[0], strlen(&_b[0]), &_c); \
  26. } \
  27. }
  28. #define CharOrSpace(C) ((C) < 0x20 ? 0x20: (C))
  29. #define FatDebugOutput83(X,N,Y,Z) { \
  30. if (BlConsoleOutDeviceId) { \
  31. CHAR _b[128]; \
  32. CHAR _n[13]; \
  33. ULONG _c; \
  34. sprintf(&_n[0], "> %c%c%c%c%c%c%c%c.%c%c%c <", \
  35. CharOrSpace(*((PCHAR)N +0)), \
  36. CharOrSpace(*((PCHAR)N +1)), \
  37. CharOrSpace(*((PCHAR)N +2)), \
  38. CharOrSpace(*((PCHAR)N +3)), \
  39. CharOrSpace(*((PCHAR)N +4)), \
  40. CharOrSpace(*((PCHAR)N +5)), \
  41. CharOrSpace(*((PCHAR)N +6)), \
  42. CharOrSpace(*((PCHAR)N +7)), \
  43. CharOrSpace(*((PCHAR)N +8)), \
  44. CharOrSpace(*((PCHAR)N +9)), \
  45. CharOrSpace(*((PCHAR)N +10))); \
  46. sprintf(&_b[0], X, _n, Y, Z); \
  47. ArcWrite(BlConsoleOutDeviceId, &_b[0], strlen(&_b[0]), &_c); \
  48. } \
  49. }
  50. #else
  51. #define FatDebugOutput(X,Y,Z) {NOTHING;}
  52. #define FatDebugOutput83(X,N,Y,Z) {NOTHING;}
  53. #endif // FATBOOTDBG
  54. //
  55. // Low level disk I/O procedure prototypes
  56. //
  57. ARC_STATUS
  58. FatDiskRead (
  59. IN ULONG DeviceId,
  60. IN LBO Lbo,
  61. IN ULONG ByteCount,
  62. IN PVOID Buffer,
  63. IN BOOLEAN CacheNewData
  64. );
  65. ARC_STATUS
  66. FatDiskWrite (
  67. IN ULONG DeviceId,
  68. IN LBO Lbo,
  69. IN ULONG ByteCount,
  70. IN PVOID Buffer
  71. );
  72. //
  73. // VOID
  74. // DiskRead (
  75. // IN ULONG DeviceId,
  76. // IN LBO Lbo,
  77. // IN ULONG ByteCount,
  78. // IN PVOID Buffer,
  79. // IN BOOLEAN CacheNewData,
  80. // IN BOOLEAN IsDoubleSpace
  81. // );
  82. //
  83. #define DiskRead(A,B,C,D,E,ignored) { ARC_STATUS _s; \
  84. if ((_s = FatDiskRead(A,B,C,D,E)) != ESUCCESS) { return _s; } \
  85. }
  86. #define DiskWrite(A,B,C,D) { ARC_STATUS _s; \
  87. if ((_s = FatDiskWrite(A,B,C,D)) != ESUCCESS) { return _s; } \
  88. }
  89. //
  90. // Cluster/Index routines
  91. //
  92. typedef enum _CLUSTER_TYPE {
  93. FatClusterAvailable,
  94. FatClusterReserved,
  95. FatClusterBad,
  96. FatClusterLast,
  97. FatClusterNext
  98. } CLUSTER_TYPE;
  99. CLUSTER_TYPE
  100. FatInterpretClusterType (
  101. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  102. IN FAT_ENTRY Entry
  103. );
  104. ARC_STATUS
  105. FatLookupFatEntry (
  106. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  107. IN ULONG DeviceId,
  108. IN ULONG FatIndex,
  109. OUT PULONG FatEntry,
  110. IN BOOLEAN IsDoubleSpace
  111. );
  112. ARC_STATUS
  113. FatSetFatEntry (
  114. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  115. IN ULONG DeviceId,
  116. IN FAT_ENTRY FatIndex,
  117. IN FAT_ENTRY FatEntry
  118. );
  119. ARC_STATUS
  120. FatFlushFatEntries (
  121. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  122. IN ULONG DeviceId
  123. );
  124. LBO
  125. FatIndexToLbo (
  126. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  127. IN FAT_ENTRY FatIndex
  128. );
  129. #define LookupFatEntry(A,B,C,D,E) { ARC_STATUS _s; \
  130. if ((_s = FatLookupFatEntry(A,B,C,D,E)) != ESUCCESS) { return _s; } \
  131. }
  132. #define SetFatEntry(A,B,C,D) { ARC_STATUS _s; \
  133. if ((_s = FatSetFatEntry(A,B,C,D)) != ESUCCESS) { return _s; } \
  134. }
  135. #define FlushFatEntries(A,B) { ARC_STATUS _s; \
  136. if ((_s = FatFlushFatEntries(A,B)) != ESUCCESS) { return _s; } \
  137. }
  138. //
  139. // Directory routines
  140. //
  141. ARC_STATUS
  142. FatSearchForDirent (
  143. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  144. IN ULONG DeviceId,
  145. IN FAT_ENTRY DirectoriesStartingIndex,
  146. IN PFAT8DOT3 FileName,
  147. OUT PDIRENT Dirent,
  148. OUT PLBO Lbo,
  149. IN BOOLEAN IsDoubleSpace
  150. );
  151. ARC_STATUS
  152. FatCreateDirent (
  153. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  154. IN ULONG DeviceId,
  155. IN FAT_ENTRY DirectoriesStartingIndex,
  156. IN PDIRENT Dirent,
  157. OUT PLBO Lbo
  158. );
  159. VOID
  160. FatSetDirent (
  161. IN PFAT8DOT3 FileName,
  162. IN OUT PDIRENT Dirent,
  163. IN UCHAR Attributes
  164. );
  165. #define SearchForDirent(A,B,C,D,E,F,G) { ARC_STATUS _s; \
  166. if ((_s = FatSearchForDirent(A,B,C,D,E,F,G)) != ESUCCESS) { return _s; } \
  167. }
  168. #define CreateDirent(A,B,C,D,E) { ARC_STATUS _s; \
  169. if ((_s = FatCreateDirent(A,B,C,D,E)) != ESUCCESS) { return _s; } \
  170. }
  171. //
  172. // Allocation and mcb routines
  173. //
  174. ARC_STATUS
  175. FatLoadMcb (
  176. IN ULONG FileId,
  177. IN VBO StartingVbo,
  178. IN BOOLEAN IsDoubleSpace
  179. );
  180. ARC_STATUS
  181. FatVboToLbo (
  182. IN ULONG FileId,
  183. IN VBO Vbo,
  184. OUT PLBO Lbo,
  185. OUT PULONG ByteCount,
  186. IN BOOLEAN IsDoubleSpace
  187. );
  188. ARC_STATUS
  189. FatIncreaseFileAllocation (
  190. IN ULONG FileId,
  191. IN ULONG ByteSize
  192. );
  193. ARC_STATUS
  194. FatTruncateFileAllocation (
  195. IN ULONG FileId,
  196. IN ULONG ByteSize
  197. );
  198. ARC_STATUS
  199. FatAllocateClusters (
  200. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  201. IN ULONG DeviceId,
  202. IN ULONG ClusterCount,
  203. IN ULONG Hint,
  204. OUT PULONG AllocatedEntry
  205. );
  206. #define LoadMcb(A,B,C) { ARC_STATUS _s; \
  207. if ((_s = FatLoadMcb(A,B,C)) != ESUCCESS) { return _s; } \
  208. }
  209. #define VboToLbo(A,B,C,D) { ARC_STATUS _s; \
  210. if ((_s = FatVboToLbo(A,B,C,D,FALSE)) != ESUCCESS) { return _s; } \
  211. }
  212. #define IncreaseFileAllocation(A,B) { ARC_STATUS _s; \
  213. if ((_s = FatIncreaseFileAllocation(A,B)) != ESUCCESS) { return _s; } \
  214. }
  215. #define TruncateFileAllocation(A,B) { ARC_STATUS _s; \
  216. if ((_s = FatTruncateFileAllocation(A,B)) != ESUCCESS) { return _s; } \
  217. }
  218. #define AllocateClusters(A,B,C,D,E) { ARC_STATUS _s; \
  219. if ((_s = FatAllocateClusters(A,B,C,D,E)) != ESUCCESS) { return _s; } \
  220. }
  221. //
  222. // Miscellaneous routines
  223. //
  224. VOID
  225. FatFirstComponent (
  226. IN OUT PSTRING String,
  227. OUT PFAT8DOT3 FirstComponent
  228. );
  229. #define AreNamesEqual(X,Y) ( \
  230. ((*(X))[0]==(*(Y))[0]) && ((*(X))[1]==(*(Y))[1]) && ((*(X))[2]==(*(Y))[2]) && \
  231. ((*(X))[3]==(*(Y))[3]) && ((*(X))[4]==(*(Y))[4]) && ((*(X))[5]==(*(Y))[5]) && \
  232. ((*(X))[6]==(*(Y))[6]) && ((*(X))[7]==(*(Y))[7]) && ((*(X))[8]==(*(Y))[8]) && \
  233. ((*(X))[9]==(*(Y))[9]) && ((*(X))[10]==(*(Y))[10]) \
  234. )
  235. #define ToUpper(C) ((((C) >= 'a') && ((C) <= 'z')) ? (C) - 'a' + 'A' : (C))
  236. #define FlagOn(Flags,SingleFlag) ((Flags) & (SingleFlag))
  237. #define BooleanFlagOn(Flags,SingleFlag) ((BOOLEAN)(((Flags) & (SingleFlag)) != 0))
  238. #define SetFlag(Flags,SingleFlag) { (Flags) |= (SingleFlag); }
  239. #define ClearFlag(Flags,SingleFlag) { (Flags) &= ~(SingleFlag); }
  240. #define FatFirstFatAreaLbo(B) ( (B)->ReservedSectors * (B)->BytesPerSector )
  241. #define Minimum(X,Y) ((X) < (Y) ? (X) : (Y))
  242. #define Maximum(X,Y) ((X) < (Y) ? (Y) : (X))
  243. //
  244. // The following types and macros are used to help unpack the packed and
  245. // misaligned fields found in the Bios parameter block
  246. //
  247. typedef union _UCHAR1 { UCHAR Uchar[1]; UCHAR ForceAlignment; } UCHAR1, *PUCHAR1;
  248. typedef union _UCHAR2 { UCHAR Uchar[2]; USHORT ForceAlignment; } UCHAR2, *PUCHAR2;
  249. typedef union _UCHAR4 { UCHAR Uchar[4]; ULONG ForceAlignment; } UCHAR4, *PUCHAR4;
  250. //
  251. // This macro copies an unaligned src byte to an aligned dst byte
  252. //
  253. #define CopyUchar1(Dst,Src) { \
  254. *((UCHAR1 *)(Dst)) = *((UNALIGNED UCHAR1 *)(Src)); \
  255. }
  256. //
  257. // This macro copies an unaligned src word to an aligned dst word
  258. //
  259. #define CopyUchar2(Dst,Src) { \
  260. *((UCHAR2 *)(Dst)) = *((UNALIGNED UCHAR2 *)(Src)); \
  261. }
  262. //
  263. // This macro copies an unaligned src longword to an aligned dsr longword
  264. //
  265. #define CopyUchar4(Dst,Src) { \
  266. *((UCHAR4 *)(Dst)) = *((UNALIGNED UCHAR4 *)(Src)); \
  267. }
  268. //
  269. // DirectoryEntry routines
  270. //
  271. VOID
  272. FatDirToArcDir (
  273. IN PDIRENT FatDirent,
  274. OUT PDIRECTORY_ENTRY ArcDirent
  275. );
  276. //
  277. // Define global data.
  278. //
  279. //
  280. // File entry table - This is a structure that provides entry to the FAT
  281. // file system procedures. It is exported when a FAT file structure
  282. // is recognized.
  283. //
  284. BL_DEVICE_ENTRY_TABLE FatDeviceEntryTable;
  285. PBL_DEVICE_ENTRY_TABLE
  286. IsFatFileStructure (
  287. IN ULONG DeviceId,
  288. IN PVOID StructureContext
  289. )
  290. /*++
  291. Routine Description:
  292. This routine determines if the partition on the specified channel
  293. contains a FAT file system volume.
  294. Arguments:
  295. DeviceId - Supplies the file table index for the device on which
  296. read operations are to be performed.
  297. StructureContext - Supplies a pointer to a FAT file structure context.
  298. Return Value:
  299. A pointer to the FAT entry table is returned if the partition is
  300. recognized as containing a FAT volume. Otherwise, NULL is returned.
  301. --*/
  302. {
  303. PPACKED_BOOT_SECTOR BootSector;
  304. UCHAR Buffer[sizeof(PACKED_BOOT_SECTOR)+256];
  305. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  306. FatDebugOutput("IsFatFileStructure\r\n", 0, 0);
  307. //
  308. // Clear the file system context block for the specified channel and
  309. // establish a pointer to the context structure that can be used by other
  310. // routines
  311. //
  312. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)StructureContext;
  313. RtlZeroMemory(FatStructureContext, sizeof(FAT_STRUCTURE_CONTEXT));
  314. //
  315. // Setup and read in the boot sector for the potential fat partition
  316. //
  317. BootSector = (PPACKED_BOOT_SECTOR)ALIGN_BUFFER( &Buffer[0] );
  318. if (FatDiskRead(DeviceId, 0, sizeof(PACKED_BOOT_SECTOR), BootSector, CACHE_NEW_DATA) != ESUCCESS) {
  319. return NULL;
  320. }
  321. //
  322. // Unpack the Bios parameter block
  323. //
  324. FatUnpackBios(&FatStructureContext->Bpb, &BootSector->PackedBpb);
  325. //
  326. // Check if it is fat
  327. //
  328. if ((BootSector->Jump[0] != 0xeb) &&
  329. (BootSector->Jump[0] != 0xe9)) {
  330. return NULL;
  331. } else if ((FatStructureContext->Bpb.BytesPerSector != 128) &&
  332. (FatStructureContext->Bpb.BytesPerSector != 256) &&
  333. (FatStructureContext->Bpb.BytesPerSector != 512) &&
  334. (FatStructureContext->Bpb.BytesPerSector != 1024)) {
  335. return NULL;
  336. } else if ((FatStructureContext->Bpb.SectorsPerCluster != 1) &&
  337. (FatStructureContext->Bpb.SectorsPerCluster != 2) &&
  338. (FatStructureContext->Bpb.SectorsPerCluster != 4) &&
  339. (FatStructureContext->Bpb.SectorsPerCluster != 8) &&
  340. (FatStructureContext->Bpb.SectorsPerCluster != 16) &&
  341. (FatStructureContext->Bpb.SectorsPerCluster != 32) &&
  342. (FatStructureContext->Bpb.SectorsPerCluster != 64) &&
  343. (FatStructureContext->Bpb.SectorsPerCluster != 128)) {
  344. return NULL;
  345. } else if (FatStructureContext->Bpb.ReservedSectors == 0) {
  346. return NULL;
  347. } else if (((FatStructureContext->Bpb.Sectors == 0) && (FatStructureContext->Bpb.LargeSectors == 0)) ||
  348. ((FatStructureContext->Bpb.Sectors != 0) && (FatStructureContext->Bpb.LargeSectors != 0))) {
  349. return NULL;
  350. } else if (FatStructureContext->Bpb.Fats == 0) {
  351. return NULL;
  352. } else if ((FatStructureContext->Bpb.Media != 0xf0) &&
  353. (FatStructureContext->Bpb.Media != 0xf8) &&
  354. (FatStructureContext->Bpb.Media != 0xf9) &&
  355. (FatStructureContext->Bpb.Media != 0xfc) &&
  356. (FatStructureContext->Bpb.Media != 0xfd) &&
  357. (FatStructureContext->Bpb.Media != 0xfe) &&
  358. (FatStructureContext->Bpb.Media != 0xff)) {
  359. return NULL;
  360. } else if (FatStructureContext->Bpb.SectorsPerFat == 0) {
  361. if (!IsBpbFat32(&BootSector->PackedBpb)) {
  362. return NULL;
  363. }
  364. } else if (FatStructureContext->Bpb.RootEntries == 0) {
  365. return NULL;
  366. }
  367. //
  368. // Initialize the file entry table and return the address of the table.
  369. //
  370. FatDeviceEntryTable.Open = FatOpen;
  371. FatDeviceEntryTable.Close = FatClose;
  372. FatDeviceEntryTable.Read = FatRead;
  373. FatDeviceEntryTable.Seek = FatSeek;
  374. FatDeviceEntryTable.Write = FatWrite;
  375. FatDeviceEntryTable.GetFileInformation = FatGetFileInformation;
  376. FatDeviceEntryTable.SetFileInformation = FatSetFileInformation;
  377. FatDeviceEntryTable.Rename = FatRename;
  378. FatDeviceEntryTable.GetDirectoryEntry = FatGetDirectoryEntry;
  379. FatDeviceEntryTable.BootFsInfo = &FatBootFsInfo;
  380. return &FatDeviceEntryTable;
  381. }
  382. ARC_STATUS
  383. FatClose (
  384. IN ULONG FileId
  385. )
  386. /*++
  387. Routine Description:
  388. This routine closes the file specified by the file id.
  389. Arguments:
  390. FileId - Supplies the file table index.
  391. Return Value:
  392. ESUCCESS if returned as the function value.
  393. --*/
  394. {
  395. PBL_FILE_TABLE FileTableEntry;
  396. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  397. ULONG DeviceId;
  398. FatDebugOutput("FatClose\r\n", 0, 0);
  399. //
  400. // Load our local variables
  401. //
  402. FileTableEntry = &BlFileTable[FileId];
  403. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  404. DeviceId = FileTableEntry->DeviceId;
  405. //
  406. // Mark the file closed
  407. //
  408. BlFileTable[FileId].Flags.Open = 0;
  409. //
  410. // Check if the fat is dirty and flush it out if it is.
  411. //
  412. if (FatStructureContext->CachedFatDirty) {
  413. FlushFatEntries( FatStructureContext, DeviceId );
  414. }
  415. //
  416. // Check if the current mcb is for this file and if it is then zero it out.
  417. // By setting the file id for the mcb to be the table size we guarantee that
  418. // we've just set it to an invalid file id.
  419. //
  420. if (FatStructureContext->FileId == FileId) {
  421. FatStructureContext->FileId = BL_FILE_TABLE_SIZE;
  422. FatStructureContext->Mcb.InUse = 0;
  423. }
  424. return ESUCCESS;
  425. }
  426. ARC_STATUS
  427. FatGetDirectoryEntry (
  428. IN ULONG FileId,
  429. IN DIRECTORY_ENTRY * FIRMWARE_PTR DirEntry,
  430. IN ULONG NumberDir,
  431. OUT ULONG * FIRMWARE_PTR CountDir
  432. )
  433. /*++
  434. Routine Description:
  435. This routine implements the GetDirectoryEntry operation for the
  436. FAT file system.
  437. Arguments:
  438. FileId - Supplies the file table index.
  439. DirEntry - Supplies a pointer to a directory entry structure.
  440. NumberDir - Supplies the number of directory entries to read.
  441. Count - Supplies a pointer to a variable to receive the number
  442. of entries read.
  443. Return Value:
  444. ESUCCESS is returned if the read was successful, otherwise
  445. an error code is returned.
  446. --*/
  447. {
  448. //
  449. // define local variables
  450. //
  451. ARC_STATUS Status; // ARC status
  452. ULONG Count = 0; // # of bytes read
  453. ULONG Position; // file position
  454. PFAT_FILE_CONTEXT pContext; // FAT file context
  455. ULONG RunByteCount = 0; // max sequential bytes
  456. ULONG RunDirCount; // max dir entries to read per time
  457. ULONG i; // general index
  458. PDIRENT FatDirEnt; // directory entry pointer
  459. UCHAR Buffer[ 16 * sizeof(DIRENT) + 32 ];
  460. LBO Lbo;
  461. BOOLEAN EofDir = FALSE; // not end of file
  462. //
  463. // initialize local variables
  464. //
  465. pContext = &BlFileTable[ FileId ].u.FatFileContext;
  466. FatDirEnt = (PDIRENT)ALIGN_BUFFER( &Buffer[0] );
  467. //
  468. // if not directory entry, exit with error
  469. //
  470. if ( !FlagOn(pContext->Dirent.Attributes, FAT_DIRENT_ATTR_DIRECTORY) ) {
  471. return EBADF;
  472. }
  473. //
  474. // Initialize the output count to zero
  475. //
  476. *CountDir = 0;
  477. //
  478. // if NumberDir is zero, return ESUCCESS.
  479. //
  480. if ( !NumberDir ) {
  481. return ESUCCESS;
  482. }
  483. //
  484. // read one directory at a time.
  485. //
  486. do {
  487. //
  488. // save position
  489. //
  490. Position = BlFileTable[ FileId ].Position.LowPart;
  491. //
  492. // Lookup the corresponding Lbo and run length for the current position
  493. //
  494. if ( !RunByteCount ) {
  495. if (Status = FatVboToLbo( FileId, Position, &Lbo, &RunByteCount, FALSE )) {
  496. if ( Status == EINVAL ) {
  497. break; // eof has been reached
  498. } else {
  499. return Status; // I/O error
  500. }
  501. }
  502. }
  503. //
  504. // validate the # of bytes readable in sequance (exit loop if eof)
  505. // the block is always multiple of a directory entry size.
  506. //
  507. if ( !(RunDirCount = Minimum( RunByteCount/sizeof(DIRENT), 16)) ) {
  508. break;
  509. }
  510. //
  511. // issue the read
  512. //
  513. if ( Status = FatDiskRead( BlFileTable[ FileId ].DeviceId,
  514. Lbo,
  515. RunDirCount * sizeof(DIRENT),
  516. (PVOID)FatDirEnt,
  517. CACHE_NEW_DATA)) {
  518. BlFileTable[ FileId ].Position.LowPart = Position;
  519. return Status;
  520. }
  521. for ( i=0; i<RunDirCount; i++ ) {
  522. //
  523. // exit from loop if logical end of directory
  524. //
  525. if ( FatDirEnt[i].FileName[0] == FAT_DIRENT_NEVER_USED ) {
  526. EofDir = TRUE;
  527. break;
  528. }
  529. //
  530. // update the current position and the number of bytes transfered
  531. //
  532. BlFileTable[ FileId ].Position.LowPart += sizeof(DIRENT);
  533. Lbo += sizeof(DIRENT);
  534. RunByteCount -= sizeof(DIRENT);
  535. //
  536. // skip this entry if the file or directory has been erased
  537. //
  538. if ( FatDirEnt[i].FileName[0] == FAT_DIRENT_DELETED ) {
  539. continue;
  540. }
  541. //
  542. // skip this entry if this is a valume label
  543. //
  544. if (FlagOn( FatDirEnt[i].Attributes, FAT_DIRENT_ATTR_VOLUME_ID )) {
  545. continue;
  546. }
  547. //
  548. // convert FAT directory entry in ARC directory entry
  549. //
  550. FatDirToArcDir( &FatDirEnt[i], DirEntry++ );
  551. //
  552. // update pointers
  553. //
  554. if ( ++*CountDir >= NumberDir ) {
  555. break;
  556. }
  557. }
  558. } while ( !EofDir && *CountDir < NumberDir );
  559. //
  560. // all done
  561. //
  562. return *CountDir ? ESUCCESS : ENOTDIR;
  563. }
  564. ARC_STATUS
  565. FatGetFileInformation (
  566. IN ULONG FileId,
  567. OUT PFILE_INFORMATION Buffer
  568. )
  569. /*++
  570. Routine Description:
  571. This procedure returns to the user a buffer filled with file information
  572. Arguments:
  573. FileId - Supplies the File id for the operation
  574. Buffer - Supplies the buffer to receive the file information. Note that
  575. it must be large enough to hold the full file name
  576. Return Value:
  577. ESUCCESS is returned if the open operation is successful. Otherwise,
  578. an unsuccessful status is returned that describes the reason for failure.
  579. --*/
  580. {
  581. PBL_FILE_TABLE FileTableEntry;
  582. UCHAR Attributes;
  583. ULONG i;
  584. FatDebugOutput("FatGetFileInformation\r\n", 0, 0);
  585. //
  586. // Load our local variables
  587. //
  588. FileTableEntry = &BlFileTable[FileId];
  589. Attributes = FileTableEntry->u.FatFileContext.Dirent.Attributes;
  590. //
  591. // Zero out the buffer, and fill in its non-zero values.
  592. //
  593. RtlZeroMemory(Buffer, sizeof(FILE_INFORMATION));
  594. Buffer->EndingAddress.LowPart = FileTableEntry->u.FatFileContext.Dirent.FileSize;
  595. Buffer->CurrentPosition.LowPart = FileTableEntry->Position.LowPart;
  596. Buffer->CurrentPosition.HighPart = 0;
  597. if (FlagOn(Attributes, FAT_DIRENT_ATTR_READ_ONLY)) { SetFlag(Buffer->Attributes, ArcReadOnlyFile) };
  598. if (FlagOn(Attributes, FAT_DIRENT_ATTR_HIDDEN)) { SetFlag(Buffer->Attributes, ArcHiddenFile) };
  599. if (FlagOn(Attributes, FAT_DIRENT_ATTR_SYSTEM)) { SetFlag(Buffer->Attributes, ArcSystemFile) };
  600. if (FlagOn(Attributes, FAT_DIRENT_ATTR_ARCHIVE)) { SetFlag(Buffer->Attributes, ArcArchiveFile) };
  601. if (FlagOn(Attributes, FAT_DIRENT_ATTR_DIRECTORY)) { SetFlag(Buffer->Attributes, ArcDirectoryFile) };
  602. Buffer->FileNameLength = FileTableEntry->FileNameLength;
  603. for (i = 0; i < FileTableEntry->FileNameLength; i += 1) {
  604. Buffer->FileName[i] = FileTableEntry->FileName[i];
  605. }
  606. return ESUCCESS;
  607. }
  608. ARC_STATUS
  609. FatOpen (
  610. IN CHAR * FIRMWARE_PTR FileName,
  611. IN OPEN_MODE OpenMode,
  612. IN ULONG * FIRMWARE_PTR FileId
  613. )
  614. /*++
  615. Routine Description:
  616. This routine searches the device for a file matching FileName.
  617. If a match is found the dirent for the file is saved and the file is
  618. opened.
  619. Arguments:
  620. FileName - Supplies a pointer to a zero terminated file name.
  621. OpenMode - Supplies the mode of the open.
  622. FileId - Supplies a pointer to a variable that specifies the file
  623. table entry that is to be filled in if the open is successful.
  624. Return Value:
  625. ESUCCESS is returned if the open operation is successful. Otherwise,
  626. an unsuccessful status is returned that describes the reason for failure.
  627. --*/
  628. {
  629. PBL_FILE_TABLE FileTableEntry;
  630. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  631. ULONG DeviceId;
  632. FAT_ENTRY CurrentDirectoryIndex;
  633. BOOLEAN SearchSucceeded;
  634. BOOLEAN IsDirectory;
  635. BOOLEAN IsReadOnly;
  636. STRING PathName;
  637. FAT8DOT3 Name;
  638. FatDebugOutput("FatOpen: %s\r\n", FileName, 0);
  639. //
  640. // Load our local variables
  641. //
  642. FileTableEntry = &BlFileTable[*FileId];
  643. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  644. DeviceId = FileTableEntry->DeviceId;
  645. //
  646. // Construct a file name descriptor from the input file name
  647. //
  648. RtlInitString( &PathName, FileName );
  649. //
  650. // While the path name has some characters in it we'll go through our loop
  651. // which extracts the first part of the path name and searches the current
  652. // directory for an entry. If what we find is a directory then we have to
  653. // continue looping until we're done with the path name.
  654. //
  655. FileTableEntry->u.FatFileContext.DirentLbo = 0;
  656. FileTableEntry->Position.LowPart = 0;
  657. FileTableEntry->Position.HighPart = 0;
  658. CurrentDirectoryIndex = 0;
  659. SearchSucceeded = TRUE;
  660. IsDirectory = TRUE;
  661. IsReadOnly = TRUE;
  662. if ((PathName.Buffer[0] == '\\') && (PathName.Length == 1)) {
  663. //
  664. // We are opening the root directory.
  665. //
  666. // N.B.: IsDirectory and SearchSucceeded are already TRUE.
  667. //
  668. PathName.Length = 0;
  669. FileTableEntry->FileNameLength = 1;
  670. FileTableEntry->FileName[0] = PathName.Buffer[0];
  671. //
  672. // Root dirent is all zeroes with a directory attribute.
  673. //
  674. RtlZeroMemory(&FileTableEntry->u.FatFileContext.Dirent, sizeof(DIRENT));
  675. FileTableEntry->u.FatFileContext.Dirent.Attributes = FAT_DIRENT_ATTR_DIRECTORY;
  676. FileTableEntry->u.FatFileContext.DirentLbo = 0;
  677. IsReadOnly = FALSE;
  678. CurrentDirectoryIndex = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
  679. } else {
  680. //
  681. // We are not opening the root directory.
  682. //
  683. //
  684. // If the search begins in a FAT32 root, set up the starting point
  685. // for the search.
  686. //
  687. if (IsBpbFat32(&FatStructureContext->Bpb)) {
  688. CurrentDirectoryIndex = FatStructureContext->Bpb.RootDirFirstCluster;
  689. }
  690. while ((PathName.Length > 0) && IsDirectory) {
  691. ARC_STATUS Status;
  692. //
  693. // Extract the first component and search the directory for a match, but
  694. // first copy the first part to the file name buffer in the file table entry
  695. //
  696. if (PathName.Buffer[0] == '\\') {
  697. PathName.Buffer +=1;
  698. PathName.Length -=1;
  699. }
  700. for (FileTableEntry->FileNameLength = 0;
  701. (((USHORT)FileTableEntry->FileNameLength < PathName.Length) &&
  702. (PathName.Buffer[FileTableEntry->FileNameLength] != '\\'));
  703. FileTableEntry->FileNameLength += 1) {
  704. FileTableEntry->FileName[FileTableEntry->FileNameLength] =
  705. PathName.Buffer[FileTableEntry->FileNameLength];
  706. }
  707. FatFirstComponent( &PathName, &Name );
  708. Status = FatSearchForDirent( FatStructureContext,
  709. DeviceId,
  710. CurrentDirectoryIndex,
  711. &Name,
  712. &FileTableEntry->u.FatFileContext.Dirent,
  713. &FileTableEntry->u.FatFileContext.DirentLbo,
  714. FALSE );
  715. if (Status == ENOENT) {
  716. SearchSucceeded = FALSE;
  717. break;
  718. }
  719. if (Status != ESUCCESS) {
  720. return Status;
  721. }
  722. //
  723. // We have a match now check to see if it is a directory, and also
  724. // if it is readonly
  725. //
  726. IsDirectory = BooleanFlagOn( FileTableEntry->u.FatFileContext.Dirent.Attributes,
  727. FAT_DIRENT_ATTR_DIRECTORY );
  728. IsReadOnly = BooleanFlagOn( FileTableEntry->u.FatFileContext.Dirent.Attributes,
  729. FAT_DIRENT_ATTR_READ_ONLY );
  730. if (IsDirectory) {
  731. CurrentDirectoryIndex = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
  732. if (IsBpbFat32(&FatStructureContext->Bpb)) {
  733. CurrentDirectoryIndex += 0x10000 *
  734. FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFileHi;
  735. }
  736. }
  737. }
  738. }
  739. //
  740. // If the path name length is not zero then we were trying to crack a path
  741. // with an nonexistent (or non directory) name in it. For example, we tried
  742. // to crack a\b\c\d and b is not a directory or does not exist (then the path
  743. // name will still contain c\d).
  744. //
  745. if (PathName.Length != 0) {
  746. return ENOTDIR;
  747. }
  748. //
  749. // At this point we've cracked the name up to (an maybe including the last
  750. // component). We located the last component if the SearchSucceeded flag is
  751. // true, otherwise the last component does not exist. If we located the last
  752. // component then this is like an open or a supersede, but not a create.
  753. //
  754. if (SearchSucceeded) {
  755. //
  756. // Check if the last component is a directory
  757. //
  758. if (IsDirectory) {
  759. //
  760. // For an existing directory the only valid open mode is OpenDirectory
  761. // all other modes return an error
  762. //
  763. switch (OpenMode) {
  764. case ArcOpenReadOnly:
  765. case ArcOpenWriteOnly:
  766. case ArcOpenReadWrite:
  767. case ArcCreateWriteOnly:
  768. case ArcCreateReadWrite:
  769. case ArcSupersedeWriteOnly:
  770. case ArcSupersedeReadWrite:
  771. //
  772. // If we reach here then the caller got a directory but didn't
  773. // want to open a directory
  774. //
  775. return EISDIR;
  776. case ArcOpenDirectory:
  777. //
  778. // If we reach here then the caller got a directory and wanted
  779. // to open a directory.
  780. //
  781. FileTableEntry->Flags.Open = 1;
  782. FileTableEntry->Flags.Read = 1;
  783. return ESUCCESS;
  784. case ArcCreateDirectory:
  785. //
  786. // If we reach here then the caller got a directory and wanted
  787. // to create a new directory
  788. //
  789. return EACCES;
  790. }
  791. }
  792. //
  793. // If we get there then we have an existing file that is being opened.
  794. // We can open existing files through a lot of different open modes in
  795. // some cases we need to check the read only part of file and/or truncate
  796. // the file.
  797. //
  798. switch (OpenMode) {
  799. case ArcOpenReadOnly:
  800. //
  801. // If we reach here then the user got a file and wanted to open the
  802. // file read only
  803. //
  804. FileTableEntry->Flags.Open = 1;
  805. FileTableEntry->Flags.Read = 1;
  806. return ESUCCESS;
  807. case ArcOpenWriteOnly:
  808. //
  809. // If we reach here then the user got a file and wanted to open the
  810. // file write only
  811. //
  812. if (IsReadOnly) { return EROFS; }
  813. FileTableEntry->Flags.Open = 1;
  814. FileTableEntry->Flags.Write = 1;
  815. return ESUCCESS;
  816. case ArcOpenReadWrite:
  817. //
  818. // If we reach here then the user got a file and wanted to open the
  819. // file read/write
  820. //
  821. if (IsReadOnly) { return EROFS; }
  822. FileTableEntry->Flags.Open = 1;
  823. FileTableEntry->Flags.Read = 1;
  824. FileTableEntry->Flags.Write = 1;
  825. return ESUCCESS;
  826. case ArcCreateWriteOnly:
  827. case ArcCreateReadWrite:
  828. //
  829. // If we reach here then the user got a file and wanted to create a new
  830. // file
  831. //
  832. return EACCES;
  833. case ArcSupersedeWriteOnly:
  834. //
  835. // If we reach here then the user got a file and wanted to supersede a
  836. // file
  837. //
  838. if (IsReadOnly) { return EROFS; }
  839. TruncateFileAllocation( *FileId, 0 );
  840. FileTableEntry->Flags.Open = 1;
  841. FileTableEntry->Flags.Read = 1;
  842. FileTableEntry->Flags.Write = 1;
  843. return ESUCCESS;
  844. case ArcSupersedeReadWrite:
  845. //
  846. // If we reach here then the user got a file and wanted to supersede a
  847. // file
  848. //
  849. if (IsReadOnly) { return EROFS; }
  850. TruncateFileAllocation( *FileId, 0 );
  851. FileTableEntry->Flags.Open = 1;
  852. FileTableEntry->Flags.Read = 1;
  853. FileTableEntry->Flags.Write = 1;
  854. return ESUCCESS;
  855. case ArcOpenDirectory:
  856. case ArcCreateDirectory:
  857. //
  858. // If we reach here then the user got a file and wanted a directory
  859. //
  860. return ENOTDIR;
  861. }
  862. }
  863. //
  864. // If we get here the last component does not exist so we are trying to create
  865. // either a new file or a directory.
  866. //
  867. switch (OpenMode) {
  868. case ArcOpenReadOnly:
  869. case ArcOpenWriteOnly:
  870. case ArcOpenReadWrite:
  871. //
  872. // If we reach here then the user did not get a file but wanted a file
  873. //
  874. return ENOENT;
  875. case ArcCreateWriteOnly:
  876. case ArcSupersedeWriteOnly:
  877. //
  878. // If we reach here then the user did not get a file and wanted to create
  879. // or supersede a file write only
  880. //
  881. RtlZeroMemory( &FileTableEntry->u.FatFileContext.Dirent, sizeof(DIRENT));
  882. FatSetDirent( &Name, &FileTableEntry->u.FatFileContext.Dirent, 0 );
  883. CreateDirent( FatStructureContext,
  884. DeviceId,
  885. CurrentDirectoryIndex,
  886. &FileTableEntry->u.FatFileContext.Dirent,
  887. &FileTableEntry->u.FatFileContext.DirentLbo );
  888. FileTableEntry->Flags.Open = 1;
  889. FileTableEntry->Flags.Write = 1;
  890. return ESUCCESS;
  891. case ArcCreateReadWrite:
  892. case ArcSupersedeReadWrite:
  893. //
  894. // If we reach here then the user did not get a file and wanted to create
  895. // or supersede a file read/write
  896. //
  897. RtlZeroMemory( &FileTableEntry->u.FatFileContext.Dirent, sizeof(DIRENT));
  898. FatSetDirent( &Name, &FileTableEntry->u.FatFileContext.Dirent, 0 );
  899. CreateDirent( FatStructureContext,
  900. DeviceId,
  901. CurrentDirectoryIndex,
  902. &FileTableEntry->u.FatFileContext.Dirent,
  903. &FileTableEntry->u.FatFileContext.DirentLbo );
  904. FileTableEntry->Flags.Open = 1;
  905. FileTableEntry->Flags.Read = 1;
  906. FileTableEntry->Flags.Write = 1;
  907. return ESUCCESS;
  908. case ArcOpenDirectory:
  909. //
  910. // If we reach here then the user did not get a file and wanted to open
  911. // an existing directory
  912. //
  913. return ENOENT;
  914. case ArcCreateDirectory:
  915. //
  916. // If we reach here then the user did not get a file and wanted to create
  917. // a new directory.
  918. //
  919. RtlZeroMemory( &FileTableEntry->u.FatFileContext.Dirent, sizeof(DIRENT));
  920. FatSetDirent( &Name,
  921. &FileTableEntry->u.FatFileContext.Dirent,
  922. FAT_DIRENT_ATTR_DIRECTORY );
  923. CreateDirent( FatStructureContext,
  924. DeviceId,
  925. CurrentDirectoryIndex,
  926. &FileTableEntry->u.FatFileContext.Dirent,
  927. &FileTableEntry->u.FatFileContext.DirentLbo );
  928. IncreaseFileAllocation( *FileId, sizeof(DIRENT) * 2 );
  929. {
  930. DIRENT Buffer;
  931. LBO Lbo;
  932. ULONG Count;
  933. ULONG i;
  934. ULONG Entry;
  935. RtlZeroMemory((PVOID)&Buffer.FileName[0], sizeof(DIRENT) );
  936. for (i = 0; i < 11; i += 1) {
  937. Buffer.FileName[i] = ' ';
  938. }
  939. Buffer.Attributes = FAT_DIRENT_ATTR_DIRECTORY;
  940. VboToLbo( *FileId, 0, &Lbo, &Count );
  941. Buffer.FileName[0] = FAT_DIRENT_DIRECTORY_ALIAS;
  942. Buffer.FirstClusterOfFile =
  943. FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
  944. Buffer.FirstClusterOfFileHi =
  945. FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFileHi;
  946. DiskWrite( DeviceId, Lbo, sizeof(DIRENT), (PVOID)&Buffer.FileName[0] );
  947. VboToLbo( *FileId, sizeof(DIRENT), &Lbo, &Count );
  948. Buffer.FileName[1] = FAT_DIRENT_DIRECTORY_ALIAS;
  949. Buffer.FirstClusterOfFile = (USHORT)CurrentDirectoryIndex;
  950. Buffer.FirstClusterOfFileHi = (USHORT)(CurrentDirectoryIndex >> 16);
  951. DiskWrite( DeviceId, Lbo, sizeof(DIRENT), (PVOID)&Buffer.FileName[0] );
  952. }
  953. FileTableEntry->Flags.Open = 1;
  954. FileTableEntry->Flags.Read = 1;
  955. return ESUCCESS;
  956. }
  957. return( EINVAL );
  958. }
  959. ARC_STATUS
  960. FatRead (
  961. IN ULONG FileId,
  962. OUT VOID * FIRMWARE_PTR Buffer,
  963. IN ULONG Length,
  964. OUT ULONG * FIRMWARE_PTR Transfer
  965. )
  966. /*++
  967. Routine Description:
  968. This routine reads data from the specified file.
  969. Arguments:
  970. FileId - Supplies the file table index.
  971. Buffer - Supplies a pointer to the buffer that receives the data
  972. read.
  973. Length - Supplies the number of bytes that are to be read.
  974. Transfer - Supplies a pointer to a variable that receives the number
  975. of bytes actually transfered.
  976. Return Value:
  977. ESUCCESS is returned if the read operation is successful. Otherwise,
  978. an unsuccessful status is returned that describes the reason for failure.
  979. --*/
  980. {
  981. PBL_FILE_TABLE FileTableEntry;
  982. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  983. ULONG DeviceId;
  984. FatDebugOutput("FatRead\r\n", 0, 0);
  985. //
  986. // Load out local variables
  987. //
  988. FileTableEntry = &BlFileTable[FileId];
  989. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  990. DeviceId = FileTableEntry->DeviceId;
  991. //
  992. // Clear the transfer count
  993. //
  994. *Transfer = 0;
  995. //
  996. // Read in runs (i.e., bytes) until the byte count goes to zero
  997. //
  998. while (Length > 0) {
  999. LBO Lbo;
  1000. ULONG CurrentRunByteCount;
  1001. //
  1002. // Lookup the corresponding Lbo and run length for the current position
  1003. // (i.e., Vbo).
  1004. //
  1005. if (FatVboToLbo( FileId, FileTableEntry->Position.LowPart, &Lbo, &CurrentRunByteCount, FALSE ) != ESUCCESS) {
  1006. return ESUCCESS;
  1007. }
  1008. //
  1009. // while there are bytes to be read in from the current run
  1010. // length and we haven't exhausted the request we loop reading
  1011. // in bytes. The biggest request we'll handle is only 32KB
  1012. // contiguous bytes per physical read. So we might need to loop
  1013. // through the run.
  1014. //
  1015. while ((Length > 0) && (CurrentRunByteCount > 0)) {
  1016. LONG SingleReadSize;
  1017. //
  1018. // Compute the size of the next physical read
  1019. //
  1020. SingleReadSize = Minimum(Length, 32 * 1024);
  1021. SingleReadSize = Minimum((ULONG)SingleReadSize, CurrentRunByteCount);
  1022. //
  1023. // Don't read beyond the eof
  1024. //
  1025. if (((ULONG)SingleReadSize + FileTableEntry->Position.LowPart) >
  1026. FileTableEntry->u.FatFileContext.Dirent.FileSize) {
  1027. SingleReadSize = FileTableEntry->u.FatFileContext.Dirent.FileSize -
  1028. FileTableEntry->Position.LowPart;
  1029. //
  1030. // If the readjusted read length is now zero then we're done.
  1031. //
  1032. if (SingleReadSize <= 0) {
  1033. return ESUCCESS;
  1034. }
  1035. //
  1036. // By also setting length here we'll make sure that this is our last
  1037. // read
  1038. //
  1039. Length = SingleReadSize;
  1040. }
  1041. //
  1042. // Issue the read
  1043. //
  1044. DiskRead( DeviceId, Lbo, SingleReadSize, Buffer, DONT_CACHE_NEW_DATA, FALSE );
  1045. //
  1046. // Update the remaining length, Current run byte count
  1047. // and new Lbo offset
  1048. //
  1049. Length -= SingleReadSize;
  1050. CurrentRunByteCount -= SingleReadSize;
  1051. Lbo += SingleReadSize;
  1052. //
  1053. // Update the current position and the number of bytes transfered
  1054. //
  1055. FileTableEntry->Position.LowPart += SingleReadSize;
  1056. *Transfer += SingleReadSize;
  1057. //
  1058. // Update buffer to point to the next byte location to fill in
  1059. //
  1060. Buffer = (PCHAR)Buffer + SingleReadSize;
  1061. }
  1062. }
  1063. //
  1064. // If we get here then remaining sector count is zero so we can
  1065. // return success to our caller
  1066. //
  1067. return ESUCCESS;
  1068. }
  1069. ARC_STATUS
  1070. FatRename(
  1071. IN ULONG FileId,
  1072. IN CHAR * FIRMWARE_PTR NewFileName
  1073. )
  1074. /*++
  1075. Routine Description:
  1076. This routine renames an open file. It does no checking to
  1077. see if the target filename already exists. It is intended for use
  1078. only when dual-booting DOS on x86 machines, where it is used to
  1079. replace the NT MVDM CONFIG.SYS and AUTOEXEC.BAT with the native DOS
  1080. CONFIG.SYS and AUTOEXEC.BAT files.
  1081. Arguments:
  1082. FileId - Supplies the file id of the file to be renamed
  1083. NewFileName - Supplies the new name for the file.
  1084. Return Value:
  1085. ARC_STATUS
  1086. --*/
  1087. {
  1088. PBL_FILE_TABLE FileTableEntry;
  1089. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  1090. ULONG DeviceId;
  1091. FAT8DOT3 FatName;
  1092. STRING String;
  1093. //
  1094. // Initialize our local variables
  1095. //
  1096. RtlInitString( &String, NewFileName );
  1097. FileTableEntry = &BlFileTable[FileId];
  1098. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  1099. DeviceId = FileTableEntry->DeviceId;
  1100. //
  1101. // Modify a in-memory copy of the dirent with the new name
  1102. //
  1103. FatFirstComponent( &String, &FatName );
  1104. FatSetDirent( &FatName,
  1105. &FileTableEntry->u.FatFileContext.Dirent,
  1106. FileTableEntry->u.FatFileContext.Dirent.Attributes );
  1107. //
  1108. // Write the modified dirent to disk
  1109. //
  1110. DiskWrite( DeviceId,
  1111. FileTableEntry->u.FatFileContext.DirentLbo,
  1112. sizeof(DIRENT),
  1113. &FileTableEntry->u.FatFileContext.Dirent );
  1114. //
  1115. // And return to our caller
  1116. //
  1117. return ESUCCESS;
  1118. }
  1119. ARC_STATUS
  1120. FatSeek (
  1121. IN ULONG FileId,
  1122. IN LARGE_INTEGER * FIRMWARE_PTR Offset,
  1123. IN SEEK_MODE SeekMode
  1124. )
  1125. /*++
  1126. Routine Description:
  1127. This routine seeks to the specified position for the file specified
  1128. by the file id.
  1129. Arguments:
  1130. FileId - Supplies the file table index.
  1131. Offset - Supplies the offset in the file to position to.
  1132. SeekMode - Supplies the mode of the seek operation.
  1133. Return Value:
  1134. ESUCCESS is returned if the seek operation is successful. Otherwise,
  1135. EINVAL is returned.
  1136. --*/
  1137. {
  1138. PBL_FILE_TABLE FileTableEntry;
  1139. ULONG NewPosition;
  1140. FatDebugOutput("FatSeek\r\n", 0, 0);
  1141. //
  1142. // Load our local variables
  1143. //
  1144. FileTableEntry = &BlFileTable[FileId];
  1145. //
  1146. // Compute the new position
  1147. //
  1148. if (SeekMode == SeekAbsolute) {
  1149. NewPosition = Offset->LowPart;
  1150. } else {
  1151. NewPosition = FileTableEntry->Position.LowPart + Offset->LowPart;
  1152. }
  1153. //
  1154. // If the new position is greater than the file size then return
  1155. // an error
  1156. //
  1157. if (NewPosition > FileTableEntry->u.FatFileContext.Dirent.FileSize) {
  1158. return EINVAL;
  1159. }
  1160. //
  1161. // Otherwise set the new position and return to our caller
  1162. //
  1163. FileTableEntry->Position.LowPart = NewPosition;
  1164. return ESUCCESS;
  1165. }
  1166. ARC_STATUS
  1167. FatSetFileInformation (
  1168. IN ULONG FileId,
  1169. IN ULONG AttributeFlags,
  1170. IN ULONG AttributeMask
  1171. )
  1172. /*++
  1173. Routine Description:
  1174. This routine sets the file attributes of the indicated file
  1175. Arguments:
  1176. FileId - Supplies the File Id for the operation
  1177. AttributeFlags - Supplies the value (on or off) for each attribute being modified
  1178. AttributeMask - Supplies a mask of the attributes being altered. All other
  1179. file attributes are left alone.
  1180. Return Value:
  1181. ESUCCESS is returned if the read operation is successful. Otherwise,
  1182. an unsuccessful status is returned that describes the reason for failure.
  1183. --*/
  1184. {
  1185. PBL_FILE_TABLE FileTableEntry;
  1186. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  1187. ULONG DeviceId;
  1188. UCHAR DirentAttributes;
  1189. UCHAR DirentMask;
  1190. UCHAR DirentFlags;
  1191. FatDebugOutput("FatSetFileInformation\r\n", 0, 0);
  1192. //
  1193. // Load our local variables
  1194. //
  1195. FileTableEntry = &BlFileTable[FileId];
  1196. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  1197. DeviceId = FileTableEntry->DeviceId;
  1198. DirentAttributes = FileTableEntry->u.FatFileContext.Dirent.Attributes;
  1199. //
  1200. // Check if this is the root directory
  1201. //
  1202. if (FileTableEntry->u.FatFileContext.DirentLbo == 0) {
  1203. return EACCES;
  1204. }
  1205. //
  1206. // Check if the users wishes to delete the file/directory
  1207. //
  1208. if (FlagOn(AttributeMask, ArcDeleteFile) && FlagOn(AttributeFlags, ArcDeleteFile)) {
  1209. //
  1210. // Check if the file/directory is marked read only
  1211. //
  1212. if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_READ_ONLY)) {
  1213. return EACCES;
  1214. }
  1215. //
  1216. // Check if this is a directory because we need to then check if the
  1217. // directory is empty
  1218. //
  1219. if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DIRECTORY)) {
  1220. ULONG BytesPerCluster;
  1221. FAT_ENTRY FatEntry;
  1222. CLUSTER_TYPE ClusterType;
  1223. BytesPerCluster = FatBytesPerCluster( &FatStructureContext->Bpb );
  1224. if (IsBpbFat32(&FatStructureContext->Bpb)) {
  1225. FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile |
  1226. (FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFileHi << 16);
  1227. } else {
  1228. FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
  1229. }
  1230. ClusterType = FatInterpretClusterType( FatStructureContext, FatEntry );
  1231. //
  1232. // Now loop through each cluster, and compute the starting Lbo for each
  1233. // cluster that we encounter
  1234. //
  1235. while (ClusterType == FatClusterNext) {
  1236. LBO ClusterLbo;
  1237. ULONG Offset;
  1238. ClusterLbo = FatIndexToLbo( FatStructureContext, FatEntry );
  1239. //
  1240. // Now for each dirent in the cluster compute the lbo, read in the dirent
  1241. // and check if it is in use
  1242. //
  1243. for (Offset = 0; Offset < BytesPerCluster; Offset += sizeof(DIRENT)) {
  1244. DIRENT Dirent;
  1245. DiskRead( DeviceId, Offset + ClusterLbo, sizeof(DIRENT), &Dirent, CACHE_NEW_DATA, FALSE );
  1246. if (Dirent.FileName[0] == FAT_DIRENT_NEVER_USED) {
  1247. break;
  1248. }
  1249. if ((Dirent.FileName[0] != FAT_DIRENT_DIRECTORY_ALIAS) ||
  1250. (Dirent.FileName[0] != FAT_DIRENT_DELETED)) {
  1251. return EACCES;
  1252. }
  1253. }
  1254. //
  1255. // Now that we've exhausted the current cluster we need to read
  1256. // in the next cluster. So locate the next fat entry in the chain
  1257. // and go back to the top of the while loop.
  1258. //
  1259. LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry, FALSE );
  1260. ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
  1261. }
  1262. }
  1263. //
  1264. // At this point the file/directory can be deleted so mark the name
  1265. // as deleted and write it back out
  1266. //
  1267. FileTableEntry->u.FatFileContext.Dirent.FileName[0] = FAT_DIRENT_DELETED;
  1268. DiskWrite( DeviceId,
  1269. FileTableEntry->u.FatFileContext.DirentLbo,
  1270. sizeof(DIRENT),
  1271. &FileTableEntry->u.FatFileContext.Dirent );
  1272. //
  1273. // And then truncate any file allocation assigned to the file
  1274. //
  1275. TruncateFileAllocation( FileId, 0);
  1276. return ESUCCESS;
  1277. }
  1278. //
  1279. // At this point the user does not want to delete the file so we only
  1280. // need to modify the attributes
  1281. //
  1282. DirentMask = 0;
  1283. DirentFlags = 0;
  1284. //
  1285. // Build up a mask and flag byte that correspond to the bits in the dirent
  1286. //
  1287. if (FlagOn(AttributeMask, ArcReadOnlyFile)) { SetFlag(DirentMask, FAT_DIRENT_ATTR_READ_ONLY); }
  1288. if (FlagOn(AttributeMask, ArcHiddenFile)) { SetFlag(DirentMask, FAT_DIRENT_ATTR_HIDDEN); }
  1289. if (FlagOn(AttributeMask, ArcSystemFile)) { SetFlag(DirentMask, FAT_DIRENT_ATTR_SYSTEM); }
  1290. if (FlagOn(AttributeMask, ArcArchiveFile)) { SetFlag(DirentMask, FAT_DIRENT_ATTR_ARCHIVE); }
  1291. if (FlagOn(AttributeFlags, ArcReadOnlyFile)) { SetFlag(DirentFlags, FAT_DIRENT_ATTR_READ_ONLY); }
  1292. if (FlagOn(AttributeFlags, ArcHiddenFile)) { SetFlag(DirentFlags, FAT_DIRENT_ATTR_HIDDEN); }
  1293. if (FlagOn(AttributeFlags, ArcSystemFile)) { SetFlag(DirentFlags, FAT_DIRENT_ATTR_SYSTEM); }
  1294. if (FlagOn(AttributeFlags, ArcArchiveFile)) { SetFlag(DirentFlags, FAT_DIRENT_ATTR_ARCHIVE); }
  1295. //
  1296. // The new attributes is calculated via the following formula
  1297. //
  1298. // Attributes = (~Mask & OldAttributes) | (Mask & NewAttributes);
  1299. //
  1300. // After we calculate the new attribute byte we write it out.
  1301. //
  1302. FileTableEntry->u.FatFileContext.Dirent.Attributes = (UCHAR)((~DirentMask & DirentAttributes) |
  1303. (DirentMask & DirentFlags));
  1304. DiskWrite( DeviceId,
  1305. FileTableEntry->u.FatFileContext.DirentLbo,
  1306. sizeof(DIRENT),
  1307. &FileTableEntry->u.FatFileContext.Dirent );
  1308. return ESUCCESS;
  1309. }
  1310. ARC_STATUS
  1311. FatWrite (
  1312. IN ULONG FileId,
  1313. IN VOID * FIRMWARE_PTR Buffer,
  1314. IN ULONG Length,
  1315. OUT ULONG * FIRMWARE_PTR Transfer
  1316. )
  1317. /*++
  1318. Routine Description:
  1319. This routine writes data to the specified file.
  1320. Arguments:
  1321. FileId - Supplies the file table index.
  1322. Buffer - Supplies a pointer to the buffer that contains the data
  1323. written.
  1324. Length - Supplies the number of bytes that are to be written.
  1325. Transfer - Supplies a pointer to a variable that receives the number
  1326. of bytes actually transfered.
  1327. Return Value:
  1328. ESUCCESS is returned if the write operation is successful. Otherwise,
  1329. an unsuccessful status is returned that describes the reason for failure.
  1330. --*/
  1331. {
  1332. PBL_FILE_TABLE FileTableEntry;
  1333. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  1334. ULONG DeviceId;
  1335. ULONG OffsetBeyondWrite;
  1336. FatDebugOutput("FatWrite\r\n", 0, 0);
  1337. //
  1338. // Load our local variables
  1339. //
  1340. FileTableEntry = &BlFileTable[FileId];
  1341. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  1342. DeviceId = FileTableEntry->DeviceId;
  1343. //
  1344. // Reset the file size to be the maximum of what is it now and the end of
  1345. // our write. We will assume that there is always enough allocation to support
  1346. // the file size, so we only need to increase allocation if we are increasing
  1347. // the file size.
  1348. //
  1349. OffsetBeyondWrite = FileTableEntry->Position.LowPart + Length;
  1350. if (OffsetBeyondWrite > FileTableEntry->u.FatFileContext.Dirent.FileSize) {
  1351. IncreaseFileAllocation( FileId, OffsetBeyondWrite );
  1352. FileTableEntry->u.FatFileContext.Dirent.FileSize = OffsetBeyondWrite;
  1353. DiskWrite( DeviceId,
  1354. FileTableEntry->u.FatFileContext.DirentLbo,
  1355. sizeof(DIRENT),
  1356. &FileTableEntry->u.FatFileContext.Dirent );
  1357. }
  1358. //
  1359. // Clear the transfer count
  1360. //
  1361. *Transfer = 0;
  1362. //
  1363. // Write out runs (i.e., bytes) until the byte count goes to zero
  1364. //
  1365. while (Length > 0) {
  1366. LBO Lbo;
  1367. ULONG CurrentRunByteCount;
  1368. //
  1369. // Lookup the corresponding Lbo and run length for the current position
  1370. // (i.e., Vbo).
  1371. //
  1372. VboToLbo( FileId, FileTableEntry->Position.LowPart, &Lbo, &CurrentRunByteCount );
  1373. //
  1374. // While there are bytes to be written out to the current run
  1375. // length and we haven't exhausted the request we loop reading
  1376. // in bytes. The biggest request we'll handle is only 32KB
  1377. // contiguous bytes per physical read. So we might need to loop
  1378. // through the run.
  1379. //
  1380. while ((Length > 0) && (CurrentRunByteCount > 0)) {
  1381. LONG SingleWriteSize;
  1382. //
  1383. // Compute the size of the next physical read
  1384. //
  1385. SingleWriteSize = Minimum(Length, 32 * 1024);
  1386. SingleWriteSize = Minimum((ULONG)SingleWriteSize, CurrentRunByteCount);
  1387. //
  1388. // Issue the Write
  1389. //
  1390. DiskWrite( DeviceId, Lbo, SingleWriteSize, Buffer);
  1391. //
  1392. // Update the remaining length, Current run byte count
  1393. // and new Lbo offset
  1394. //
  1395. Length -= SingleWriteSize;
  1396. CurrentRunByteCount -= SingleWriteSize;
  1397. Lbo += SingleWriteSize;
  1398. //
  1399. // Update the current position and the number of bytes transfered
  1400. //
  1401. FileTableEntry->Position.LowPart += SingleWriteSize;
  1402. *Transfer += SingleWriteSize;
  1403. //
  1404. // Update buffer to point to the next byte location to fill in
  1405. //
  1406. Buffer = (PCHAR)Buffer + SingleWriteSize;
  1407. }
  1408. }
  1409. //
  1410. // Check if the fat is dirty and flush it out if it is.
  1411. //
  1412. if (FatStructureContext->CachedFatDirty) {
  1413. FlushFatEntries( FatStructureContext, DeviceId );
  1414. }
  1415. //
  1416. // If we get here then remaining sector count is zero so we can
  1417. // return success to our caller
  1418. //
  1419. return ESUCCESS;
  1420. }
  1421. ARC_STATUS
  1422. FatInitialize (
  1423. VOID
  1424. )
  1425. /*++
  1426. Routine Description:
  1427. This routine initializes the fat boot filesystem.
  1428. Currently this is a no-op.
  1429. Arguments:
  1430. None.
  1431. Return Value:
  1432. ESUCCESS.
  1433. --*/
  1434. {
  1435. return ESUCCESS;
  1436. }
  1437. //
  1438. // Internal support routine
  1439. //
  1440. ARC_STATUS
  1441. FatDiskRead (
  1442. IN ULONG DeviceId,
  1443. IN LBO Lbo,
  1444. IN ULONG ByteCount,
  1445. IN PVOID Buffer,
  1446. IN BOOLEAN CacheNewData
  1447. )
  1448. /*++
  1449. Routine Description:
  1450. This routine reads in zero or more bytes from the specified device.
  1451. Arguments:
  1452. DeviceId - Supplies the device id to use in the arc calls.
  1453. Lbo - Supplies the LBO to start reading from.
  1454. ByteCount - Supplies the number of bytes to read.
  1455. Buffer - Supplies a pointer to the buffer to read the bytes into.
  1456. Return Value:
  1457. ESUCCESS is returned if the read operation is successful. Otherwise,
  1458. an unsuccessful status is returned that describes the reason for failure.
  1459. --*/
  1460. {
  1461. LARGE_INTEGER LargeLbo;
  1462. ARC_STATUS Status;
  1463. ULONG i;
  1464. //
  1465. // Special case the zero byte read request
  1466. //
  1467. if (ByteCount == 0) {
  1468. return ESUCCESS;
  1469. }
  1470. //
  1471. // Issue the read through the cache.
  1472. //
  1473. LargeLbo.QuadPart = Lbo;
  1474. Status = BlDiskCacheRead(DeviceId,
  1475. &LargeLbo,
  1476. Buffer,
  1477. ByteCount,
  1478. &i,
  1479. CacheNewData);
  1480. if (Status != ESUCCESS) {
  1481. return Status;
  1482. }
  1483. //
  1484. // Make sure we got back the amount requested
  1485. //
  1486. if (ByteCount != i) {
  1487. return EIO;
  1488. }
  1489. //
  1490. // Everything is fine so return success to our caller
  1491. //
  1492. return ESUCCESS;
  1493. }
  1494. //
  1495. // Internal support routine
  1496. //
  1497. ARC_STATUS
  1498. FatDiskWrite (
  1499. IN ULONG DeviceId,
  1500. IN LBO Lbo,
  1501. IN ULONG ByteCount,
  1502. IN PVOID Buffer
  1503. )
  1504. /*++
  1505. Routine Description:
  1506. This routine writes in zero or more bytes to the specified device.
  1507. Arguments:
  1508. DeviceId - Supplies the device id to use in the arc calls.
  1509. Lbo - Supplies the LBO to start writing from.
  1510. ByteCount - Supplies the number of bytes to write.
  1511. Buffer - Supplies a pointer to the buffer of bytes to write out.
  1512. Return Value:
  1513. ESUCCESS is returned if the write operation is successful. Otherwise,
  1514. an unsuccessful status is returned that describes the reason for failure.
  1515. --*/
  1516. {
  1517. LARGE_INTEGER LargeLbo;
  1518. ARC_STATUS Status;
  1519. ULONG i;
  1520. //
  1521. // Special case the zero byte write request
  1522. //
  1523. if (ByteCount == 0) {
  1524. return ESUCCESS;
  1525. }
  1526. //
  1527. // Issue the write through the cache.
  1528. //
  1529. LargeLbo.QuadPart = Lbo;
  1530. Status = BlDiskCacheWrite (DeviceId,
  1531. &LargeLbo,
  1532. Buffer,
  1533. ByteCount,
  1534. &i);
  1535. if (Status != ESUCCESS) {
  1536. return Status;
  1537. }
  1538. //
  1539. // Make sure we wrote out the amount requested
  1540. //
  1541. if (ByteCount != i) {
  1542. return EIO;
  1543. }
  1544. //
  1545. // Everything is fine so return success to our caller
  1546. //
  1547. return ESUCCESS;
  1548. }
  1549. //
  1550. // Internal support routine
  1551. //
  1552. CLUSTER_TYPE
  1553. FatInterpretClusterType (
  1554. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  1555. IN FAT_ENTRY Entry
  1556. )
  1557. /*++
  1558. Routine Description:
  1559. This procedure tells the caller how to interpret a fat table entry. It will
  1560. indicate if the fat cluster is available, reserved, bad, the last one, or another
  1561. fat index.
  1562. Arguments:
  1563. FatStructureContext - Supplies the volume structure for the operation
  1564. DeviceId - Supplies the DeviceId for the volume being used.
  1565. Entry - Supplies the fat entry to examine.
  1566. Return Value:
  1567. The type of the input fat entry is returned
  1568. --*/
  1569. {
  1570. //
  1571. // Check for 12 or 16 bit fat.
  1572. //
  1573. if (FatIndexBitSize(&FatStructureContext->Bpb) == 12) {
  1574. //
  1575. // For 12 bit fat check for one of the cluster types, but first
  1576. // make sure we only looking at 12 bits of the entry
  1577. //
  1578. Entry &= 0x00000fff;
  1579. if (Entry == 0x000) { return FatClusterAvailable; }
  1580. else if ((Entry >= 0xff0) && (Entry <= 0xff6)) { return FatClusterReserved; }
  1581. else if (Entry == 0xff7) { return FatClusterBad; }
  1582. else if ((Entry >= 0xff8) && (Entry <= 0xfff)) { return FatClusterLast; }
  1583. else { return FatClusterNext; }
  1584. } else if (FatIndexBitSize(&FatStructureContext->Bpb) == 32) {
  1585. Entry &= 0x0fffffff;
  1586. if (Entry == 0x0000) { return FatClusterAvailable; }
  1587. else if (Entry == 0x0ffffff7) { return FatClusterBad; }
  1588. else if ((Entry >= 0x0ffffff8)) { return FatClusterLast; }
  1589. else { return FatClusterNext; }
  1590. } else {
  1591. //
  1592. // For 16 bit fat check for one of the cluster types, but first
  1593. // make sure we are only looking at 16 bits of the entry
  1594. //
  1595. Entry &= 0x0000ffff;
  1596. if (Entry == 0x0000) { return FatClusterAvailable; }
  1597. else if ((Entry >= 0xfff0) && (Entry <= 0xfff6)) { return FatClusterReserved; }
  1598. else if (Entry == 0xfff7) { return FatClusterBad; }
  1599. else if ((Entry >= 0xfff8) && (Entry <= 0xffff)) { return FatClusterLast; }
  1600. else { return FatClusterNext; }
  1601. }
  1602. }
  1603. //
  1604. // Internal support routine
  1605. //
  1606. ARC_STATUS
  1607. FatLookupFatEntry (
  1608. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  1609. IN ULONG DeviceId,
  1610. IN ULONG FatIndex,
  1611. OUT PULONG FatEntry,
  1612. IN BOOLEAN IsDoubleSpace
  1613. )
  1614. /*++
  1615. Routine Description:
  1616. This routine returns the value stored within the fat table and the specified
  1617. fat index. It is semantically equivalent to doing
  1618. x = Fat[FatIndex]
  1619. Arguments:
  1620. FatStrutureContext - Supplies the volume struture being used
  1621. DeviceId - Supplies the device being used
  1622. FatIndex - Supplies the index being looked up.
  1623. FatEntry - Receives the value stored at the specified fat index
  1624. IsDoubleSpace - Indicates if the search is being done on a double space volume
  1625. Return Value:
  1626. ESUCCESS is returned if the operation is successful. Otherwise,
  1627. an unsuccessful status is returned that describes the reason for failure.
  1628. --*/
  1629. {
  1630. BOOLEAN TwelveBitFat;
  1631. VBO Vbo;
  1632. //****if (IsDoubleSpace) { DbgPrint("FatLookupFatEntry(%0x,%0x,%0x,%0x,%0x)\n",FatStructureContext, DeviceId, FatIndex, FatEntry, IsDoubleSpace); }
  1633. //
  1634. // Calculate the Vbo of the word in the fat we need and
  1635. // also figure out if this is a 12 or 16 bit fat
  1636. //
  1637. if (FatIndexBitSize( &FatStructureContext->Bpb ) == 12) {
  1638. TwelveBitFat = TRUE;
  1639. Vbo = (FatIndex * 3) / 2;
  1640. } else if (FatIndexBitSize( &FatStructureContext->Bpb ) == 32) {
  1641. TwelveBitFat = FALSE;
  1642. Vbo = FatIndex * 4;
  1643. } else {
  1644. TwelveBitFat = FALSE;
  1645. Vbo = FatIndex * 2;
  1646. }
  1647. //
  1648. // Check if the Vbo we need is already in the cached fat
  1649. //
  1650. if ((FatStructureContext->CachedFat == NULL) ||
  1651. (Vbo < FatStructureContext->CachedFatVbo) ||
  1652. ((Vbo+1) > (FatStructureContext->CachedFatVbo + FAT_CACHE_SIZE))) {
  1653. //
  1654. // Set the aligned cached fat buffer in the structure context
  1655. //
  1656. FatStructureContext->CachedFat = ALIGN_BUFFER( &FatStructureContext->CachedFatBuffer[0] );
  1657. //
  1658. // As a safety net we'll flush any dirty fats that we might have cached before
  1659. // we turn the window
  1660. //
  1661. if (!IsDoubleSpace && FatStructureContext->CachedFatDirty) {
  1662. FlushFatEntries( FatStructureContext, DeviceId );
  1663. }
  1664. //
  1665. // Now set the new cached Vbo to be the Vbo of the cache sized section that
  1666. // we're trying to map. Each time we read in the cache we only read in
  1667. // cache sized and cached aligned pieces of the fat. So first compute an
  1668. // aligned cached fat vbo and then do the read.
  1669. //
  1670. FatStructureContext->CachedFatVbo = (Vbo / FAT_CACHE_SIZE) * FAT_CACHE_SIZE;
  1671. DiskRead( DeviceId,
  1672. FatStructureContext->CachedFatVbo + FatFirstFatAreaLbo(&FatStructureContext->Bpb),
  1673. FAT_CACHE_SIZE,
  1674. FatStructureContext->CachedFat,
  1675. CACHE_NEW_DATA,
  1676. IsDoubleSpace );
  1677. }
  1678. //
  1679. // At this point the cached fat contains the vbo we're after so simply
  1680. // extract the word
  1681. //
  1682. if (IsBpbFat32(&FatStructureContext->Bpb)) {
  1683. CopyUchar4( FatEntry,
  1684. &FatStructureContext->CachedFat[Vbo - FatStructureContext->CachedFatVbo] );
  1685. } else {
  1686. CopyUchar2( FatEntry,
  1687. &FatStructureContext->CachedFat[Vbo - FatStructureContext->CachedFatVbo] );
  1688. }
  1689. //
  1690. // Now if this is a 12 bit fat then check if the index is odd or even
  1691. // If it is odd then we need to shift it over 4 bits, and in all
  1692. // cases we need to mask out the high 4 bits.
  1693. //
  1694. if (TwelveBitFat) {
  1695. if ((FatIndex % 2) == 1) { *FatEntry >>= 4; }
  1696. *FatEntry &= 0x0fff;
  1697. }
  1698. return ESUCCESS;
  1699. }
  1700. //
  1701. // Internal support routine
  1702. //
  1703. ARC_STATUS
  1704. FatSetFatEntry(
  1705. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  1706. IN ULONG DeviceId,
  1707. IN FAT_ENTRY FatIndex,
  1708. IN FAT_ENTRY FatEntry
  1709. )
  1710. /*++
  1711. Routine Description:
  1712. This procedure sets the data within the fat table at the specified index to
  1713. to the specified value. It is semantically equivalent to doing
  1714. Fat[FatIndex] = FatEntry;
  1715. Arguments:
  1716. FatStructureContext - Supplies the structure context for the operation
  1717. DeviceId - Supplies the device for the operation
  1718. FatIndex - Supplies the index within the fat table to set
  1719. FatEntry - Supplies the value to store within the fat table
  1720. Return Value:
  1721. ESUCCESS is returned if the operation is successful. Otherwise,
  1722. an unsuccessful status is returned that describes the reason for failure.
  1723. --*/
  1724. {
  1725. BOOLEAN TwelveBitFat;
  1726. VBO Vbo;
  1727. //
  1728. // Calculate the Vbo of the word in the fat we are modifying and
  1729. // also figure out if this is a 12 or 16 bit fat
  1730. //
  1731. if (FatIndexBitSize( &FatStructureContext->Bpb ) == 12) {
  1732. TwelveBitFat = TRUE;
  1733. Vbo = (FatIndex * 3) / 2;
  1734. } else if (FatIndexBitSize( &FatStructureContext->Bpb ) == 32) {
  1735. TwelveBitFat = FALSE;
  1736. Vbo = FatIndex * 4;
  1737. } else {
  1738. TwelveBitFat = FALSE;
  1739. Vbo = FatIndex * 2;
  1740. }
  1741. //
  1742. // Check if the Vbo we need is already in the cached fat
  1743. //
  1744. if ((FatStructureContext->CachedFat == NULL) ||
  1745. (Vbo < FatStructureContext->CachedFatVbo) ||
  1746. ((Vbo+1) > (FatStructureContext->CachedFatVbo + FAT_CACHE_SIZE))) {
  1747. //
  1748. // Set the aligned cached fat buffer in the structure context
  1749. //
  1750. FatStructureContext->CachedFat = ALIGN_BUFFER( &FatStructureContext->CachedFatBuffer[0] );
  1751. //
  1752. // As a safety net we'll flush any dirty fats that we might have cached before
  1753. // we turn the window
  1754. //
  1755. if (FatStructureContext->CachedFatDirty) {
  1756. FlushFatEntries( FatStructureContext, DeviceId );
  1757. }
  1758. //
  1759. // Now set the new cached Vbo to be the Vbo of the cache sized section that
  1760. // we're trying to map. Each time we read in the cache we only read in
  1761. // cache sized and cached aligned pieces of the fat. So first compute an
  1762. // aligned cached fat vbo and then do the read.
  1763. //
  1764. FatStructureContext->CachedFatVbo = (Vbo / FAT_CACHE_SIZE) * FAT_CACHE_SIZE;
  1765. DiskRead( DeviceId,
  1766. FatStructureContext->CachedFatVbo + FatFirstFatAreaLbo(&FatStructureContext->Bpb),
  1767. FAT_CACHE_SIZE,
  1768. FatStructureContext->CachedFat,
  1769. CACHE_NEW_DATA,
  1770. FALSE );
  1771. }
  1772. //
  1773. // At this point the cached fat contains the vbo we're after. For a 16 bit
  1774. // fat we simply put in the fat entry. For the 12 bit fat we first need to extract
  1775. // the word containing the entry, modify the word, and then put it back.
  1776. //
  1777. if (TwelveBitFat) {
  1778. FAT_ENTRY Temp;
  1779. CopyUchar2( &Temp,
  1780. &FatStructureContext->CachedFat[Vbo - FatStructureContext->CachedFatVbo] );
  1781. if ((FatIndex % 2) == 0) {
  1782. FatEntry = (FAT_ENTRY)((Temp & 0xf000) | (FatEntry & 0x0fff));
  1783. } else {
  1784. FatEntry = (FAT_ENTRY)((Temp & 0x000f) | ((FatEntry << 4) & 0xfff0));
  1785. }
  1786. }
  1787. if (IsBpbFat32(&FatStructureContext->Bpb)) {
  1788. CopyUchar4( &FatStructureContext->CachedFat[Vbo - FatStructureContext->CachedFatVbo],
  1789. &FatEntry );
  1790. } else {
  1791. CopyUchar2( &FatStructureContext->CachedFat[Vbo - FatStructureContext->CachedFatVbo],
  1792. &FatEntry );
  1793. }
  1794. //
  1795. // Now that we're done we can set the fat dirty
  1796. //
  1797. FatStructureContext->CachedFatDirty = TRUE;
  1798. return ESUCCESS;
  1799. }
  1800. //
  1801. // Internal support routine
  1802. //
  1803. ARC_STATUS
  1804. FatFlushFatEntries (
  1805. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  1806. IN ULONG DeviceId
  1807. )
  1808. /*++
  1809. Routine Description:
  1810. This routine flushes out any dirty cached fat entries to the volume.
  1811. Arguments:
  1812. FatStructureContext - Supplies the structure context for the operation
  1813. DeviceId - Supplies the Device for the operation
  1814. Return Value:
  1815. ESUCCESS is returned if the operation is successful. Otherwise,
  1816. an unsuccessful status is returned that describes the reason for failure.
  1817. --*/
  1818. {
  1819. ULONG BytesPerFat;
  1820. ULONG AmountToWrite;
  1821. ULONG i;
  1822. //
  1823. // Compute the actual number of bytes that we need to write. We do this
  1824. // because we don't want to overwrite beyond the fat.
  1825. //
  1826. BytesPerFat = FatBytesPerFat(&FatStructureContext->Bpb);
  1827. if (FatStructureContext->CachedFatVbo + FAT_CACHE_SIZE <= BytesPerFat) {
  1828. AmountToWrite = FAT_CACHE_SIZE;
  1829. } else {
  1830. AmountToWrite = BytesPerFat - FatStructureContext->CachedFatVbo;
  1831. }
  1832. //
  1833. // For each fat table on the volume we will calculate the lbo for the operation
  1834. // and then write out the cached fat
  1835. //
  1836. for (i = 0; i < FatStructureContext->Bpb.Fats; i += 1) {
  1837. LBO Lbo;
  1838. Lbo = FatStructureContext->CachedFatVbo +
  1839. FatFirstFatAreaLbo(&FatStructureContext->Bpb) +
  1840. (i * BytesPerFat);
  1841. DiskWrite( DeviceId,
  1842. Lbo,
  1843. AmountToWrite,
  1844. FatStructureContext->CachedFat );
  1845. }
  1846. //
  1847. // we are all done so now mark the fat clean
  1848. //
  1849. FatStructureContext->CachedFatDirty = FALSE;
  1850. return ESUCCESS;
  1851. }
  1852. //
  1853. // Internal support routine
  1854. //
  1855. LBO
  1856. FatIndexToLbo (
  1857. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  1858. IN FAT_ENTRY FatIndex
  1859. )
  1860. /*++
  1861. Routine Description:
  1862. This procedure translates a fat index into its corresponding lbo.
  1863. Arguments:
  1864. FatStructureContext - Supplies the volume structure for the operation
  1865. Entry - Supplies the fat entry to examine.
  1866. Return Value:
  1867. The LBO for the input fat index is returned
  1868. --*/
  1869. {
  1870. //
  1871. // The formula for translating an index into an lbo is to take the index subtract
  1872. // 2 (because index values 0 and 1 are reserved) multiply that by the bytes per
  1873. // cluster and add the results to the first file area lbo.
  1874. //
  1875. return ((FatIndex-2) * (LBO) FatBytesPerCluster(&FatStructureContext->Bpb))
  1876. + FatFileAreaLbo(&FatStructureContext->Bpb);
  1877. }
  1878. //
  1879. // Internal support routine
  1880. //
  1881. ARC_STATUS
  1882. FatSearchForDirent (
  1883. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  1884. IN ULONG DeviceId,
  1885. IN FAT_ENTRY DirectoriesStartingIndex,
  1886. IN PFAT8DOT3 FileName,
  1887. OUT PDIRENT Dirent,
  1888. OUT PLBO Lbo,
  1889. IN BOOLEAN IsDoubleSpace
  1890. )
  1891. /*++
  1892. Routine Description:
  1893. The procedure searches the indicated directory for a dirent that matches
  1894. the input file name.
  1895. Arguments:
  1896. FatStructureContext - Supplies the structure context for the operation
  1897. DeviceId - Supplies the Device id for the operation
  1898. DirectoriesStartingIndex - Supplies the fat index of the directory we are
  1899. to search. A value of zero indicates that we are searching the root directory
  1900. of a non-FAT32 volume. FAT32 volumes will have a non-zero index.
  1901. FileName - Supplies the file name to look for. The name must have already been
  1902. biased by the 0xe5 transmogrification
  1903. Dirent - The caller supplies the memory for a dirent and this procedure will
  1904. fill in the dirent if one is located
  1905. Lbo - Receives the Lbo of the dirent if one is located
  1906. IsDoubleSpace - Indicates if the search is being done on a double space volume
  1907. Return Value:
  1908. ESUCCESS is returned if the operation is successful. Otherwise,
  1909. an unsuccessful status is returned that describes the reason for failure.
  1910. --*/
  1911. {
  1912. PDIRENT DirentBuffer;
  1913. UCHAR Buffer[ 16 * sizeof(DIRENT) + 256 ];
  1914. ULONG i;
  1915. ULONG j;
  1916. ULONG BytesPerCluster;
  1917. FAT_ENTRY FatEntry;
  1918. CLUSTER_TYPE ClusterType;
  1919. DirentBuffer = (PDIRENT)ALIGN_BUFFER( &Buffer[0] );
  1920. FatDebugOutput83("FatSearchForDirent: %s\r\n", FileName, 0, 0);
  1921. //****if (IsDoubleSpace) { (*FileName)[11] = 0; DbgPrint("FatSearchForDirent(%0x,%0x,%0x,\"%11s\",%0x,%0x,%0x)\n", FatStructureContext, DeviceId, DirectoriesStartingIndex, FileName, Dirent, Lbo, IsDoubleSpace); }
  1922. //
  1923. // Check if this is the root directory that is being searched
  1924. //
  1925. if (DirectoriesStartingIndex == FAT_CLUSTER_AVAILABLE) {
  1926. VBO Vbo;
  1927. ULONG RootLbo = FatRootDirectoryLbo(&FatStructureContext->Bpb);
  1928. ULONG RootSize = FatRootDirectorySize(&FatStructureContext->Bpb);
  1929. //
  1930. // For the root directory we'll zoom down the dirents until we find
  1931. // a match, or run out of dirents or hit the never used dirent.
  1932. // The outer loop reads in 512 bytes of the directory at a time into
  1933. // dirent buffer.
  1934. //
  1935. for (Vbo = 0; Vbo < RootSize; Vbo += 16 * sizeof(DIRENT)) {
  1936. *Lbo = Vbo + RootLbo;
  1937. DiskRead( DeviceId, *Lbo, 16 * sizeof(DIRENT), DirentBuffer, CACHE_NEW_DATA, IsDoubleSpace );
  1938. //
  1939. // The inner loop cycles through the 16 dirents that we've just read in
  1940. //
  1941. for (i = 0; i < 16; i += 1) {
  1942. //
  1943. // Check if we've found a non label match for file name, and if so
  1944. // then copy the buffer into the dirent and set the real lbo
  1945. // of the dirent and return
  1946. //
  1947. if (!FlagOn(DirentBuffer[i].Attributes, FAT_DIRENT_ATTR_VOLUME_ID ) &&
  1948. AreNamesEqual(&DirentBuffer[i].FileName, FileName)) {
  1949. for (j = 0; j < sizeof(DIRENT); j += 1) {
  1950. ((PCHAR)Dirent)[j] = ((PCHAR)DirentBuffer)[(i * sizeof(DIRENT)) + j];
  1951. }
  1952. *Lbo = Vbo + RootLbo + (i * sizeof(DIRENT));
  1953. return ESUCCESS;
  1954. }
  1955. if (DirentBuffer[i].FileName[0] == FAT_DIRENT_NEVER_USED) {
  1956. return ENOENT;
  1957. }
  1958. }
  1959. }
  1960. return ENOENT;
  1961. }
  1962. //
  1963. // If we get here we need to search a non-root directory. The alrogithm
  1964. // for doing the search is that for each cluster we read in each dirent
  1965. // until we find a match, or run out of clusters, or hit the never used
  1966. // dirent. First set some local variables and then get the cluster type
  1967. // of the first cluster
  1968. //
  1969. BytesPerCluster = FatBytesPerCluster( &FatStructureContext->Bpb );
  1970. FatEntry = DirectoriesStartingIndex;
  1971. ClusterType = FatInterpretClusterType( FatStructureContext, FatEntry );
  1972. //
  1973. // Now loop through each cluster, and compute the starting Lbo for each cluster
  1974. // that we encounter
  1975. //
  1976. while (ClusterType == FatClusterNext) {
  1977. LBO ClusterLbo;
  1978. ULONG Offset;
  1979. ClusterLbo = FatIndexToLbo( FatStructureContext, FatEntry );
  1980. //
  1981. // Now for each dirent in the cluster compute the lbo, read in the dirent
  1982. // and check for a match, the outer loop reads in 512 bytes of dirents at
  1983. // a time.
  1984. //
  1985. for (Offset = 0; Offset < BytesPerCluster; Offset += 16 * sizeof(DIRENT)) {
  1986. *Lbo = Offset + ClusterLbo;
  1987. DiskRead( DeviceId, *Lbo, 16 * sizeof(DIRENT), DirentBuffer, CACHE_NEW_DATA, IsDoubleSpace );
  1988. //
  1989. // The inner loop cycles through the 16 dirents that we've just read in
  1990. //
  1991. for (i = 0; i < 16; i += 1) {
  1992. //
  1993. // Check if we've found a for file name, and if so
  1994. // then copy the buffer into the dirent and set the real lbo
  1995. // of the dirent and return
  1996. //
  1997. if (!FlagOn(DirentBuffer[i].Attributes, FAT_DIRENT_ATTR_VOLUME_ID ) &&
  1998. AreNamesEqual(&DirentBuffer[i].FileName, FileName)) {
  1999. for (j = 0; j < sizeof(DIRENT); j += 1) {
  2000. ((PCHAR)Dirent)[j] = ((PCHAR)DirentBuffer)[(i * sizeof(DIRENT)) + j];
  2001. }
  2002. *Lbo = Offset + ClusterLbo + (i * sizeof(DIRENT));
  2003. return ESUCCESS;
  2004. }
  2005. if (DirentBuffer[i].FileName[0] == FAT_DIRENT_NEVER_USED) {
  2006. return ENOENT;
  2007. }
  2008. }
  2009. }
  2010. //
  2011. // Now that we've exhausted the current cluster we need to read
  2012. // in the next cluster. So locate the next fat entry in the chain
  2013. // and go back to the top of the while loop.
  2014. //
  2015. LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry, IsDoubleSpace );
  2016. ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
  2017. }
  2018. return ENOENT;
  2019. }
  2020. //
  2021. // Internal support routine
  2022. //
  2023. ARC_STATUS
  2024. FatCreateDirent (
  2025. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  2026. IN ULONG DeviceId,
  2027. IN FAT_ENTRY DirectoriesStartingIndex,
  2028. IN PDIRENT Dirent,
  2029. OUT PLBO Lbo
  2030. )
  2031. /*++
  2032. Routine Description:
  2033. This procedure allocates and write out a new dirent for a data file in the
  2034. specified directory. It assumes that the file name does not already exist.
  2035. Arguments:
  2036. FatStructureContext - Supplies the structure context for the operation
  2037. DeviceId - Supplies the device id for the operation
  2038. DirectoriesStartingIndex - Supplies the fat index of the directory we are
  2039. to use. A value of zero indicates that we are using the root directory
  2040. Dirent - Supplies a copy of the dirent to put out on the disk
  2041. Lbo - Recieves the Lbo of where the dirent is placed
  2042. Return Value:
  2043. ESUCCESS is returned if the operation is successful. Otherwise,
  2044. an unsuccessful status is returned that describes the reason for failure.
  2045. --*/
  2046. {
  2047. DIRENT TemporaryDirent;
  2048. ULONG BytesPerCluster;
  2049. FAT_ENTRY FatEntry;
  2050. FAT_ENTRY PreviousEntry;
  2051. //
  2052. // Check if this is the root directory that is being used
  2053. //
  2054. if (DirectoriesStartingIndex == FAT_CLUSTER_AVAILABLE) {
  2055. VBO Vbo;
  2056. ULONG RootLbo = FatRootDirectoryLbo(&FatStructureContext->Bpb);
  2057. ULONG RootSize = FatRootDirectorySize(&FatStructureContext->Bpb);
  2058. //
  2059. // For the root directory we'll zoom down the dirents until we find
  2060. // a the never used (or deleted) dirent, if we never find one then the
  2061. // directory is full.
  2062. //
  2063. for (Vbo = 0; Vbo < RootSize; Vbo += sizeof(DIRENT)) {
  2064. *Lbo = Vbo + RootLbo;
  2065. DiskRead( DeviceId, *Lbo, sizeof(DIRENT), &TemporaryDirent, CACHE_NEW_DATA, FALSE );
  2066. if ((TemporaryDirent.FileName[0] == FAT_DIRENT_DELETED) ||
  2067. (TemporaryDirent.FileName[0] == FAT_DIRENT_NEVER_USED)) {
  2068. //
  2069. // This dirent is free so write out the dirent, and we're done.
  2070. //
  2071. DiskWrite( DeviceId, *Lbo, sizeof(DIRENT), Dirent );
  2072. return ESUCCESS;
  2073. }
  2074. }
  2075. return ENOSPC;
  2076. }
  2077. //
  2078. // If we get here we need to use a non-root directory. The alrogithm
  2079. // for doing the work is that for each cluster we read in each dirent
  2080. // until we hit a never used dirent or run out of clusters. First set
  2081. // some local variables and then get the cluster type of the first
  2082. // cluster
  2083. //
  2084. BytesPerCluster = FatBytesPerCluster( &FatStructureContext->Bpb );
  2085. FatEntry = DirectoriesStartingIndex;
  2086. //
  2087. // Now loop through each cluster, and compute the starting Lbo for each cluster
  2088. // that we encounter
  2089. //
  2090. while (TRUE) {
  2091. LBO ClusterLbo;
  2092. ULONG Offset;
  2093. ClusterLbo = FatIndexToLbo( FatStructureContext, FatEntry );
  2094. //
  2095. // Now for each dirent in the cluster compute the lbo, read in the dirent
  2096. // and check if it is available.
  2097. //
  2098. for (Offset = 0; Offset < BytesPerCluster; Offset += sizeof(DIRENT)) {
  2099. *Lbo = Offset + ClusterLbo;
  2100. DiskRead( DeviceId, *Lbo, sizeof(DIRENT), &TemporaryDirent, CACHE_NEW_DATA, FALSE );
  2101. if ((TemporaryDirent.FileName[0] == FAT_DIRENT_DELETED) ||
  2102. (TemporaryDirent.FileName[0] == FAT_DIRENT_NEVER_USED)) {
  2103. //
  2104. // This dirent is free so write out the dirent, and we're done.
  2105. //
  2106. DiskWrite( DeviceId, *Lbo, sizeof(DIRENT), Dirent );
  2107. return ESUCCESS;
  2108. }
  2109. }
  2110. //
  2111. // Now that we've exhausted the current cluster we need to read
  2112. // in the next cluster. So locate the next fat entry in the chain.
  2113. // Set previous entry to be the saved entry just in case we run off
  2114. // the chain and need to allocate another cluster.
  2115. //
  2116. PreviousEntry = FatEntry;
  2117. LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry, FALSE );
  2118. //
  2119. // If there isn't another cluster in the chain then we need to allocate a
  2120. // new cluster, and set previous entry to point to it.
  2121. //
  2122. if (FatInterpretClusterType(FatStructureContext, FatEntry) != FatClusterNext) {
  2123. AllocateClusters( FatStructureContext, DeviceId, 1, PreviousEntry, &FatEntry );
  2124. SetFatEntry( FatStructureContext, DeviceId, PreviousEntry, FatEntry );
  2125. }
  2126. }
  2127. return ENOSPC;
  2128. }
  2129. //
  2130. // Internal support routine
  2131. //
  2132. VOID
  2133. FatSetDirent (
  2134. IN PFAT8DOT3 FileName,
  2135. IN OUT PDIRENT Dirent,
  2136. IN UCHAR Attributes
  2137. )
  2138. /*++
  2139. Routine Description:
  2140. This routine sets up the dirent
  2141. Arguments:
  2142. FileName - Supplies the name to store in the dirent
  2143. Dirent - Receives the current date and time
  2144. Attributes - Supplies the attributes to initialize the dirent with
  2145. Return Value:
  2146. None.
  2147. --*/
  2148. {
  2149. PTIME_FIELDS Time;
  2150. ULONG i;
  2151. for (i = 0; i < sizeof(FAT8DOT3); i+= 1) {
  2152. Dirent->FileName[i] = (*FileName)[i];
  2153. }
  2154. Dirent->Attributes = (UCHAR)(Attributes | FAT_DIRENT_ATTR_ARCHIVE);
  2155. Time = ArcGetTime();
  2156. Dirent->LastWriteTime.Time.DoubleSeconds = (USHORT)(Time->Second/2);
  2157. Dirent->LastWriteTime.Time.Minute = Time->Minute;
  2158. Dirent->LastWriteTime.Time.Hour = Time->Hour;
  2159. Dirent->LastWriteTime.Date.Day = Time->Day;
  2160. Dirent->LastWriteTime.Date.Month = Time->Month;
  2161. Dirent->LastWriteTime.Date.Year = (USHORT)(Time->Year - 1980);
  2162. return;
  2163. }
  2164. //
  2165. // Internal support routine
  2166. //
  2167. ARC_STATUS
  2168. FatLoadMcb (
  2169. IN ULONG FileId,
  2170. IN VBO StartingVbo,
  2171. IN BOOLEAN IsDoubleSpace
  2172. )
  2173. /*++
  2174. Routine Description:
  2175. This routine loads into the cached mcb table the the retrival information for
  2176. the starting vbo.
  2177. Arguments:
  2178. FileId - Supplies the FileId for the operation
  2179. StartingVbo - Supplies the starting vbo to use when loading the mcb
  2180. IsDoubleSpace - Indicates if the operation is being done on a double space volume
  2181. Return Value:
  2182. ESUCCESS is returned if the operation is successful. Otherwise,
  2183. an unsuccessful status is returned that describes the reason for failure.
  2184. --*/
  2185. {
  2186. PBL_FILE_TABLE FileTableEntry;
  2187. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  2188. PFAT_MCB Mcb;
  2189. ULONG DeviceId;
  2190. ULONG BytesPerCluster;
  2191. FAT_ENTRY FatEntry;
  2192. CLUSTER_TYPE ClusterType;
  2193. VBO Vbo;
  2194. //****if (IsDoubleSpace) { DbgPrint("FatLoadMcb(%0x,%0x,%0x)\n", FileId, StartingVbo, IsDoubleSpace); }
  2195. //
  2196. // Preload some of the local variables
  2197. //
  2198. FileTableEntry = &BlFileTable[FileId];
  2199. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  2200. Mcb = &FatStructureContext->Mcb;
  2201. DeviceId = FileTableEntry->DeviceId;
  2202. BytesPerCluster = FatBytesPerCluster(&FatStructureContext->Bpb);
  2203. if (IsDoubleSpace) { DeviceId = FileId; }
  2204. //
  2205. // Set the file id in the structure context, and also set the mcb to be initially
  2206. // empty
  2207. //
  2208. FatStructureContext->FileId = FileId;
  2209. Mcb->InUse = 0;
  2210. Mcb->Vbo[0] = 0;
  2211. if (!IsBpbFat32(&FatStructureContext->Bpb)) {
  2212. //
  2213. // Check if this is the root directory. If it is then we build the single
  2214. // run mcb entry for the root directory.
  2215. //
  2216. if (FileTableEntry->u.FatFileContext.DirentLbo == 0) {
  2217. Mcb->InUse = 1;
  2218. Mcb->Lbo[0] = FatRootDirectoryLbo(&FatStructureContext->Bpb);
  2219. Mcb->Vbo[1] = FatRootDirectorySize(&FatStructureContext->Bpb);
  2220. return ESUCCESS;
  2221. }
  2222. //
  2223. // For all other files/directories we need to do some work. First get the fat
  2224. // entry and cluster type of the fat entry stored in the dirent
  2225. //
  2226. FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
  2227. } else {
  2228. //
  2229. // Check if this is the root directory. If it is then we use
  2230. // the BPB values to start the run.
  2231. //
  2232. if (FileTableEntry->u.FatFileContext.DirentLbo == 0) {
  2233. FatEntry = FatStructureContext->Bpb.RootDirFirstCluster;
  2234. } else {
  2235. //
  2236. // For all other files/directories we use the dirent values
  2237. //
  2238. if (IsBpbFat32(&FatStructureContext->Bpb)) {
  2239. FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile |
  2240. (FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFileHi << 16);
  2241. } else {
  2242. FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
  2243. }
  2244. }
  2245. }
  2246. ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
  2247. //
  2248. // Scan through the fat until we reach the vbo we're after and then build the
  2249. // mcb for the file
  2250. //
  2251. for (Vbo = BytesPerCluster; Vbo < StartingVbo; Vbo += BytesPerCluster) {
  2252. //
  2253. // Check if the file does not have any allocation beyond this point in which
  2254. // case the mcb we return is empty
  2255. //
  2256. if (ClusterType != FatClusterNext) {
  2257. return ESUCCESS;
  2258. }
  2259. LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry, IsDoubleSpace );
  2260. ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
  2261. }
  2262. //
  2263. // We need to check again if the file does not have any allocation beyond this
  2264. // point in which case the mcb we return is empty
  2265. //
  2266. if (ClusterType != FatClusterNext) {
  2267. return ESUCCESS;
  2268. }
  2269. //
  2270. // At this point FatEntry denotes another cluster, and it happens to be the
  2271. // cluster we want to start loading into the mcb. So set up the first run in
  2272. // the mcb to be this cluster, with a size of a single cluster.
  2273. //
  2274. Mcb->InUse = 1;
  2275. Mcb->Vbo[0] = Vbo - BytesPerCluster;
  2276. Mcb->Lbo[0] = FatIndexToLbo( FatStructureContext, FatEntry );
  2277. Mcb->Vbo[1] = Vbo;
  2278. //
  2279. // Now we'll scan through the fat chain until we either exhaust the fat chain
  2280. // or we fill up the mcb
  2281. //
  2282. while (TRUE) {
  2283. LBO Lbo;
  2284. //
  2285. // Get the next fat entry and interpret its cluster type
  2286. //
  2287. LookupFatEntry( FatStructureContext, DeviceId, FatEntry, &FatEntry, IsDoubleSpace );
  2288. ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
  2289. if (ClusterType != FatClusterNext) {
  2290. return ESUCCESS;
  2291. }
  2292. //
  2293. // Now calculate the lbo for this cluster and determine if it
  2294. // is a continuation of the previous run or a start of a new run
  2295. //
  2296. Lbo = FatIndexToLbo(FatStructureContext, FatEntry);
  2297. //
  2298. // It is a continuation if the lbo of the last run plus the current
  2299. // size of the run is equal to the lbo for the next cluster. If it
  2300. // is a contination then we only need to add a cluster amount to the
  2301. // last vbo to increase the run size. If it is a new run then
  2302. // we need to check if the run will fit, and if so then add in the
  2303. // new run.
  2304. //
  2305. if ((Mcb->Lbo[Mcb->InUse-1] + (Mcb->Vbo[Mcb->InUse] - Mcb->Vbo[Mcb->InUse-1])) == Lbo) {
  2306. Mcb->Vbo[Mcb->InUse] += BytesPerCluster;
  2307. } else {
  2308. if ((Mcb->InUse + 1) >= FAT_MAXIMUM_MCB) {
  2309. return ESUCCESS;
  2310. }
  2311. Mcb->InUse += 1;
  2312. Mcb->Lbo[Mcb->InUse-1] = Lbo;
  2313. Mcb->Vbo[Mcb->InUse] = Mcb->Vbo[Mcb->InUse-1] + BytesPerCluster;
  2314. }
  2315. }
  2316. return ESUCCESS;
  2317. }
  2318. //
  2319. // Internal support routine
  2320. //
  2321. ARC_STATUS
  2322. FatVboToLbo (
  2323. IN ULONG FileId,
  2324. IN VBO Vbo,
  2325. OUT PLBO Lbo,
  2326. OUT PULONG ByteCount,
  2327. IN BOOLEAN IsDoubleSpace
  2328. )
  2329. /*++
  2330. Routine Description:
  2331. This routine computes the run denoted by the input vbo to into its
  2332. corresponding lbo and also returns the number of bytes remaining in
  2333. the run.
  2334. Arguments:
  2335. Vbo - Supplies the Vbo to match
  2336. Lbo - Recieves the corresponding Lbo
  2337. ByteCount - Receives the number of bytes remaining in the run
  2338. IsDoubleSpace - Indicates if the operation is being done on a double space volume
  2339. Return Value:
  2340. ESUCCESS is returned if the operation is successful. Otherwise,
  2341. an unsuccessful status is returned that describes the reason for failure.
  2342. --*/
  2343. {
  2344. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  2345. PFAT_MCB Mcb;
  2346. ULONG i;
  2347. //****if (IsDoubleSpace) { DbgPrint("FatVboToLbo(%0x,%0x,%0x,%0x,%0x)\n", FileId, Vbo, Lbo, ByteCount, IsDoubleSpace); }
  2348. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)BlFileTable[FileId].StructureContext;
  2349. Mcb = &FatStructureContext->Mcb;
  2350. //
  2351. // Check if the mcb is for the correct file id and has the range we're asking for.
  2352. // If it doesn't then call load mcb to load in the right range.
  2353. //
  2354. if ((FileId != FatStructureContext->FileId) ||
  2355. (Vbo < Mcb->Vbo[0]) || (Vbo >= Mcb->Vbo[Mcb->InUse])) {
  2356. LoadMcb(FileId, Vbo, IsDoubleSpace);
  2357. }
  2358. //
  2359. // Now search for the slot where the Vbo fits in the mcb. Note that
  2360. // we could also do a binary search here but because the run count
  2361. // is probably small the extra overhead of a binary search doesn't
  2362. // buy us anything
  2363. //
  2364. for (i = 0; i < Mcb->InUse; i += 1) {
  2365. //
  2366. // We found our slot if the vbo we're after is less then the
  2367. // next mcb's vbo
  2368. //
  2369. if (Vbo < Mcb->Vbo[i+1]) {
  2370. //
  2371. // Compute the corresponding lbo which is the stored lbo plus
  2372. // the difference between the stored vbo and the vbo we're
  2373. // looking up. Also compute the byte count which is the
  2374. // difference between the current vbo we're looking up and
  2375. // the vbo for the next run.
  2376. //
  2377. *Lbo = Mcb->Lbo[i] + (Vbo - Mcb->Vbo[i]);
  2378. *ByteCount = Mcb->Vbo[i+1] - Vbo;
  2379. //
  2380. // and return success to our caller
  2381. //
  2382. return ESUCCESS;
  2383. }
  2384. }
  2385. //
  2386. // If we really reach here we have an error, most likely because the file is
  2387. // not large enough for the requested Vbo.
  2388. //
  2389. return EINVAL;
  2390. }
  2391. //
  2392. // Internal support routine
  2393. //
  2394. ARC_STATUS
  2395. FatIncreaseFileAllocation (
  2396. IN ULONG FileId,
  2397. IN ULONG ByteSize
  2398. )
  2399. /*++
  2400. Routine Description:
  2401. This procedure increases the file allocation to be at minimum the indicated
  2402. size.
  2403. Arguments:
  2404. FileId - Supplies the file id being processed
  2405. ByteSize - Supplies the minimum byte size for file allocation
  2406. Return Value:
  2407. ESUCCESS is returned if the operation is successful. Otherwise,
  2408. an unsuccessful status is returned that describes the reason for failure.
  2409. --*/
  2410. {
  2411. PBL_FILE_TABLE FileTableEntry;
  2412. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  2413. ULONG DeviceId;
  2414. ULONG BytesPerCluster;
  2415. ULONG NumberOfClustersNeeded;
  2416. FAT_ENTRY FatEntry;
  2417. CLUSTER_TYPE ClusterType;
  2418. FAT_ENTRY PreviousEntry;
  2419. ULONG i;
  2420. //
  2421. // Preload some of the local variables
  2422. //
  2423. FileTableEntry = &BlFileTable[FileId];
  2424. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  2425. DeviceId = FileTableEntry->DeviceId;
  2426. BytesPerCluster = FatBytesPerCluster(&FatStructureContext->Bpb);
  2427. //
  2428. // Check if this is the root directory. If it is then check if the allocation
  2429. // increase is already accommodated in the volume
  2430. //
  2431. if (FileTableEntry->u.FatFileContext.DirentLbo == 0) {
  2432. if (FatRootDirectorySize(&FatStructureContext->Bpb) >= ByteSize) {
  2433. return ESUCCESS;
  2434. } else {
  2435. return ENOSPC;
  2436. }
  2437. }
  2438. //
  2439. // Compute the actual number of clusters needed to satisfy the request
  2440. // Also get the first fat entry and its cluster type from the dirent.
  2441. //
  2442. NumberOfClustersNeeded = (ByteSize + BytesPerCluster - 1) / BytesPerCluster;
  2443. if (IsBpbFat32(&FatStructureContext->Bpb)) {
  2444. FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile |
  2445. (FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFileHi << 16);
  2446. } else {
  2447. FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
  2448. }
  2449. ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
  2450. //
  2451. // Previous Entry is as a hint to allocate new space and to show us where
  2452. // the end of the current fat chain is located
  2453. //
  2454. PreviousEntry = 2;
  2455. //
  2456. // We loop for the number of clusters we need trying to go down the fat chain.
  2457. // When we exit i is either number of clusters in the file (if less then
  2458. // the number of clusters we need) or it is set equal to the number of clusters
  2459. // we need
  2460. //
  2461. for (i = 0; i < NumberOfClustersNeeded; i += 1) {
  2462. if (ClusterType != FatClusterNext) { break; }
  2463. PreviousEntry = FatEntry;
  2464. LookupFatEntry( FatStructureContext, DeviceId, PreviousEntry, &FatEntry, FALSE );
  2465. ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
  2466. }
  2467. if (i >= NumberOfClustersNeeded) {
  2468. return ESUCCESS;
  2469. }
  2470. //
  2471. // At this point previous entry points to the last entry and i contains the
  2472. // number of clusters in the file. We now need to build up the allocation
  2473. //
  2474. AllocateClusters( FatStructureContext,
  2475. DeviceId,
  2476. NumberOfClustersNeeded - i,
  2477. PreviousEntry,
  2478. &FatEntry );
  2479. //
  2480. // We have our additional allocation, so now figure out if we need to chain off of
  2481. // the dirent or it we already have a few clusters in the chain and we
  2482. // need to munge the fat.
  2483. //
  2484. if (FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile == FAT_CLUSTER_AVAILABLE) {
  2485. FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile = (USHORT)FatEntry;
  2486. FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFileHi =
  2487. (USHORT)(FatEntry >> 16);
  2488. DiskWrite( DeviceId,
  2489. FileTableEntry->u.FatFileContext.DirentLbo,
  2490. sizeof(DIRENT),
  2491. &FileTableEntry->u.FatFileContext.Dirent );
  2492. } else {
  2493. SetFatEntry( FatStructureContext, DeviceId, PreviousEntry, FatEntry );
  2494. }
  2495. return ESUCCESS;
  2496. }
  2497. //
  2498. // Internal support routine
  2499. //
  2500. ARC_STATUS
  2501. FatTruncateFileAllocation (
  2502. IN ULONG FileId,
  2503. IN ULONG ByteSize
  2504. )
  2505. /*++
  2506. Routine Description:
  2507. This procedure decreases the file allocation to be at maximum the indicated
  2508. size.
  2509. Arguments:
  2510. FileId - Supplies the file id being processed
  2511. ByteSize - Supplies the maximum byte size for file allocation
  2512. Return Value:
  2513. ESUCCESS is returned if the operation is successful. Otherwise,
  2514. an unsuccessful status is returned that describes the reason for failure.
  2515. --*/
  2516. {
  2517. PBL_FILE_TABLE FileTableEntry;
  2518. PFAT_STRUCTURE_CONTEXT FatStructureContext;
  2519. ULONG DeviceId;
  2520. ULONG BytesPerCluster;
  2521. ULONG NumberOfClustersNeeded;
  2522. FAT_ENTRY FatEntry;
  2523. CLUSTER_TYPE ClusterType;
  2524. FAT_ENTRY CurrentIndex;
  2525. ULONG i;
  2526. //
  2527. // Preload some of the local variables
  2528. //
  2529. FileTableEntry = &BlFileTable[FileId];
  2530. FatStructureContext = (PFAT_STRUCTURE_CONTEXT)FileTableEntry->StructureContext;
  2531. DeviceId = FileTableEntry->DeviceId;
  2532. BytesPerCluster = FatBytesPerCluster(&FatStructureContext->Bpb);
  2533. //
  2534. // Check if this is the root directory. If it is then noop this request
  2535. //
  2536. if (FileTableEntry->u.FatFileContext.DirentLbo == 0) {
  2537. return ESUCCESS;
  2538. }
  2539. //
  2540. // Compute the actual number of clusters needed to satisfy the request
  2541. // Also get the first fat entry and its cluster type from the dirent
  2542. //
  2543. NumberOfClustersNeeded = (ByteSize + BytesPerCluster - 1) / BytesPerCluster;
  2544. if (IsBpbFat32(&FatStructureContext->Bpb)) {
  2545. FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile |
  2546. (FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFileHi << 16);
  2547. } else {
  2548. FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
  2549. }
  2550. ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
  2551. //
  2552. // The current index variable is used to indicate where we extracted the current
  2553. // fat entry value from. It has a value of 0 we got the fat entry from the
  2554. // dirent.
  2555. //
  2556. CurrentIndex = FAT_CLUSTER_AVAILABLE;
  2557. //
  2558. // Now loop through the fat chain for the number of clusters needed.
  2559. // If we run out of the chain before we run out of clusters needed then the
  2560. // current allocation is already smaller than necessary.
  2561. //
  2562. for (i = 0; i < NumberOfClustersNeeded; i += 1) {
  2563. //
  2564. // If we run out of the chain before we run out of clusters needed then the
  2565. // current allocation is already smaller than necessary.
  2566. //
  2567. if (ClusterType != FatClusterNext) { return ESUCCESS; }
  2568. //
  2569. // Update the current index, and read in a new fat entry and interpret its
  2570. // type
  2571. //
  2572. CurrentIndex = FatEntry;
  2573. LookupFatEntry( FatStructureContext, DeviceId, CurrentIndex, &FatEntry, FALSE );
  2574. ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
  2575. }
  2576. //
  2577. // If we get here then we've found that the current allocation is equal to or
  2578. // larger than what we want. It is equal if the current cluster type does not
  2579. // point to another cluster. The first thing we have to do is terminate the
  2580. // fat chain correctly. If the current index is zero then we zero out the
  2581. // dirent, otherwise we need to set the value to be last cluster.
  2582. //
  2583. if (CurrentIndex == FAT_CLUSTER_AVAILABLE) {
  2584. FatEntry = FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile;
  2585. if (IsBpbFat32(&FatStructureContext->Bpb)) {
  2586. FatEntry |= FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFileHi << 16;
  2587. }
  2588. if (FatEntry != FAT_CLUSTER_AVAILABLE) {
  2589. //
  2590. // By setting the dirent we set in a new date.
  2591. //
  2592. FatSetDirent( &FileTableEntry->u.FatFileContext.Dirent.FileName,
  2593. &FileTableEntry->u.FatFileContext.Dirent,
  2594. 0 );
  2595. FatEntry = FAT_CLUSTER_AVAILABLE;
  2596. FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFile = (USHORT)FatEntry;
  2597. if (IsBpbFat32(&FatStructureContext->Bpb)) {
  2598. FileTableEntry->u.FatFileContext.Dirent.FirstClusterOfFileHi =
  2599. (USHORT)(FatEntry >> 16);
  2600. }
  2601. FileTableEntry->u.FatFileContext.Dirent.FileSize = 0;
  2602. DiskWrite( DeviceId,
  2603. FileTableEntry->u.FatFileContext.DirentLbo,
  2604. sizeof(DIRENT),
  2605. &FileTableEntry->u.FatFileContext.Dirent );
  2606. }
  2607. } else {
  2608. if (ClusterType != FatClusterLast) {
  2609. SetFatEntry( FatStructureContext, DeviceId, CurrentIndex, FAT_CLUSTER_LAST );
  2610. }
  2611. }
  2612. //
  2613. // Now while there are clusters left to deallocate then we need to go down the
  2614. // chain freeing up the clusters
  2615. //
  2616. while (ClusterType == FatClusterNext) {
  2617. //
  2618. // Read in the value at the next fat entry and interpret its cluster type
  2619. //
  2620. CurrentIndex = FatEntry;
  2621. LookupFatEntry( FatStructureContext, DeviceId, CurrentIndex, &FatEntry, FALSE );
  2622. ClusterType = FatInterpretClusterType(FatStructureContext, FatEntry);
  2623. //
  2624. // Now deallocate the cluster at the current index
  2625. //
  2626. SetFatEntry( FatStructureContext, DeviceId, CurrentIndex, FAT_CLUSTER_AVAILABLE );
  2627. }
  2628. return ESUCCESS;
  2629. }
  2630. //
  2631. // Internal support routine
  2632. //
  2633. ARC_STATUS
  2634. FatAllocateClusters (
  2635. IN PFAT_STRUCTURE_CONTEXT FatStructureContext,
  2636. IN ULONG DeviceId,
  2637. IN ULONG ClusterCount,
  2638. IN ULONG Hint,
  2639. OUT PULONG AllocatedEntry
  2640. )
  2641. /*++
  2642. Routine Description:
  2643. This procedure allocates a new cluster, set its entry to be the last one,
  2644. and zeros out the cluster.
  2645. Arguments:
  2646. FatStructureContext - Supplies the structure context for the operation
  2647. DeviceId - Supplies the device id for the operation
  2648. ClusterCount - Supplies the number of clusters we need to allocate
  2649. Hint - Supplies a hint to start from when looking for a free cluster
  2650. AllocatedEntry - Receives the first fat index for the new allocated cluster chain
  2651. Return Value:
  2652. ESUCCESS is returned if the operation is successful. Otherwise,
  2653. an unsuccessful status is returned that describes the reason for failure.
  2654. --*/
  2655. {
  2656. ULONG TotalClustersInVolume;
  2657. ULONG BytesPerCluster;
  2658. UCHAR BlankBuffer[512];
  2659. FAT_ENTRY PreviousEntry;
  2660. ULONG CurrentClusterCount;
  2661. ULONG j;
  2662. LBO ClusterLbo;
  2663. ULONG i;
  2664. //
  2665. // Load some local variables
  2666. //
  2667. TotalClustersInVolume = FatNumberOfClusters(&FatStructureContext->Bpb);
  2668. BytesPerCluster = FatBytesPerCluster(&FatStructureContext->Bpb);
  2669. RtlZeroMemory((PVOID)&BlankBuffer[0], 512);
  2670. PreviousEntry = 0;
  2671. CurrentClusterCount = 0;
  2672. //
  2673. // For each cluster on the disk we'll do the following loop
  2674. //
  2675. for (j = 0; j < TotalClustersInVolume; j += 1) {
  2676. FAT_ENTRY EntryToExamine;
  2677. FAT_ENTRY FatEntry;
  2678. //
  2679. // Check if the current allocation is enough.
  2680. //
  2681. if (CurrentClusterCount >= ClusterCount) {
  2682. return ESUCCESS;
  2683. }
  2684. //
  2685. // Compute an entry to examine based on the loop iteration and our hint
  2686. //
  2687. EntryToExamine = (FAT_ENTRY)(((j + Hint - 2) % TotalClustersInVolume) + 2);
  2688. //
  2689. // Read in the prospective fat entry and check if it is available. If it
  2690. // is not available then continue looping.
  2691. //
  2692. LookupFatEntry( FatStructureContext, DeviceId, EntryToExamine, &FatEntry, FALSE );
  2693. if (FatInterpretClusterType(FatStructureContext, FatEntry) != FatClusterAvailable) {
  2694. continue;
  2695. }
  2696. //
  2697. // We have a free cluster, so put it at the end of the chain.
  2698. //
  2699. if (PreviousEntry == 0) {
  2700. *AllocatedEntry = EntryToExamine;
  2701. } else {
  2702. SetFatEntry( FatStructureContext, DeviceId, PreviousEntry, EntryToExamine );
  2703. }
  2704. SetFatEntry( FatStructureContext, DeviceId, EntryToExamine, FAT_CLUSTER_LAST );
  2705. //
  2706. // Now we need to go through and zero out the data in the cluster that we've
  2707. // just allocated. Because all clusters must be a multiple of dirents we'll
  2708. // do it a dirent at a time.
  2709. //
  2710. ClusterLbo = FatIndexToLbo( FatStructureContext, EntryToExamine );
  2711. for (i = 0; i < BytesPerCluster; i += 512) {
  2712. DiskWrite( DeviceId, ClusterLbo + i, 512, BlankBuffer );
  2713. }
  2714. //
  2715. // Before we go back to the top of the loop we need to update the
  2716. // previous entry so that it points to the end of the current chain and
  2717. // also i because we've just added another cluster.
  2718. //
  2719. PreviousEntry = EntryToExamine;
  2720. CurrentClusterCount += 1;
  2721. }
  2722. return ENOSPC;
  2723. }
  2724. //
  2725. // Internal support routine
  2726. //
  2727. VOID
  2728. FatFirstComponent (
  2729. IN OUT PSTRING String,
  2730. OUT PFAT8DOT3 FirstComponent
  2731. )
  2732. /*++
  2733. Routine Description:
  2734. Convert a string into fat 8.3 format and advance the input string
  2735. descriptor to point to the next file name component.
  2736. Arguments:
  2737. InputString - Supplies a pointer to the input string descriptor.
  2738. Output8dot3 - Supplies a pointer to the converted string.
  2739. Return Value:
  2740. None.
  2741. --*/
  2742. {
  2743. ULONG Extension;
  2744. ULONG Index;
  2745. //
  2746. // Fill the output name with blanks.
  2747. //
  2748. for (Index = 0; Index < 11; Index += 1) { (*FirstComponent)[Index] = ' '; }
  2749. //
  2750. // Copy the first part of the file name up to eight characters and
  2751. // skip to the end of the name or the input string as appropriate.
  2752. //
  2753. for (Index = 0; Index < String->Length; Index += 1) {
  2754. if ((String->Buffer[Index] == '\\') || (String->Buffer[Index] == '.')) {
  2755. break;
  2756. }
  2757. if (Index < 8) {
  2758. (*FirstComponent)[Index] = (CHAR)ToUpper(String->Buffer[Index]);
  2759. }
  2760. }
  2761. //
  2762. // Check if the end of the string was reached, an extension was specified,
  2763. // or a subdirectory was specified..
  2764. //
  2765. if (Index < String->Length) {
  2766. if (String->Buffer[Index] == '.') {
  2767. //
  2768. // Skip over the extension separator and add the extension to
  2769. // the file name.
  2770. //
  2771. Index += 1;
  2772. Extension = 8;
  2773. while (Index < String->Length) {
  2774. if (String->Buffer[Index] == '\\') {
  2775. break;
  2776. }
  2777. if (Extension < 11) {
  2778. (*FirstComponent)[Extension] = (CHAR)ToUpper(String->Buffer[Index]);
  2779. Extension += 1;
  2780. }
  2781. Index += 1;
  2782. }
  2783. }
  2784. }
  2785. //
  2786. // Now we'll bias the first component by the 0xe5 factor so that all our tests
  2787. // to names on the disk will be ready for a straight 11 byte comparison
  2788. //
  2789. if ((*FirstComponent)[0] == 0xe5) {
  2790. (*FirstComponent)[0] = FAT_DIRENT_REALLY_0E5;
  2791. }
  2792. //
  2793. // Update string descriptor.
  2794. //
  2795. String->Buffer += Index;
  2796. String->Length -= (USHORT)Index;
  2797. return;
  2798. }
  2799. //
  2800. // Internal support routine
  2801. //
  2802. VOID
  2803. FatDirToArcDir (
  2804. IN PDIRENT FatDirent,
  2805. OUT PDIRECTORY_ENTRY ArcDirent
  2806. )
  2807. /*++
  2808. Routine Description:
  2809. This routine converts a FAT directory entry into an ARC
  2810. directory entry.
  2811. Arguments:
  2812. FatDirent - supplies a pointer to a FAT directory entry.
  2813. ArcDirent - supplies a pointer to an ARC directory entry.
  2814. Return Value:
  2815. None.
  2816. --*/
  2817. {
  2818. ULONG i, e;
  2819. //
  2820. // clear info area
  2821. //
  2822. RtlZeroMemory( ArcDirent, sizeof(DIRECTORY_ENTRY) );
  2823. //
  2824. // check the directory flag
  2825. //
  2826. if (FlagOn( FatDirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
  2827. SetFlag( ArcDirent->FileAttribute, ArcDirectoryFile );
  2828. }
  2829. //
  2830. // check the read-only flag
  2831. //
  2832. if (FlagOn( FatDirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY )) {
  2833. SetFlag( ArcDirent->FileAttribute, ArcReadOnlyFile );
  2834. }
  2835. //
  2836. // clear name string
  2837. //
  2838. RtlZeroMemory( ArcDirent->FileName, 32 );
  2839. //
  2840. // copy first portion of file name
  2841. //
  2842. for (i = 0; (i < 8) && (FatDirent->FileName[i] != ' '); i += 1) {
  2843. ArcDirent->FileName[i] = FatDirent->FileName[i];
  2844. }
  2845. //
  2846. // check for an extension
  2847. //
  2848. if ( FatDirent->FileName[8] != ' ' ) {
  2849. //
  2850. // store the dot char
  2851. //
  2852. ArcDirent->FileName[i++] = '.';
  2853. //
  2854. // add the extension
  2855. //
  2856. for (e = 8; (e < 11) && (FatDirent->FileName[e] != ' '); e += 1) {
  2857. ArcDirent->FileName[i++] = FatDirent->FileName[e];
  2858. }
  2859. }
  2860. //
  2861. // set file name length before returning
  2862. //
  2863. ArcDirent->FileNameLength = i;
  2864. return;
  2865. }