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

3625 lines
104 KiB

  1. /*++
  2. Copyright (c) 1989-2000 Microsoft Corporation
  3. Module Name:
  4. DirSup.c
  5. Abstract:
  6. This module implements the dirent support routines for Fat.
  7. // @@BEGIN_DDKSPLIT
  8. Author:
  9. DavidGoebel [DavidGoe] 08-Nov-90
  10. // @@END_DDKSPLIT
  11. --*/
  12. #include "FatProcs.h"
  13. //
  14. // The Bug check file id for this module
  15. //
  16. #define BugCheckFileId (FAT_BUG_CHECK_DIRSUP)
  17. //
  18. // Local debug trace level
  19. //
  20. #define Dbg (DEBUG_TRACE_DIRSUP)
  21. //
  22. // The following three macro all assume the input dirent has been zeroed.
  23. //
  24. //
  25. // VOID
  26. // FatConstructDot (
  27. // IN PIRP_CONTEXT IrpContext,
  28. // IN PDCB Directory,
  29. // IN PDIRENT ParentDirent,
  30. // IN OUT PDIRENT Dirent
  31. // );
  32. //
  33. // The following macro is called to initalize the "." dirent.
  34. //
  35. // Always setting FirstClusterOfFileHi is OK because it will be zero
  36. // unless we're working on a FAT 32 disk.
  37. //
  38. #define FatConstructDot(IRPCONTEXT,DCB,PARENT,DIRENT) { \
  39. \
  40. RtlCopyMemory( (PUCHAR)(DIRENT), ". ", 11 ); \
  41. (DIRENT)->Attributes = FAT_DIRENT_ATTR_DIRECTORY; \
  42. (DIRENT)->LastWriteTime = (PARENT)->LastWriteTime; \
  43. if (FatData.ChicagoMode) { \
  44. (DIRENT)->CreationTime = (PARENT)->CreationTime; \
  45. (DIRENT)->CreationMSec = (PARENT)->CreationMSec; \
  46. (DIRENT)->LastAccessDate = (PARENT)->LastAccessDate; \
  47. } \
  48. (DIRENT)->FirstClusterOfFile = \
  49. (USHORT)(DCB)->FirstClusterOfFile; \
  50. (DIRENT)->FirstClusterOfFileHi = \
  51. (USHORT)((DCB)->FirstClusterOfFile/0x10000); \
  52. }
  53. //
  54. // VOID
  55. // FatConstructDotDot (
  56. // IN PIRP_CONTEXT IrpContext,
  57. // IN PDCB Directory,
  58. // IN PDIRENT ParentDirent,
  59. // IN OUT PDIRENT Dirent
  60. // );
  61. //
  62. // The following macro is called to initalize the ".." dirent.
  63. //
  64. // Always setting FirstClusterOfFileHi is OK because it will be zero
  65. // unless we're working on a FAT 32 disk.
  66. //
  67. #define FatConstructDotDot(IRPCONTEXT,DCB,PARENT,DIRENT) { \
  68. \
  69. RtlCopyMemory( (PUCHAR)(DIRENT), ".. ", 11 ); \
  70. (DIRENT)->Attributes = FAT_DIRENT_ATTR_DIRECTORY; \
  71. (DIRENT)->LastWriteTime = (PARENT)->LastWriteTime; \
  72. if (FatData.ChicagoMode) { \
  73. (DIRENT)->CreationTime = (PARENT)->CreationTime; \
  74. (DIRENT)->CreationMSec = (PARENT)->CreationMSec; \
  75. (DIRENT)->LastAccessDate = (PARENT)->LastAccessDate; \
  76. } \
  77. if (NodeType((DCB)->ParentDcb) == FAT_NTC_ROOT_DCB) { \
  78. (DIRENT)->FirstClusterOfFile = 0; \
  79. (DIRENT)->FirstClusterOfFileHi = 0; \
  80. } else { \
  81. (DIRENT)->FirstClusterOfFile = (USHORT) \
  82. ((DCB)->ParentDcb->FirstClusterOfFile); \
  83. (DIRENT)->FirstClusterOfFileHi = (USHORT) \
  84. ((DCB)->ParentDcb->FirstClusterOfFile/0x10000); \
  85. } \
  86. }
  87. //
  88. // VOID
  89. // FatConstructEndDirent (
  90. // IN PIRP_CONTEXT IrpContext,
  91. // IN OUT PDIRENT Dirent
  92. // );
  93. //
  94. // The following macro created the end dirent. Note that since the
  95. // dirent was zeroed, the first byte of the name already contains 0x0,
  96. // so there is nothing to do.
  97. //
  98. #define FatConstructEndDirent(IRPCONTEXT,DIRENT) NOTHING
  99. //
  100. // VOID
  101. // FatReadDirent (
  102. // IN PIRP_CONTEXT IrpContext,
  103. // IN PDCB Dcb,
  104. // IN VBO Vbo,
  105. // OUT PBCB *Bcb,
  106. // OUT PVOID *Dirent,
  107. // OUT PNTSTATUS Status
  108. // );
  109. //
  110. //
  111. // This macro reads in a page of dirents when we step onto a new page,
  112. // or this is the first iteration of a loop and Bcb is NULL.
  113. //
  114. #define FatReadDirent(IRPCONTEXT,DCB,VBO,BCB,DIRENT,STATUS) \
  115. if ((VBO) >= (DCB)->Header.AllocationSize.LowPart) { \
  116. *(STATUS) = STATUS_END_OF_FILE; \
  117. FatUnpinBcb( (IRPCONTEXT), *(BCB) ); \
  118. } else if ( ((VBO) % PAGE_SIZE == 0) || (*(BCB) == NULL) ) { \
  119. FatUnpinBcb( (IRPCONTEXT), *(BCB) ); \
  120. FatReadDirectoryFile( (IRPCONTEXT), \
  121. (DCB), \
  122. (VBO) & ~(PAGE_SIZE - 1), \
  123. PAGE_SIZE, \
  124. FALSE, \
  125. (BCB), \
  126. (PVOID *)(DIRENT), \
  127. (STATUS) ); \
  128. *(DIRENT) = (PVOID)((PUCHAR)*(DIRENT) + ((VBO) % PAGE_SIZE)); \
  129. }
  130. //
  131. // Internal support routines
  132. //
  133. UCHAR
  134. FatComputeLfnChecksum (
  135. PDIRENT Dirent
  136. );
  137. VOID
  138. FatRescanDirectory (
  139. PIRP_CONTEXT IrpContext,
  140. PDCB Dcb
  141. );
  142. ULONG
  143. FatDefragDirectory (
  144. IN PIRP_CONTEXT IrpContext,
  145. IN PDCB Dcb,
  146. IN ULONG DirentsNeeded
  147. );
  148. #ifdef ALLOC_PRAGMA
  149. #pragma alloc_text(PAGE, FatComputeLfnChecksum)
  150. #pragma alloc_text(PAGE, FatConstructDirent)
  151. #pragma alloc_text(PAGE, FatConstructLabelDirent)
  152. #pragma alloc_text(PAGE, FatCreateNewDirent)
  153. #pragma alloc_text(PAGE, FatDefragDirectory)
  154. #pragma alloc_text(PAGE, FatDeleteDirent)
  155. #pragma alloc_text(PAGE, FatGetDirentFromFcbOrDcb)
  156. #pragma alloc_text(PAGE, FatInitializeDirectoryDirent)
  157. #pragma alloc_text(PAGE, FatIsDirectoryEmpty)
  158. #pragma alloc_text(PAGE, FatLfnDirentExists)
  159. #pragma alloc_text(PAGE, FatLocateDirent)
  160. #pragma alloc_text(PAGE, FatLocateSimpleOemDirent)
  161. #pragma alloc_text(PAGE, FatLocateVolumeLabel)
  162. #pragma alloc_text(PAGE, FatRescanDirectory)
  163. #pragma alloc_text(PAGE, FatSetFileSizeInDirent)
  164. #pragma alloc_text(PAGE, FatTunnelFcbOrDcb)
  165. #pragma alloc_text(PAGE, FatUpdateDirentFromFcb)
  166. #endif
  167. ULONG
  168. FatCreateNewDirent (
  169. IN PIRP_CONTEXT IrpContext,
  170. IN PDCB ParentDirectory,
  171. IN ULONG DirentsNeeded
  172. )
  173. /*++
  174. Routine Description:
  175. This routine allocates on the disk a new dirent inside of the
  176. parent directory. If a new dirent cannot be allocated (i.e.,
  177. because the disk is full or the root directory is full) then
  178. it raises the appropriate status. The dirent itself is
  179. neither initialized nor pinned by this procedure.
  180. Arguments:
  181. ParentDirectory - Supplies the DCB for the directory in which
  182. to create the new dirent
  183. DirentsNeeded - This is the number of continginous dirents required
  184. Return Value:
  185. ByteOffset - Returns the VBO within the Parent directory where
  186. the dirent has been allocated
  187. --*/
  188. {
  189. VBO UnusedVbo;
  190. VBO DeletedHint;
  191. ULONG ByteOffset;
  192. PBCB Bcb = NULL;
  193. PDIRENT Dirent;
  194. NTSTATUS Status = STATUS_SUCCESS;
  195. PAGED_CODE();
  196. DebugTrace(+1, Dbg, "FatCreateNewDirent\n", 0);
  197. DebugTrace( 0, Dbg, " ParentDirectory = %08lx\n", ParentDirectory);
  198. //
  199. // If UnusedDirentVbo is within our current file allocation then we
  200. // don't have to search through the directory at all; we know just
  201. // where to put it.
  202. //
  203. // If UnusedDirentVbo is beyond the current file allocation then
  204. // there are no more unused dirents in the current allocation, though
  205. // upon adding another cluster of allocation UnusedDirentVbo
  206. // will point to an unused dirent. Haveing found no unused dirents
  207. // we use the DeletedDirentHint to try and find a deleted dirent in
  208. // the current allocation. In this also runs off the end of the file,
  209. // we finally have to break down and allocate another sector. Note
  210. // that simply writing beyond the current allocation will automatically
  211. // do just this.
  212. //
  213. // We also must deal with the special case where UnusedDirentVbo and
  214. // DeletedDirentHint have yet to be initialized. In this case we must
  215. // first walk through the directory looking for the first deleted entry
  216. // first unused dirent. After this point we continue as before.
  217. // This virgin state is denoted by the special value of 0xffffffff.
  218. //
  219. UnusedVbo = ParentDirectory->Specific.Dcb.UnusedDirentVbo;
  220. DeletedHint = ParentDirectory->Specific.Dcb.DeletedDirentHint;
  221. //
  222. // Check for our first call to this routine with this Dcb. If so
  223. // we have to correctly set the two hints in the Dcb.
  224. //
  225. if (UnusedVbo == 0xffffffff) {
  226. FatRescanDirectory( IrpContext, ParentDirectory );
  227. UnusedVbo = ParentDirectory->Specific.Dcb.UnusedDirentVbo;
  228. DeletedHint = ParentDirectory->Specific.Dcb.DeletedDirentHint;
  229. }
  230. //
  231. // Now we know that UnusedDirentVbo and DeletedDirentHint are correctly
  232. // set so we check if there is already an unused dirent in the the
  233. // current allocation. This is the easy case.
  234. //
  235. DebugTrace( 0, Dbg, " UnusedVbo = %08lx\n", UnusedVbo);
  236. DebugTrace( 0, Dbg, " DeletedHint = %08lx\n", DeletedHint);
  237. if ( UnusedVbo + (DirentsNeeded * sizeof(DIRENT)) <=
  238. ParentDirectory->Header.AllocationSize.LowPart ) {
  239. //
  240. // Get this unused dirent for the caller. We have a
  241. // sporting chance that we won't have to wait.
  242. //
  243. DebugTrace( 0, Dbg, "There is a never used entry.\n", 0);
  244. ByteOffset = UnusedVbo;
  245. UnusedVbo += DirentsNeeded * sizeof(DIRENT);
  246. } else {
  247. //
  248. // Life is tough. We have to march from the DeletedDirentHint
  249. // looking for a deleted dirent. If we get to EOF without finding
  250. // one, we will have to allocate a new cluster.
  251. //
  252. ByteOffset =
  253. RtlFindClearBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
  254. DirentsNeeded,
  255. DeletedHint / sizeof(DIRENT) );
  256. //
  257. // Do a quick check for a root directory allocation that failed
  258. // simply because of fragmentation. Also, only attempt to defrag
  259. // if the length is less that 0x40000. This is to avoid
  260. // complications arising from crossing a MM view boundary (256kb).
  261. // By default on DOS the root directory is only 0x2000 long.
  262. //
  263. // Don't try to defrag fat32 root dirs.
  264. //
  265. if (!FatIsFat32(ParentDirectory->Vcb) &&
  266. (ByteOffset == -1) &&
  267. (NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB) &&
  268. (ParentDirectory->Header.AllocationSize.LowPart <= 0x40000)) {
  269. ByteOffset = FatDefragDirectory( IrpContext, ParentDirectory, DirentsNeeded );
  270. }
  271. if (ByteOffset != -1) {
  272. //
  273. // If we consuemed deleted dirents at Deleted Hint, update.
  274. // We also may have consumed some un-used dirents as well,
  275. // so be sure to check for that as well.
  276. //
  277. ByteOffset *= sizeof(DIRENT);
  278. if (ByteOffset == DeletedHint) {
  279. DeletedHint += DirentsNeeded * sizeof(DIRENT);
  280. }
  281. if (ByteOffset + DirentsNeeded * sizeof(DIRENT) > UnusedVbo) {
  282. UnusedVbo = ByteOffset + DirentsNeeded * sizeof(DIRENT);
  283. }
  284. } else {
  285. //
  286. // We are going to have to allocate another cluster. Do
  287. // so, update both the UnusedVbo and the DeletedHint and bail.
  288. //
  289. DebugTrace( 0, Dbg, "We have to allocate another cluster.\n", 0);
  290. //
  291. // A reason why we might fail, unrelated to physical reasons,
  292. // is that we constrain to 64k directory entries to match the
  293. // restriction on Win95. There are fundamental reasons to do
  294. // this since searching a FAT directory is a linear operation
  295. // and to allow FAT32 to toss us over the cliff is not permissable.
  296. //
  297. if (ParentDirectory->Header.AllocationSize.LowPart >= (64 * 1024 * sizeof(DIRENT)) ||
  298. //
  299. // Make sure we are not trying to expand the root directory on non
  300. // FAT32. FAT16 and FAT12 have fixed size allocations.
  301. //
  302. (!FatIsFat32(ParentDirectory->Vcb) &&
  303. NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB)) {
  304. DebugTrace(0, Dbg, "Full root directory or too big on FAT32. Raise Status.\n", 0);
  305. FatRaiseStatus( IrpContext, STATUS_CANNOT_MAKE );
  306. }
  307. //
  308. // Take the last dirent(s) in this cluster. We will allocate
  309. // more clusters below.
  310. //
  311. ByteOffset = UnusedVbo;
  312. UnusedVbo += DirentsNeeded * sizeof(DIRENT);
  313. //
  314. // Touch the directory file to cause space for the new dirents
  315. // to be allocated.
  316. //
  317. Bcb = NULL;
  318. try {
  319. ULONG ClusterSize;
  320. PVOID Buffer;
  321. ClusterSize =
  322. 1 << ParentDirectory->Vcb->AllocationSupport.LogOfBytesPerCluster;
  323. FatPrepareWriteDirectoryFile( IrpContext,
  324. ParentDirectory,
  325. UnusedVbo,
  326. 1,
  327. &Bcb,
  328. &Buffer,
  329. FALSE,
  330. TRUE,
  331. &Status );
  332. } finally {
  333. FatUnpinBcb( IrpContext, Bcb );
  334. }
  335. }
  336. }
  337. //
  338. // If we are only requesting a single dirent, and we did not get the
  339. // first dirent in a directory, then check that the preceding dirent
  340. // is not an orphaned LFN. If it is, then mark it deleted. Thus
  341. // reducing the possibility of an accidental pairing.
  342. //
  343. // Only do this when we are in Chicago Mode.
  344. //
  345. Bcb = NULL;
  346. if (FatData.ChicagoMode &&
  347. (DirentsNeeded == 1) &&
  348. (ByteOffset > (NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB ?
  349. 0 : 2 * sizeof(DIRENT)))) {
  350. try {
  351. FatReadDirent( IrpContext,
  352. ParentDirectory,
  353. ByteOffset - sizeof(DIRENT),
  354. &Bcb,
  355. &Dirent,
  356. &Status );
  357. if ((Status != STATUS_SUCCESS) ||
  358. (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) {
  359. FatPopUpFileCorrupt( IrpContext, ParentDirectory );
  360. FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  361. }
  362. if ((Dirent->Attributes == FAT_DIRENT_ATTR_LFN) &&
  363. (Dirent->FileName[0] != FAT_DIRENT_DELETED)) {
  364. //
  365. // Pin it, mark it, and set it dirty.
  366. //
  367. FatPinMappedData( IrpContext,
  368. ParentDirectory,
  369. ByteOffset - sizeof(DIRENT),
  370. sizeof(DIRENT),
  371. &Bcb );
  372. Dirent->FileName[0] = FAT_DIRENT_DELETED;
  373. FatSetDirtyBcb( IrpContext, Bcb, ParentDirectory->Vcb, TRUE );
  374. ASSERT( RtlAreBitsSet( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
  375. (ByteOffset - sizeof(DIRENT))/ sizeof(DIRENT),
  376. DirentsNeeded ) );
  377. RtlClearBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
  378. (ByteOffset - sizeof(DIRENT))/ sizeof(DIRENT),
  379. DirentsNeeded );
  380. }
  381. } finally {
  382. FatUnpinBcb( IrpContext, Bcb );
  383. }
  384. }
  385. //
  386. // Assert that the dirents are in fact unused
  387. //
  388. try {
  389. ULONG i;
  390. Bcb = NULL;
  391. for (i = 0; i < DirentsNeeded; i++) {
  392. FatReadDirent( IrpContext,
  393. ParentDirectory,
  394. ByteOffset + i*sizeof(DIRENT),
  395. &Bcb,
  396. &Dirent,
  397. &Status );
  398. if ((Status != STATUS_SUCCESS) ||
  399. ((Dirent->FileName[0] != FAT_DIRENT_NEVER_USED) &&
  400. (Dirent->FileName[0] != FAT_DIRENT_DELETED))) {
  401. FatPopUpFileCorrupt( IrpContext, ParentDirectory );
  402. FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  403. }
  404. }
  405. } finally {
  406. FatUnpinBcb( IrpContext, Bcb );
  407. }
  408. //
  409. // Set the Bits in the bitmap and move the Unused Dirent Vbo.
  410. //
  411. ASSERT( RtlAreBitsClear( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
  412. ByteOffset / sizeof(DIRENT),
  413. DirentsNeeded ) );
  414. RtlSetBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
  415. ByteOffset / sizeof(DIRENT),
  416. DirentsNeeded );
  417. //
  418. // Save the newly computed values in the Parent Directory Fcb
  419. //
  420. ParentDirectory->Specific.Dcb.UnusedDirentVbo = UnusedVbo;
  421. ParentDirectory->Specific.Dcb.DeletedDirentHint = DeletedHint;
  422. DebugTrace(-1, Dbg, "FatCreateNewDirent -> (VOID)\n", 0);
  423. return ByteOffset;
  424. }
  425. VOID
  426. FatInitializeDirectoryDirent (
  427. IN PIRP_CONTEXT IrpContext,
  428. IN PDCB Dcb,
  429. IN PDIRENT ParentDirent
  430. )
  431. /*++
  432. Routine Description:
  433. This routine converts a dirent into a directory on the disk. It does this
  434. setting the directory flag in the dirent, and by allocating the necessary
  435. space for the "." and ".." dirents and initializing them.
  436. If a new dirent cannot be allocated (i.e., because the disk is full) then
  437. it raises the appropriate status.
  438. Arguments:
  439. Dcb - Supplies the Dcb denoting the file that is to be made into a
  440. directory. This must be input a completely empty file with
  441. an allocation size of zero.
  442. ParentDirent - Provides the parent Dirent for a time-stamp model.
  443. Return Value:
  444. None.
  445. --*/
  446. {
  447. PBCB Bcb;
  448. PVOID Buffer;
  449. NTSTATUS DontCare;
  450. PAGED_CODE();
  451. DebugTrace(+1, Dbg, "FatInitializeDirectoryDirent\n", 0);
  452. DebugTrace( 0, Dbg, " Dcb = %08lx\n", Dcb);
  453. //
  454. // Assert that we are not attempting this on the root directory.
  455. //
  456. ASSERT( NodeType(Dcb) != FAT_NTC_ROOT_DCB );
  457. //
  458. // Assert that this is only attempted on newly created directories.
  459. //
  460. ASSERT( Dcb->Header.AllocationSize.LowPart == 0 );
  461. //
  462. // Prepare the directory file for writing. Note that we can use a single
  463. // Bcb for these two entries because we know they are the first two in
  464. // the directory, and thus together do not span a page boundry. Also
  465. // note that we prepare write 2 entries: one for "." and one for "..".
  466. // The end of directory marker is automatically set since the whole
  467. // directory is initially zero (DIRENT_NEVER_USED).
  468. //
  469. FatPrepareWriteDirectoryFile( IrpContext,
  470. Dcb,
  471. 0,
  472. 2 * sizeof(DIRENT),
  473. &Bcb,
  474. &Buffer,
  475. FALSE,
  476. TRUE,
  477. &DontCare );
  478. ASSERT( NT_SUCCESS( DontCare ));
  479. //
  480. // Add the . and .. entries
  481. //
  482. try {
  483. FatConstructDot( IrpContext, Dcb, ParentDirent, (PDIRENT)Buffer + 0);
  484. FatConstructDotDot( IrpContext, Dcb, ParentDirent, (PDIRENT)Buffer + 1);
  485. //
  486. // Unpin the buffer and return to the caller.
  487. //
  488. } finally {
  489. FatUnpinBcb( IrpContext, Bcb );
  490. }
  491. DebugTrace(-1, Dbg, "FatInitializeDirectoryDirent -> (VOID)\n", 0);
  492. return;
  493. }
  494. VOID
  495. FatTunnelFcbOrDcb (
  496. IN PFCB FcbOrDcb,
  497. IN PCCB Ccb OPTIONAL
  498. )
  499. /*++
  500. Routine Description:
  501. This routine handles tunneling of an Fcb or Dcb associated with
  502. an object whose name is disappearing from a directory.
  503. Arguments:
  504. FcbOrDcb - Supplies the Fcb/Dcb whose name will be going away
  505. Ccb - Supplies the Ccb for the Fcb (not reqired for a Dcb) so
  506. that we know which name the Fcb was opened by
  507. Return Value:
  508. None.
  509. --*/
  510. {
  511. UNICODE_STRING ShortNameWithCase;
  512. UNICODE_STRING DownCaseSeg;
  513. WCHAR ShortNameBuffer[8+1+3];
  514. NTSTATUS Status;
  515. USHORT i;
  516. PAGED_CODE();
  517. DebugTrace(+1, Dbg, "FatTunnelFcbOrDcb\n", 0);
  518. if (NodeType(FcbOrDcb) == FAT_NTC_DCB) {
  519. //
  520. // Directory deletion. Flush all entries from this directory in
  521. // the cache for this volume
  522. //
  523. FsRtlDeleteKeyFromTunnelCache( &FcbOrDcb->Vcb->Tunnel,
  524. FatDirectoryKey(FcbOrDcb) );
  525. } else {
  526. //
  527. // Was a file, so throw it into the tunnel cache
  528. //
  529. //
  530. // Get the short name into UNICODE
  531. //
  532. ShortNameWithCase.Length = 0;
  533. ShortNameWithCase.MaximumLength = sizeof(ShortNameBuffer);
  534. ShortNameWithCase.Buffer = ShortNameBuffer;
  535. Status = RtlOemStringToCountedUnicodeString( &ShortNameWithCase,
  536. &FcbOrDcb->ShortName.Name.Oem,
  537. FALSE);
  538. ASSERT(ShortNameWithCase.Length != 0);
  539. ASSERT(NT_SUCCESS(Status));
  540. if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_8_LOWER_CASE | FCB_STATE_3_LOWER_CASE)) {
  541. //
  542. // Have to repair the case of the short name
  543. //
  544. for (i = 0; i < (ShortNameWithCase.Length/sizeof(WCHAR)) &&
  545. ShortNameWithCase.Buffer[i] != L'.'; i++);
  546. //
  547. // Now pointing at the '.', or otherwise the end of name component
  548. //
  549. if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_8_LOWER_CASE)) {
  550. DownCaseSeg.Buffer = ShortNameWithCase.Buffer;
  551. DownCaseSeg.MaximumLength = DownCaseSeg.Length = i*sizeof(WCHAR);
  552. RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE);
  553. }
  554. i++;
  555. //
  556. // Now pointing at first wchar of the extension.
  557. //
  558. if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_3_LOWER_CASE)) {
  559. //
  560. // It is not neccesarily the case that we can rely on the flag
  561. // indicating that we really have an extension.
  562. //
  563. if ((i*sizeof(WCHAR)) < ShortNameWithCase.Length) {
  564. DownCaseSeg.Buffer = &ShortNameWithCase.Buffer[i];
  565. DownCaseSeg.MaximumLength = DownCaseSeg.Length = ShortNameWithCase.Length - i*sizeof(WCHAR);
  566. RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE);
  567. }
  568. }
  569. }
  570. //
  571. // ... and add it in
  572. //
  573. FsRtlAddToTunnelCache( &FcbOrDcb->Vcb->Tunnel,
  574. FatDirectoryKey(FcbOrDcb->ParentDcb),
  575. &ShortNameWithCase,
  576. &FcbOrDcb->ExactCaseLongName,
  577. BooleanFlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME),
  578. sizeof(LARGE_INTEGER),
  579. &FcbOrDcb->CreationTime );
  580. }
  581. DebugTrace(-1, Dbg, "FatTunnelFcbOrDcb -> (VOID)\n", 0);
  582. return;
  583. }
  584. VOID
  585. FatDeleteDirent (
  586. IN PIRP_CONTEXT IrpContext,
  587. IN PFCB FcbOrDcb,
  588. IN PDELETE_CONTEXT DeleteContext OPTIONAL,
  589. IN BOOLEAN DeleteEa
  590. )
  591. /*++
  592. Routine Description:
  593. This routine Deletes on the disk the indicated dirent. It does
  594. this by marking the dirent as deleted.
  595. Arguments:
  596. FcbOrDcb - Supplies the FCB/DCB for the file/directory being
  597. deleted. For a file the file size and allocation must be zero.
  598. (Zero allocation is implied by a zero cluster index).
  599. For a directory the allocation must be zero.
  600. DeleteContext - This variable, if speicified, may be used to preserve
  601. the file size and first cluster of file information in the dirent
  602. fot the benefit of unerase utilities.
  603. DeleteEa - Tells us whether to delete the EA and whether to check
  604. for no allocation/ Mainly TRUE. FALSE passed in from rename.
  605. Return Value:
  606. None.
  607. --*/
  608. {
  609. PBCB Bcb = NULL;
  610. PDIRENT Dirent;
  611. NTSTATUS DontCare;
  612. ULONG Offset;
  613. ULONG DirentsToDelete;
  614. PAGED_CODE();
  615. DebugTrace(+1, Dbg, "FatDeleteDirent\n", 0);
  616. DebugTrace( 0, Dbg, " FcbOrDcb = %08lx\n", FcbOrDcb);
  617. //
  618. // We must be holding the vcb exclusive here to deal with the locate dirent
  619. // cases where it cannot be holding the parent simply. This is actually
  620. // a true statement from olden daze, lets just wire in our assertion.
  621. //
  622. // Among other reasons, it'd be darn unfortunate if this raced with the
  623. // rename path.
  624. //
  625. ASSERT( ExIsResourceAcquiredExclusiveLite( &FcbOrDcb->Vcb->Resource ));
  626. //
  627. // Assert that we are not attempting this on the root directory.
  628. //
  629. ASSERT( NodeType(FcbOrDcb) != FAT_NTC_ROOT_DCB );
  630. //
  631. // Make sure all requests have zero allocation/file size
  632. //
  633. if (DeleteEa &&
  634. ((FcbOrDcb->Header.AllocationSize.LowPart != 0) ||
  635. ((NodeType(FcbOrDcb) == FAT_NTC_FCB) &&
  636. (FcbOrDcb->Header.FileSize.LowPart != 0)))) {
  637. DebugTrace( 0, Dbg, "Called with non zero allocation/file size.\n", 0);
  638. FatBugCheck( 0, 0, 0 );
  639. }
  640. //
  641. // Now, mark the dirents deleted, unpin the Bcb, and return to the caller.
  642. // Assert that there isn't any allocation associated with this dirent.
  643. //
  644. // Note that this loop will end with Dirent pointing to the short name.
  645. //
  646. try {
  647. //
  648. // We must acquire our parent exclusive to synchronize with enumerators
  649. // who do not hold the vcb (ex: dirctrl).
  650. //
  651. // This relies on our bottom up lockorder.
  652. //
  653. ExAcquireResourceExclusiveLite( FcbOrDcb->ParentDcb->Header.Resource, TRUE );
  654. for ( Offset = FcbOrDcb->LfnOffsetWithinDirectory;
  655. Offset <= FcbOrDcb->DirentOffsetWithinDirectory;
  656. Offset += sizeof(DIRENT), Dirent += 1 ) {
  657. //
  658. // If we stepped onto a new page, or this is the first iteration,
  659. // unpin the old page, and pin the new one.
  660. //
  661. if ((Offset == FcbOrDcb->LfnOffsetWithinDirectory) ||
  662. ((Offset & (PAGE_SIZE - 1)) == 0)) {
  663. FatUnpinBcb( IrpContext, Bcb );
  664. FatPrepareWriteDirectoryFile( IrpContext,
  665. FcbOrDcb->ParentDcb,
  666. Offset,
  667. sizeof(DIRENT),
  668. &Bcb,
  669. (PVOID *)&Dirent,
  670. FALSE,
  671. TRUE,
  672. &DontCare );
  673. }
  674. ASSERT( (Dirent->FirstClusterOfFile == 0) || !DeleteEa );
  675. Dirent->FileName[0] = FAT_DIRENT_DELETED;
  676. }
  677. //
  678. // Back Dirent off by one to point back to the short dirent.
  679. //
  680. Dirent -= 1;
  681. //
  682. // If there are extended attributes for this dirent, we will attempt
  683. // to remove them. We ignore any errors in removing Eas.
  684. //
  685. if (!FatIsFat32(FcbOrDcb->Vcb) &&
  686. DeleteEa && (Dirent->ExtendedAttributes != 0)) {
  687. try {
  688. FatDeleteEa( IrpContext,
  689. FcbOrDcb->Vcb,
  690. Dirent->ExtendedAttributes,
  691. &FcbOrDcb->ShortName.Name.Oem );
  692. } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  693. //
  694. // We catch all exceptions that Fat catches, but don't do
  695. // anything with them.
  696. //
  697. }
  698. }
  699. //
  700. // Now clear the bits in the free dirent mask.
  701. //
  702. DirentsToDelete = (FcbOrDcb->DirentOffsetWithinDirectory -
  703. FcbOrDcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1;
  704. ASSERT( (FcbOrDcb->ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) ||
  705. RtlAreBitsSet( &FcbOrDcb->ParentDcb->Specific.Dcb.FreeDirentBitmap,
  706. FcbOrDcb->LfnOffsetWithinDirectory / sizeof(DIRENT),
  707. DirentsToDelete ) );
  708. RtlClearBits( &FcbOrDcb->ParentDcb->Specific.Dcb.FreeDirentBitmap,
  709. FcbOrDcb->LfnOffsetWithinDirectory / sizeof(DIRENT),
  710. DirentsToDelete );
  711. //
  712. // Now, if the caller specified a DeleteContext, use it.
  713. //
  714. if ( ARGUMENT_PRESENT( DeleteContext ) ) {
  715. Dirent->FileSize = DeleteContext->FileSize;
  716. Dirent->FirstClusterOfFile = (USHORT)DeleteContext->FirstClusterOfFile;
  717. }
  718. //
  719. // If this newly deleted dirent is before the DeletedDirentHint, change
  720. // the DeletedDirentHint to point here.
  721. //
  722. if (FcbOrDcb->DirentOffsetWithinDirectory <
  723. FcbOrDcb->ParentDcb->Specific.Dcb.DeletedDirentHint) {
  724. FcbOrDcb->ParentDcb->Specific.Dcb.DeletedDirentHint =
  725. FcbOrDcb->LfnOffsetWithinDirectory;
  726. }
  727. } finally {
  728. FatUnpinBcb( IrpContext, Bcb );
  729. //
  730. // Release our parent.
  731. //
  732. ExReleaseResourceLite( FcbOrDcb->ParentDcb->Header.Resource );
  733. }
  734. DebugTrace(-1, Dbg, "FatDeleteDirent -> (VOID)\n", 0);
  735. return;
  736. }
  737. BOOLEAN
  738. FatLfnDirentExists (
  739. IN PIRP_CONTEXT IrpContext,
  740. IN PDCB Dcb,
  741. IN PUNICODE_STRING Lfn,
  742. IN PUNICODE_STRING LfnTmp
  743. )
  744. /*++
  745. Routine Description:
  746. This routine looks for a given Lfn in a directory
  747. Arguments:
  748. Dcb - The directory to search
  749. Lfn - The Lfn to look for
  750. Lfn - Temporary buffer to use to search for Lfn with (if < MAX_LFN then this
  751. function may cause it to be allocated from pool if not large enough.
  752. Retrn Value:
  753. BOOLEAN TRUE if it exists, FALSE if not
  754. --*/
  755. {
  756. CCB Ccb;
  757. PDIRENT Dirent;
  758. PBCB DirentBcb = NULL;
  759. VBO DirentByteOffset;
  760. BOOLEAN Result = FALSE;
  761. PAGED_CODE();
  762. //
  763. // Pay performance penalty by forcing the compares to be case insensitive as
  764. // opposed to grabbing more pool for a monocased copy of the Lfn. This is slight.
  765. //
  766. Ccb.UnicodeQueryTemplate = *Lfn;
  767. Ccb.ContainsWildCards = FALSE;
  768. Ccb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE | CCB_FLAG_QUERY_TEMPLATE_MIXED;
  769. try {
  770. FatLocateDirent( IrpContext,
  771. Dcb,
  772. &Ccb,
  773. 0,
  774. &Dirent,
  775. &DirentBcb,
  776. &DirentByteOffset,
  777. NULL,
  778. LfnTmp);
  779. } finally {
  780. if (DirentBcb) {
  781. Result = TRUE;
  782. }
  783. FatUnpinBcb(IrpContext, DirentBcb);
  784. }
  785. return Result;
  786. }
  787. VOID
  788. FatLocateDirent (
  789. IN PIRP_CONTEXT IrpContext,
  790. IN PDCB ParentDirectory,
  791. IN PCCB Ccb,
  792. IN VBO OffsetToStartSearchFrom,
  793. OUT PDIRENT *Dirent,
  794. OUT PBCB *Bcb,
  795. OUT PVBO ByteOffset,
  796. OUT PBOOLEAN FileNameDos OPTIONAL,
  797. IN OUT PUNICODE_STRING LongFileName OPTIONAL
  798. )
  799. /*++
  800. Routine Description:
  801. This routine locates on the disk an undeleted dirent matching a given name.
  802. Arguments:
  803. ParentDirectory - Supplies the DCB for the directory to search
  804. Ccb - Contains a context control block with all matching information.
  805. OffsetToStartSearchFrom - Supplies the VBO within the parent directory
  806. from which to start looking for another real dirent.
  807. Dirent - Receives a pointer to the located dirent if one was found
  808. or NULL otherwise.
  809. Bcb - Receives the Bcb for the located dirent if one was found or
  810. NULL otherwise.
  811. ByteOffset - Receives the VBO within the Parent directory for
  812. the located dirent if one was found, or 0 otherwise.
  813. FileNameDos - Receives TRUE if the element of the dirent we hit on
  814. was the short (non LFN) side
  815. LongFileName - If specified, this parameter returns the long file name
  816. associated with the returned dirent. Note that it is the caller's
  817. responsibility to provide the buffer (and set MaximumLength
  818. accordingly) for this unicode string. The Length field is reset
  819. to 0 by this routine on invocation. If the supplied buffer is not
  820. large enough, a new one will be allocated from pool.
  821. Return Value:
  822. None.
  823. --*/
  824. {
  825. NTSTATUS Status = STATUS_SUCCESS;
  826. OEM_STRING Name;
  827. UCHAR NameBuffer[12];
  828. UNICODE_STRING UpcasedLfn;
  829. WCHAR LocalLfnBuffer[32];
  830. BOOLEAN LfnInProgress = FALSE;
  831. UCHAR LfnChecksum;
  832. ULONG LfnSize;
  833. ULONG LfnIndex;
  834. UCHAR Ordinal;
  835. VBO LfnByteOffset;
  836. TimerStart(Dbg);
  837. PAGED_CODE();
  838. DebugTrace(+1, Dbg, "FatLocateDirent\n", 0);
  839. DebugTrace( 0, Dbg, " ParentDirectory = %08lx\n", ParentDirectory);
  840. DebugTrace( 0, Dbg, " OffsetToStartSearchFrom = %08lx\n", OffsetToStartSearchFrom);
  841. DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent);
  842. DebugTrace( 0, Dbg, " Bcb = %08lx\n", Bcb);
  843. DebugTrace( 0, Dbg, " ByteOffset = %08lx\n", ByteOffset);
  844. //
  845. // We must have acquired the parent or the vcb to synchronize with deletion. This
  846. // is important since we can't survive racing a thread marking a series of lfn
  847. // dirents deleted - we'd get a bogus ordinal, and otherwise get really messed up.
  848. //
  849. // This routine cannot do the acquire since it would be out-of-order with respect
  850. // to the Bcb resources on iterative calls. Our order has Bcbs as the inferior resource.
  851. //
  852. // Deletion always grabs the parent (safely - this used to not be possible until the
  853. // multiple fcb lockorder was fixed to be bottom up!). Deletion always occurs with
  854. // the vcb held exclusive as well, and this will cover the cases where we can't easily
  855. // hold the parent here, see above.
  856. //
  857. ASSERT( ExIsResourceAcquiredSharedLite( ParentDirectory->Header.Resource ) ||
  858. ExIsResourceAcquiredExclusiveLite( ParentDirectory->Header.Resource ) ||
  859. ExIsResourceAcquiredSharedLite( &ParentDirectory->Vcb->Resource ) ||
  860. ExIsResourceAcquiredExclusiveLite( &ParentDirectory->Vcb->Resource ));
  861. //
  862. // The algorithm here is pretty simple. We just walk through the
  863. // parent directory until we:
  864. //
  865. // A) Find a matching entry.
  866. // B) Can't Wait
  867. // C) Hit the End of Directory
  868. // D) Hit Eof
  869. //
  870. // In the first case we found it, in the latter three cases we did not.
  871. //
  872. //
  873. // Set up the strings that receives file names from our search
  874. //
  875. Name.MaximumLength = 12;
  876. Name.Buffer = NameBuffer;
  877. UpcasedLfn.Length = 0;
  878. UpcasedLfn.MaximumLength = sizeof( LocalLfnBuffer);
  879. UpcasedLfn.Buffer = LocalLfnBuffer;
  880. //
  881. // If we were given a non-NULL Bcb, compute the new Dirent address
  882. // from the prior one, or unpin the Bcb if the new Dirent is not pinned.
  883. //
  884. if (*Bcb != NULL) {
  885. if ((OffsetToStartSearchFrom / PAGE_SIZE) == (*ByteOffset / PAGE_SIZE)) {
  886. *Dirent += (OffsetToStartSearchFrom - *ByteOffset) / sizeof(DIRENT);
  887. } else {
  888. FatUnpinBcb( IrpContext, *Bcb );
  889. }
  890. }
  891. //
  892. // Init the Lfn if we were given one.
  893. //
  894. if (ARGUMENT_PRESENT(LongFileName)) {
  895. LongFileName->Length = 0;
  896. }
  897. //
  898. // Init the FileNameDos flag
  899. //
  900. if (FileNameDos) {
  901. *FileNameDos = FALSE;
  902. }
  903. //
  904. // Round up OffsetToStartSearchFrom to the nearest Dirent, and store
  905. // in ByteOffset. Note that this wipes out the prior value.
  906. //
  907. *ByteOffset = (OffsetToStartSearchFrom + (sizeof(DIRENT) - 1))
  908. & ~(sizeof(DIRENT) - 1);
  909. try {
  910. while ( TRUE ) {
  911. BOOLEAN FoundValidLfn;
  912. //
  913. // Try to read in the dirent
  914. //
  915. FatReadDirent( IrpContext,
  916. ParentDirectory,
  917. *ByteOffset,
  918. Bcb,
  919. Dirent,
  920. &Status );
  921. //
  922. // If End Directory dirent or EOF, set all out parameters to
  923. // indicate entry not found and, like, bail.
  924. //
  925. // Note that the order of evaluation here is important since we
  926. // cannot check the first character of the dirent until after we
  927. // know we are not beyond EOF
  928. //
  929. if ((Status == STATUS_END_OF_FILE) ||
  930. ((*Dirent)->FileName[0] == FAT_DIRENT_NEVER_USED)) {
  931. DebugTrace( 0, Dbg, "End of directory: entry not found.\n", 0);
  932. //
  933. // If there is a Bcb, unpin it and set it to null
  934. //
  935. FatUnpinBcb( IrpContext, *Bcb );
  936. *Dirent = NULL;
  937. *ByteOffset = 0;
  938. break;
  939. }
  940. //
  941. // If the entry is marked deleted, skip. If there was an Lfn in
  942. // progress we throw it out at this point.
  943. //
  944. if ((*Dirent)->FileName[0] == FAT_DIRENT_DELETED) {
  945. LfnInProgress = FALSE;
  946. goto GetNextDirent;
  947. }
  948. //
  949. // If we have wandered onto an LFN entry, try to interpret it.
  950. //
  951. if (FatData.ChicagoMode &&
  952. ARGUMENT_PRESENT(LongFileName) &&
  953. ((*Dirent)->Attributes == FAT_DIRENT_ATTR_LFN)) {
  954. PLFN_DIRENT Lfn;
  955. Lfn = (PLFN_DIRENT)*Dirent;
  956. if (LfnInProgress) {
  957. //
  958. // Check for a proper continuation of the Lfn in progress.
  959. //
  960. if ((Lfn->Ordinal & FAT_LAST_LONG_ENTRY) ||
  961. (Lfn->Ordinal == 0) ||
  962. (Lfn->Ordinal != Ordinal - 1) ||
  963. (Lfn->Checksum != LfnChecksum) ||
  964. (Lfn->MustBeZero != 0)) {
  965. //
  966. // The Lfn is not proper, stop constructing it.
  967. //
  968. LfnInProgress = FALSE;
  969. } else {
  970. ASSERT( ((LfnIndex % 13) == 0) && LfnIndex );
  971. LfnIndex -= 13;
  972. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+0],
  973. &Lfn->Name1[0],
  974. 5*sizeof(WCHAR) );
  975. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+5],
  976. &Lfn->Name2[0],
  977. 6 * sizeof(WCHAR) );
  978. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+11],
  979. &Lfn->Name3[0],
  980. 2 * sizeof(WCHAR) );
  981. Ordinal = Lfn->Ordinal;
  982. LfnByteOffset = *ByteOffset;
  983. }
  984. }
  985. //
  986. // Now check (maybe again) if we should analyze this entry
  987. // for a possible last entry.
  988. //
  989. if ((!LfnInProgress) &&
  990. (Lfn->Ordinal & FAT_LAST_LONG_ENTRY) &&
  991. ((Lfn->Ordinal & ~FAT_LAST_LONG_ENTRY) <= MAX_LFN_DIRENTS) &&
  992. (Lfn->MustBeZero == 0)) {
  993. BOOLEAN CheckTail = FALSE;
  994. Ordinal = Lfn->Ordinal & ~FAT_LAST_LONG_ENTRY;
  995. //
  996. // We're usually permissive (following the lead of Win9x) when we find
  997. // malformation of the LFN dirent pile. I'm not sure this is a good idea,
  998. // so I'm going to trigger corruption on this particularly ugly one. Perhaps
  999. // we should come back and redo the original code here with this in mind in the
  1000. // future.
  1001. //
  1002. if (Ordinal == 0) {
  1003. //
  1004. // First LFN in the pile was zero marked as the last. This is never
  1005. // possible since oridinals are 1-based.
  1006. //
  1007. FatPopUpFileCorrupt( IrpContext, ParentDirectory );
  1008. FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  1009. }
  1010. LfnIndex = (Ordinal - 1) * 13;
  1011. FatEnsureStringBufferEnough( LongFileName,
  1012. (USHORT)((LfnIndex + 13) << 1));
  1013. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+0],
  1014. &Lfn->Name1[0],
  1015. 5*sizeof(WCHAR));
  1016. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+5],
  1017. &Lfn->Name2[0],
  1018. 6 * sizeof(WCHAR) );
  1019. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+11],
  1020. &Lfn->Name3[0],
  1021. 2 * sizeof(WCHAR) );
  1022. //
  1023. // Now compute the Lfn size and make sure that the tail
  1024. // bytes are correct.
  1025. //
  1026. while (LfnIndex != (ULONG)Ordinal * 13) {
  1027. if (!CheckTail) {
  1028. if (LongFileName->Buffer[LfnIndex] == 0x0000) {
  1029. LfnSize = LfnIndex;
  1030. CheckTail = TRUE;
  1031. }
  1032. } else {
  1033. if (LongFileName->Buffer[LfnIndex] != 0xffff) {
  1034. break;
  1035. }
  1036. }
  1037. LfnIndex += 1;
  1038. }
  1039. //
  1040. // If we exited this loop prematurely, the LFN is not valid.
  1041. //
  1042. if (LfnIndex == (ULONG)Ordinal * 13) {
  1043. //
  1044. // If we didn't find the NULL terminator, then the size
  1045. // is LfnIndex.
  1046. //
  1047. if (!CheckTail) {
  1048. LfnSize = LfnIndex;
  1049. }
  1050. LfnIndex -= 13;
  1051. LfnInProgress = TRUE;
  1052. LfnChecksum = Lfn->Checksum;
  1053. LfnByteOffset = *ByteOffset;
  1054. }
  1055. }
  1056. //
  1057. // Move on to the next dirent.
  1058. //
  1059. goto GetNextDirent;
  1060. }
  1061. //
  1062. // If this is the volume label, skip. Note that we never arrive here
  1063. // while building the LFN. If we did, we weren't asked to find LFNs
  1064. // and that is another good reason to skip this LFN fragment.
  1065. //
  1066. if (FlagOn((*Dirent)->Attributes, FAT_DIRENT_ATTR_VOLUME_ID)) {
  1067. //
  1068. // If we actually were asked to hand back volume labels,
  1069. // do it.
  1070. //
  1071. if (FlagOn(Ccb->Flags, CCB_FLAG_MATCH_VOLUME_ID)) {
  1072. break;
  1073. }
  1074. goto GetNextDirent;
  1075. }
  1076. //
  1077. // We may have just stepped off a valid Lfn run. Check to see if
  1078. // it is indeed valid for the following dirent.
  1079. //
  1080. if (LfnInProgress &&
  1081. (*ByteOffset == LfnByteOffset + sizeof(DIRENT)) &&
  1082. (LfnIndex == 0) &&
  1083. (FatComputeLfnChecksum(*Dirent) == LfnChecksum)) {
  1084. ASSERT( Ordinal == 1);
  1085. FoundValidLfn = TRUE;
  1086. LongFileName->Length = (USHORT)(LfnSize * sizeof(WCHAR));
  1087. } else {
  1088. FoundValidLfn = FALSE;
  1089. }
  1090. //
  1091. // If we are supposed to match all entries, then match this entry.
  1092. //
  1093. if (FlagOn(Ccb->Flags, CCB_FLAG_MATCH_ALL)) {
  1094. break;
  1095. }
  1096. //
  1097. // Check against the short name given if one was.
  1098. //
  1099. if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE )) {
  1100. if (Ccb->ContainsWildCards) {
  1101. //
  1102. // If we get one, note that all out parameters are already set.
  1103. //
  1104. (VOID)Fat8dot3ToString( IrpContext, (*Dirent), FALSE, &Name );
  1105. //
  1106. // For fat we special case the ".." dirent because we want it to
  1107. // match ????????.??? and to do that we change ".." to "." before
  1108. // calling the Fsrtl routine. But only do this if the expression
  1109. // is greater than one character long.
  1110. //
  1111. if ((Name.Length == 2) &&
  1112. (Name.Buffer[0] == '.') &&
  1113. (Name.Buffer[1] == '.') &&
  1114. (Ccb->OemQueryTemplate.Wild.Length > 1)) {
  1115. Name.Length = 1;
  1116. }
  1117. if (FatIsNameInExpression( IrpContext,
  1118. Ccb->OemQueryTemplate.Wild,
  1119. Name)) {
  1120. DebugTrace( 0, Dbg, "Entry found: Name = \"%Z\"\n", &Name);
  1121. DebugTrace( 0, Dbg, " VBO = %08lx\n", *ByteOffset);
  1122. if (FileNameDos) {
  1123. *FileNameDos = TRUE;
  1124. }
  1125. break;
  1126. }
  1127. } else {
  1128. //
  1129. // Do the quickest 8.3 equivalency check possible
  1130. //
  1131. if (!FlagOn((*Dirent)->Attributes, FAT_DIRENT_ATTR_VOLUME_ID) &&
  1132. (*(PULONG)&(Ccb->OemQueryTemplate.Constant[0]) == *(PULONG)&((*Dirent)->FileName[0])) &&
  1133. (*(PULONG)&(Ccb->OemQueryTemplate.Constant[4]) == *(PULONG)&((*Dirent)->FileName[4])) &&
  1134. (*(PUSHORT)&(Ccb->OemQueryTemplate.Constant[8]) == *(PUSHORT)&((*Dirent)->FileName[8])) &&
  1135. (*(PUCHAR)&(Ccb->OemQueryTemplate.Constant[10]) == *(PUCHAR)&((*Dirent)->FileName[10]))) {
  1136. DebugTrace( 0, Dbg, "Entry found.\n", 0);
  1137. if (FileNameDos) {
  1138. *FileNameDos = TRUE;
  1139. }
  1140. break;
  1141. }
  1142. }
  1143. }
  1144. //
  1145. // No matches were found with the short name. If an LFN exists,
  1146. // use it for the search.
  1147. //
  1148. if (FoundValidLfn) {
  1149. //
  1150. // First do a quick check here for different sized constant
  1151. // name and expression before upcasing.
  1152. //
  1153. if (!Ccb->ContainsWildCards &&
  1154. Ccb->UnicodeQueryTemplate.Length != (USHORT)(LfnSize * sizeof(WCHAR))) {
  1155. //
  1156. // Move on to the next dirent.
  1157. //
  1158. FoundValidLfn = FALSE;
  1159. LongFileName->Length = 0;
  1160. goto GetNextDirent;
  1161. }
  1162. //
  1163. // We need to upcase the name we found.
  1164. // We need a buffer. Try to avoid doing an allocation.
  1165. //
  1166. FatEnsureStringBufferEnough( &UpcasedLfn,
  1167. LongFileName->Length);
  1168. Status = RtlUpcaseUnicodeString( &UpcasedLfn,
  1169. LongFileName,
  1170. FALSE );
  1171. if (!NT_SUCCESS(Status)) {
  1172. FatNormalizeAndRaiseStatus( IrpContext, Status );
  1173. }
  1174. //
  1175. // Do the compare
  1176. //
  1177. if (Ccb->ContainsWildCards) {
  1178. if (FsRtlIsNameInExpression( &Ccb->UnicodeQueryTemplate,
  1179. &UpcasedLfn,
  1180. TRUE,
  1181. NULL )) {
  1182. break;
  1183. }
  1184. } else {
  1185. if (FsRtlAreNamesEqual( &Ccb->UnicodeQueryTemplate,
  1186. &UpcasedLfn,
  1187. BooleanFlagOn( Ccb->Flags, CCB_FLAG_QUERY_TEMPLATE_MIXED ),
  1188. NULL )) {
  1189. break;
  1190. }
  1191. }
  1192. }
  1193. //
  1194. // This long name was not a match. Zero out the Length field.
  1195. //
  1196. if (FoundValidLfn) {
  1197. FoundValidLfn = FALSE;
  1198. LongFileName->Length = 0;
  1199. }
  1200. GetNextDirent:
  1201. //
  1202. // Move on to the next dirent.
  1203. //
  1204. *ByteOffset += sizeof(DIRENT);
  1205. *Dirent += 1;
  1206. }
  1207. } finally {
  1208. FatFreeStringBuffer( &UpcasedLfn);
  1209. }
  1210. DebugTrace(-1, Dbg, "FatLocateDirent -> (VOID)\n", 0);
  1211. TimerStop(Dbg,"FatLocateDirent");
  1212. return;
  1213. }
  1214. VOID
  1215. FatLocateSimpleOemDirent (
  1216. IN PIRP_CONTEXT IrpContext,
  1217. IN PDCB ParentDirectory,
  1218. IN POEM_STRING FileName,
  1219. OUT PDIRENT *Dirent,
  1220. OUT PBCB *Bcb,
  1221. OUT PVBO ByteOffset
  1222. )
  1223. /*++
  1224. Routine Description:
  1225. This routine locates on the disk an undelted simple Oem dirent. By simple
  1226. I mean that FileName cannot contain any extended characters, and we do
  1227. not search LFNs or return them.
  1228. Arguments:
  1229. ParentDirectory - Supplies the DCB for the directory in which
  1230. to search
  1231. FileName - Supplies the filename to search for. The name may contain
  1232. wild cards
  1233. OffsetToStartSearchFrom - Supplies the VBO within the parent directory
  1234. from which to start looking for another real dirent.
  1235. Dirent - Receives a pointer to the located dirent if one was found
  1236. or NULL otherwise.
  1237. Bcb - Receives the Bcb for the located dirent if one was found or
  1238. NULL otherwise.
  1239. ByteOffset - Receives the VBO within the Parent directory for
  1240. the located dirent if one was found, or 0 otherwise.
  1241. Return Value:
  1242. None.
  1243. --*/
  1244. {
  1245. CCB LocalCcb;
  1246. PAGED_CODE();
  1247. //
  1248. // Note, this routine is called rarely, so performance is not critical.
  1249. // Just fill in a Ccb structure on my stack with the values that are
  1250. // required.
  1251. //
  1252. FatStringTo8dot3( IrpContext,
  1253. *FileName,
  1254. &LocalCcb.OemQueryTemplate.Constant );
  1255. LocalCcb.ContainsWildCards = FALSE;
  1256. LocalCcb.Flags = 0;
  1257. FatLocateDirent( IrpContext,
  1258. ParentDirectory,
  1259. &LocalCcb,
  1260. 0,
  1261. Dirent,
  1262. Bcb,
  1263. ByteOffset,
  1264. NULL,
  1265. NULL);
  1266. return;
  1267. }
  1268. VOID
  1269. FatLocateVolumeLabel (
  1270. IN PIRP_CONTEXT IrpContext,
  1271. IN PVCB Vcb,
  1272. OUT PDIRENT *Dirent,
  1273. OUT PBCB *Bcb,
  1274. OUT PVBO ByteOffset
  1275. )
  1276. /*++
  1277. Routine Description:
  1278. This routine locates on the disk a dirent representing the volume
  1279. label. It does this by searching the root directory for a special
  1280. volume label dirent.
  1281. Arguments:
  1282. Vcb - Supplies the VCB for the volume to search
  1283. Dirent - Receives a pointer to the located dirent if one was found
  1284. or NULL otherwise.
  1285. Bcb - Receives the Bcb for the located dirent if one was found or
  1286. NULL otherwise.
  1287. ByteOffset - Receives the VBO within the Parent directory for
  1288. the located dirent if one was found, or 0 otherwise.
  1289. Return Value:
  1290. None.
  1291. --*/
  1292. {
  1293. NTSTATUS Status;
  1294. PAGED_CODE();
  1295. DebugTrace(+1, Dbg, "FatLocateVolumeLabel\n", 0);
  1296. DebugTrace( 0, Dbg, " Vcb = %08lx\n", Vcb);
  1297. DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent);
  1298. DebugTrace( 0, Dbg, " Bcb = %08lx\n", Bcb);
  1299. DebugTrace( 0, Dbg, " ByteOffset = %08lx\n", ByteOffset);
  1300. //
  1301. // The algorithm here is really simple. We just walk through the
  1302. // root directory until we:
  1303. //
  1304. // A) Find the non-deleted volume label
  1305. // B) Can't Wait
  1306. // C) Hit the End of Directory
  1307. // D) Hit Eof
  1308. //
  1309. // In the first case we found it, in the latter three cases we did not.
  1310. //
  1311. *Bcb = NULL;
  1312. *ByteOffset = 0;
  1313. while ( TRUE ) {
  1314. //
  1315. // Try to read in the dirent
  1316. //
  1317. FatReadDirent( IrpContext,
  1318. Vcb->RootDcb,
  1319. *ByteOffset,
  1320. Bcb,
  1321. Dirent,
  1322. &Status );
  1323. //
  1324. // If End Directory dirent or EOF, set all out parameters to
  1325. // indicate volume label not found and, like, bail.
  1326. //
  1327. // Note that the order of evaluation here is important since we cannot
  1328. // check the first character of the dirent until after we know we
  1329. // are not beyond EOF
  1330. //
  1331. if ((Status == STATUS_END_OF_FILE) ||
  1332. ((*Dirent)->FileName[0] == FAT_DIRENT_NEVER_USED)) {
  1333. DebugTrace( 0, Dbg, "Volume label not found.\n", 0);
  1334. //
  1335. // If there is a Bcb, unpin it and set it to null
  1336. //
  1337. FatUnpinBcb( IrpContext, *Bcb );
  1338. *Dirent = NULL;
  1339. *ByteOffset = 0;
  1340. break;
  1341. }
  1342. //
  1343. // If the entry is the non-deleted volume label break from the loop.
  1344. //
  1345. // Note that all out parameters are already correctly set.
  1346. //
  1347. if ((((*Dirent)->Attributes & ~FAT_DIRENT_ATTR_ARCHIVE) == FAT_DIRENT_ATTR_VOLUME_ID) &&
  1348. ((*Dirent)->FileName[0] != FAT_DIRENT_DELETED)) {
  1349. DebugTrace( 0, Dbg, "Volume label found at VBO = %08lx\n", *ByteOffset);
  1350. //
  1351. // We may set this dirty, so pin it.
  1352. //
  1353. FatPinMappedData( IrpContext,
  1354. Vcb->RootDcb,
  1355. *ByteOffset,
  1356. sizeof(DIRENT),
  1357. Bcb );
  1358. break;
  1359. }
  1360. //
  1361. // Move on to the next dirent.
  1362. //
  1363. *ByteOffset += sizeof(DIRENT);
  1364. *Dirent += 1;
  1365. }
  1366. DebugTrace(-1, Dbg, "FatLocateVolumeLabel -> (VOID)\n", 0);
  1367. return;
  1368. }
  1369. VOID
  1370. FatGetDirentFromFcbOrDcb (
  1371. IN PIRP_CONTEXT IrpContext,
  1372. IN PFCB FcbOrDcb,
  1373. OUT PDIRENT *Dirent,
  1374. OUT PBCB *Bcb
  1375. )
  1376. /*++
  1377. Routine Description:
  1378. This routine reads locates on the disk the dirent denoted by the
  1379. specified Fcb/Dcb.
  1380. Arguments:
  1381. FcbOrDcb - Supplies the FCB/DCB for the file/directory whose dirent
  1382. we are trying to read in. This must not be the root dcb.
  1383. Dirent - Receives a pointer to the dirent
  1384. Bcb - Receives the Bcb for the dirent
  1385. Return Value:
  1386. None.
  1387. --*/
  1388. {
  1389. NTSTATUS DontCare;
  1390. PAGED_CODE();
  1391. DebugTrace(+1, Dbg, "FatGetDirentFromFcbOrDcb\n", 0);
  1392. DebugTrace( 0, Dbg, " FcbOrDcb = %08lx\n", FcbOrDcb);
  1393. DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent);
  1394. DebugTrace( 0, Dbg, " Bcb = %08lx\n", Bcb);
  1395. //
  1396. // Assert that we are not attempting this on the root directory.
  1397. //
  1398. ASSERT( NodeType(FcbOrDcb) != FAT_NTC_ROOT_DCB );
  1399. //
  1400. // We know the offset of the dirent within the directory file,
  1401. // so we just read it (with pinning).
  1402. //
  1403. FatReadDirectoryFile( IrpContext,
  1404. FcbOrDcb->ParentDcb,
  1405. FcbOrDcb->DirentOffsetWithinDirectory,
  1406. sizeof(DIRENT),
  1407. TRUE,
  1408. Bcb,
  1409. (PVOID *)Dirent,
  1410. &DontCare );
  1411. //
  1412. // Previous call can fail. We used to assert success, but we use this
  1413. // as part of volume verification (DetermineAndMarkFcbCondition) after
  1414. // media has been removed. Clearly the directory could shrink and we
  1415. // would try to read beyond filesize.
  1416. //
  1417. // The caller will note this via NULL pointers for Bcb/Buffer. Note that
  1418. // both asserts below are OK since this should never happen fixed media.
  1419. //
  1420. // This was a Prefix catch.
  1421. //
  1422. ASSERT( FlagOn( FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) ||
  1423. NT_SUCCESS( DontCare ));
  1424. //
  1425. // Note also that the only way this could fail is if the Fcb was being
  1426. // verified. This can't happen if the Fcb is in good condition.
  1427. //
  1428. // Also a Prefix catch.
  1429. //
  1430. ASSERT( NT_SUCCESS( DontCare ) || FcbOrDcb->FcbCondition == FcbNeedsToBeVerified );
  1431. DebugTrace(-1, Dbg, "FatGetDirentFromFcbOrDcb -> (VOID)\n", 0);
  1432. return;
  1433. }
  1434. BOOLEAN
  1435. FatIsDirectoryEmpty (
  1436. IN PIRP_CONTEXT IrpContext,
  1437. IN PDCB Dcb
  1438. )
  1439. /*++
  1440. Routine Description:
  1441. This routine indicates to the caller if the specified directory
  1442. is empty. (i.e., it is not the root dcb and it only contains
  1443. the "." and ".." entries, or deleted files).
  1444. Arguments:
  1445. Dcb - Supplies the DCB for the directory being queried.
  1446. Return Value:
  1447. BOOLEAN - Returns TRUE if the directory is empty and
  1448. FALSE if the directory and is not empty.
  1449. --*/
  1450. {
  1451. PBCB Bcb;
  1452. ULONG ByteOffset;
  1453. PDIRENT Dirent;
  1454. BOOLEAN IsDirectoryEmpty = FALSE;
  1455. NTSTATUS Status = STATUS_SUCCESS;
  1456. PAGED_CODE();
  1457. DebugTrace(+1, Dbg, "FatIsDirectoryEmpty\n", 0);
  1458. DebugTrace( 0, Dbg, " Dcb = %08lx\n", Dcb);
  1459. DebugTrace( 0, Dbg, " IsDirectoryEmpty = %08lx\n", IsDirectoryEmpty);
  1460. //
  1461. // Check to see if the first entry is an and of directory marker.
  1462. // For the root directory we check at Vbo = 0, for normal directories
  1463. // we check after the "." and ".." entries.
  1464. //
  1465. ByteOffset = (NodeType(Dcb) == FAT_NTC_ROOT_DCB) ? 0 : 2*sizeof(DIRENT);
  1466. //
  1467. // We just march through the directory looking for anything other
  1468. // than deleted files, LFNs, an EOF, or end of directory marker.
  1469. //
  1470. Bcb = NULL;
  1471. try {
  1472. while ( TRUE ) {
  1473. //
  1474. // Try to read in the dirent
  1475. //
  1476. FatReadDirent( IrpContext,
  1477. Dcb,
  1478. ByteOffset,
  1479. &Bcb,
  1480. &Dirent,
  1481. &Status );
  1482. //
  1483. // If End Directory dirent or EOF, set IsDirectoryEmpty to TRUE and,
  1484. // like, bail.
  1485. //
  1486. // Note that the order of evaluation here is important since we cannot
  1487. // check the first character of the dirent until after we know we
  1488. // are not beyond EOF
  1489. //
  1490. if ((Status == STATUS_END_OF_FILE) ||
  1491. (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) {
  1492. DebugTrace( 0, Dbg, "Empty. Last exempt entry at VBO = %08lx\n", ByteOffset);
  1493. IsDirectoryEmpty = TRUE;
  1494. break;
  1495. }
  1496. //
  1497. // If this dirent is NOT deleted or an LFN set IsDirectoryEmpty to
  1498. // FALSE and, like, bail.
  1499. //
  1500. if ((Dirent->FileName[0] != FAT_DIRENT_DELETED) &&
  1501. (Dirent->Attributes != FAT_DIRENT_ATTR_LFN)) {
  1502. DebugTrace( 0, Dbg, "Not Empty. First entry at VBO = %08lx\n", ByteOffset);
  1503. IsDirectoryEmpty = FALSE;
  1504. break;
  1505. }
  1506. //
  1507. // Move on to the next dirent.
  1508. //
  1509. ByteOffset += sizeof(DIRENT);
  1510. Dirent += 1;
  1511. }
  1512. } finally {
  1513. FatUnpinBcb( IrpContext, Bcb );
  1514. }
  1515. DebugTrace(-1, Dbg, "FatIsDirectoryEmpty -> %ld\n", IsDirectoryEmpty);
  1516. return IsDirectoryEmpty;
  1517. }
  1518. VOID
  1519. FatConstructDirent (
  1520. IN PIRP_CONTEXT IrpContext,
  1521. IN OUT PDIRENT Dirent,
  1522. IN POEM_STRING FileName,
  1523. IN BOOLEAN ComponentReallyLowercase,
  1524. IN BOOLEAN ExtensionReallyLowercase,
  1525. IN PUNICODE_STRING Lfn OPTIONAL,
  1526. IN UCHAR Attributes,
  1527. IN BOOLEAN ZeroAndSetTimeFields,
  1528. IN PLARGE_INTEGER SetCreationTime OPTIONAL
  1529. )
  1530. /*++
  1531. Routine Description:
  1532. This routine modifies the fields of a dirent.
  1533. Arguments:
  1534. Dirent - Supplies the dirent being modified.
  1535. FileName - Supplies the name to store in the Dirent. This
  1536. name must not contain wildcards.
  1537. ComponentReallyLowercase - This boolean indicates that the User Specified
  1538. compoent name was really all a-z and < 0x80 characters. We set the
  1539. magic bit in this case.
  1540. ExtensionReallyLowercase - Same as above, but for the extension.
  1541. Lfn - May supply a long file name.
  1542. Attributes - Supplies the attributes to store in the dirent
  1543. ZeroAndSetTimeFields - Tells whether or not to initially zero the dirent
  1544. and update the time fields.
  1545. SetCreationTime - If specified, contains a timestamp to use as the creation
  1546. time of this dirent
  1547. Return Value:
  1548. None.
  1549. --*/
  1550. {
  1551. PAGED_CODE();
  1552. DebugTrace(+1, Dbg, "FatConstructDirent\n", 0);
  1553. DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent);
  1554. DebugTrace( 0, Dbg, " FileName = %Z\n", FileName);
  1555. DebugTrace( 0, Dbg, " Attributes = %08lx\n", Attributes);
  1556. if (ZeroAndSetTimeFields) {
  1557. RtlZeroMemory( Dirent, sizeof(DIRENT) );
  1558. }
  1559. //
  1560. // We just merrily go and fill up the dirent with the fields given.
  1561. //
  1562. FatStringTo8dot3( IrpContext, *FileName, (PFAT8DOT3)&Dirent->FileName[0] );
  1563. if (ZeroAndSetTimeFields || SetCreationTime) {
  1564. LARGE_INTEGER Time, SaveTime;
  1565. KeQuerySystemTime( &Time );
  1566. if (FatData.ChicagoMode) {
  1567. if (!SetCreationTime || !FatNtTimeToFatTime( IrpContext,
  1568. SetCreationTime,
  1569. FALSE,
  1570. &Dirent->CreationTime,
  1571. &Dirent->CreationMSec )) {
  1572. //
  1573. // No tunneled time or the tunneled time was bogus. Since we aren't
  1574. // responsible for initializing the to-be-created Fcb with creation
  1575. // time, we can't do the usual thing and let NtTimeToFatTime perform
  1576. // rounding on the timestamp - this would mess up converting to the
  1577. // LastWriteTime below.
  1578. //
  1579. SaveTime = Time;
  1580. if (!FatNtTimeToFatTime( IrpContext,
  1581. &SaveTime,
  1582. FALSE,
  1583. &Dirent->CreationTime,
  1584. &Dirent->CreationMSec )) {
  1585. //
  1586. // Failed again. Wow.
  1587. //
  1588. RtlZeroMemory( &Dirent->CreationTime, sizeof(FAT_TIME_STAMP));
  1589. Dirent->CreationMSec = 0;
  1590. }
  1591. }
  1592. }
  1593. if (ZeroAndSetTimeFields) {
  1594. //
  1595. // We only touch the other timestamps if we are initializing the dirent
  1596. //
  1597. if (!FatNtTimeToFatTime( IrpContext,
  1598. &Time,
  1599. TRUE,
  1600. &Dirent->LastWriteTime,
  1601. NULL )) {
  1602. DebugTrace( 0, Dbg, "Current time invalid.\n", 0);
  1603. RtlZeroMemory( &Dirent->LastWriteTime, sizeof(FAT_TIME_STAMP) );
  1604. }
  1605. if (FatData.ChicagoMode) {
  1606. Dirent->LastAccessDate = Dirent->LastWriteTime.Date;
  1607. }
  1608. }
  1609. }
  1610. //
  1611. // Copy the attributes
  1612. //
  1613. Dirent->Attributes = Attributes;
  1614. //
  1615. // Set the magic bit here, to tell dirctrl.c that this name is really
  1616. // lowercase.
  1617. //
  1618. Dirent->NtByte = 0;
  1619. if (ComponentReallyLowercase) {
  1620. SetFlag( Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE );
  1621. }
  1622. if (ExtensionReallyLowercase) {
  1623. SetFlag( Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE );
  1624. }
  1625. //
  1626. // See if we have to create an Lfn entry
  1627. //
  1628. if (ARGUMENT_PRESENT(Lfn)) {
  1629. UCHAR DirentChecksum;
  1630. UCHAR DirentsInLfn;
  1631. UCHAR LfnOrdinal;
  1632. PWCHAR LfnBuffer;
  1633. PLFN_DIRENT LfnDirent;
  1634. ASSERT( FatData.ChicagoMode );
  1635. DirentChecksum = FatComputeLfnChecksum( Dirent );
  1636. LfnOrdinal =
  1637. DirentsInLfn = FAT_LFN_DIRENTS_NEEDED(Lfn);
  1638. LfnBuffer = &Lfn->Buffer[(DirentsInLfn - 1) * 13];
  1639. ASSERT( DirentsInLfn <= MAX_LFN_DIRENTS );
  1640. for (LfnDirent = (PLFN_DIRENT)Dirent - DirentsInLfn;
  1641. LfnDirent < (PLFN_DIRENT)Dirent;
  1642. LfnDirent += 1, LfnOrdinal -= 1, LfnBuffer -= 13) {
  1643. WCHAR FinalLfnBuffer[13];
  1644. PWCHAR Buffer;
  1645. //
  1646. // We need to special case the "final" dirent.
  1647. //
  1648. if (LfnOrdinal == DirentsInLfn) {
  1649. ULONG i;
  1650. ULONG RemainderChars;
  1651. RemainderChars = (Lfn->Length / sizeof(WCHAR)) % 13;
  1652. LfnDirent->Ordinal = LfnOrdinal | FAT_LAST_LONG_ENTRY;
  1653. if (RemainderChars != 0) {
  1654. RtlCopyMemory( FinalLfnBuffer,
  1655. LfnBuffer,
  1656. RemainderChars * sizeof(WCHAR) );
  1657. for (i = RemainderChars; i < 13; i++) {
  1658. //
  1659. // Figure out which character to use.
  1660. //
  1661. if (i == RemainderChars) {
  1662. FinalLfnBuffer[i] = 0x0000;
  1663. } else {
  1664. FinalLfnBuffer[i] = 0xffff;
  1665. }
  1666. }
  1667. Buffer = FinalLfnBuffer;
  1668. } else {
  1669. Buffer = LfnBuffer;
  1670. }
  1671. } else {
  1672. LfnDirent->Ordinal = LfnOrdinal;
  1673. Buffer = LfnBuffer;
  1674. }
  1675. //
  1676. // Now fill in the name.
  1677. //
  1678. RtlCopyMemory( &LfnDirent->Name1[0],
  1679. &Buffer[0],
  1680. 5 * sizeof(WCHAR) );
  1681. RtlCopyMemory( &LfnDirent->Name2[0],
  1682. &Buffer[5],
  1683. 6 * sizeof(WCHAR) );
  1684. RtlCopyMemory( &LfnDirent->Name3[0],
  1685. &Buffer[11],
  1686. 2 * sizeof(WCHAR) );
  1687. //
  1688. // And the other fields
  1689. //
  1690. LfnDirent->Attributes = FAT_DIRENT_ATTR_LFN;
  1691. LfnDirent->Type = 0;
  1692. LfnDirent->Checksum = DirentChecksum;
  1693. LfnDirent->MustBeZero = 0;
  1694. }
  1695. }
  1696. DebugTrace(-1, Dbg, "FatConstructDirent -> (VOID)\n", 0);
  1697. return;
  1698. }
  1699. VOID
  1700. FatConstructLabelDirent (
  1701. IN PIRP_CONTEXT IrpContext,
  1702. IN OUT PDIRENT Dirent,
  1703. IN POEM_STRING Label
  1704. )
  1705. /*++
  1706. Routine Description:
  1707. This routine modifies the fields of a dirent to be used for a label.
  1708. Arguments:
  1709. Dirent - Supplies the dirent being modified.
  1710. Label - Supplies the name to store in the Dirent. This
  1711. name must not contain wildcards.
  1712. Return Value:
  1713. None.
  1714. --*/
  1715. {
  1716. PAGED_CODE();
  1717. DebugTrace(+1, Dbg, "FatConstructLabelDirent\n", 0);
  1718. DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent);
  1719. DebugTrace( 0, Dbg, " Label = %Z\n", Label);
  1720. RtlZeroMemory( Dirent, sizeof(DIRENT) );
  1721. //
  1722. // We just merrily go and fill up the dirent with the fields given.
  1723. //
  1724. RtlCopyMemory( Dirent->FileName, Label->Buffer, Label->Length );
  1725. //
  1726. // Pad the label with spaces, not nulls.
  1727. //
  1728. RtlFillMemory( &Dirent->FileName[Label->Length], 11 - Label->Length, ' ');
  1729. Dirent->LastWriteTime = FatGetCurrentFatTime( IrpContext );
  1730. Dirent->Attributes = FAT_DIRENT_ATTR_VOLUME_ID;
  1731. Dirent->ExtendedAttributes = 0;
  1732. Dirent->FileSize = 0;
  1733. DebugTrace(-1, Dbg, "FatConstructLabelDirent -> (VOID)\n", 0);
  1734. return;
  1735. }
  1736. VOID
  1737. FatSetFileSizeInDirent (
  1738. IN PIRP_CONTEXT IrpContext,
  1739. IN PFCB Fcb,
  1740. IN PULONG AlternativeFileSize OPTIONAL
  1741. )
  1742. /*++
  1743. Routine Description:
  1744. This routine saves the file size in an fcb into its dirent.
  1745. Arguments:
  1746. Fcb - Supplies the Fcb being referenced
  1747. AlternativeFileSize - If non-null we use the ULONG it points to as
  1748. the new file size. Otherwise we use the one in the Fcb.
  1749. Return Value:
  1750. None.
  1751. --*/
  1752. {
  1753. PDIRENT Dirent;
  1754. PBCB DirentBcb;
  1755. PAGED_CODE();
  1756. ASSERT( Fcb->FcbCondition == FcbGood );
  1757. FatGetDirentFromFcbOrDcb( IrpContext,
  1758. Fcb,
  1759. &Dirent,
  1760. &DirentBcb );
  1761. ASSERT( Dirent && DirentBcb );
  1762. try {
  1763. Dirent->FileSize = ARGUMENT_PRESENT( AlternativeFileSize ) ?
  1764. *AlternativeFileSize : Fcb->Header.FileSize.LowPart;
  1765. FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
  1766. } finally {
  1767. FatUnpinBcb( IrpContext, DirentBcb );
  1768. }
  1769. }
  1770. VOID
  1771. FatUpdateDirentFromFcb (
  1772. IN PIRP_CONTEXT IrpContext,
  1773. IN PFILE_OBJECT FileObject,
  1774. IN PFCB FcbOrDcb,
  1775. IN PCCB Ccb
  1776. )
  1777. /*++
  1778. Routine Description:
  1779. This routine modifies an objects directory entry based on the hints
  1780. that have been built up over previous operations on a handle. Notify
  1781. change filters are built and fired as a result of these updates.
  1782. Arguments:
  1783. FileObject - Fileobject representing the handle involved
  1784. FcbOrDcb - File/Dir involved
  1785. Ccb - User context involved
  1786. Return Value:
  1787. None.
  1788. --*/
  1789. {
  1790. BOOLEAN SetArchiveBit;
  1791. BOOLEAN UpdateFileSize;
  1792. BOOLEAN UpdateLastWriteTime;
  1793. BOOLEAN UpdateLastAccessTime;
  1794. PDIRENT Dirent;
  1795. PBCB DirentBcb = NULL;
  1796. ULONG NotifyFilter = 0;
  1797. FAT_TIME_STAMP CurrentFatTime;
  1798. LARGE_INTEGER CurrentTime;
  1799. LARGE_INTEGER CurrentDay;
  1800. LARGE_INTEGER LastAccessDay;
  1801. PAGED_CODE();
  1802. //
  1803. // Nothing to do if the fcb is bad, volume is readonly or we got the
  1804. // root dir.
  1805. //
  1806. if (FcbOrDcb->FcbCondition != FcbGood ||
  1807. NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB ||
  1808. FlagOn(FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
  1809. return;
  1810. }
  1811. //
  1812. // Check if we should be changing the time or file size and set
  1813. // the archive bit on the file.
  1814. //
  1815. KeQuerySystemTime( &CurrentTime );
  1816. //
  1817. // Note that we HAVE to use BooleanFlagOn() here because
  1818. // FO_FILE_SIZE_CHANGED > 0x80 (i.e., not in the first byte).
  1819. //
  1820. SetArchiveBit = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED);
  1821. UpdateLastWriteTime = FlagOn(FileObject->Flags, FO_FILE_MODIFIED) &&
  1822. !FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE);
  1823. UpdateFileSize = NodeType(FcbOrDcb) == FAT_NTC_FCB &&
  1824. BooleanFlagOn(FileObject->Flags, FO_FILE_SIZE_CHANGED);
  1825. //
  1826. // Do one further check here of access time. Only update it if
  1827. // the current version is at least one day old. We know that
  1828. // the current FcbOrDcb->LastAccessTime corresponds to 12 midnight local
  1829. // time, so just see if the current time is on the same day.
  1830. //
  1831. if (FatData.ChicagoMode &&
  1832. (UpdateLastWriteTime ||
  1833. FlagOn(FileObject->Flags, FO_FILE_FAST_IO_READ)) &&
  1834. !FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS)) {
  1835. ExSystemTimeToLocalTime( &FcbOrDcb->LastAccessTime, &LastAccessDay );
  1836. ExSystemTimeToLocalTime( &CurrentTime, &CurrentDay );
  1837. LastAccessDay.QuadPart /= FatOneDay.QuadPart;
  1838. CurrentDay.QuadPart /= FatOneDay.QuadPart;
  1839. if (LastAccessDay.LowPart != CurrentDay.LowPart) {
  1840. UpdateLastAccessTime = TRUE;
  1841. } else {
  1842. UpdateLastAccessTime = FALSE;
  1843. }
  1844. } else {
  1845. UpdateLastAccessTime = FALSE;
  1846. }
  1847. if (SetArchiveBit ||
  1848. UpdateFileSize ||
  1849. UpdateLastWriteTime ||
  1850. UpdateLastAccessTime) {
  1851. DebugTrace(0, Dbg, "Update Time and/or file size on File/Dir\n", 0);
  1852. try {
  1853. try {
  1854. //
  1855. // Get the dirent
  1856. //
  1857. FatGetDirentFromFcbOrDcb( IrpContext,
  1858. FcbOrDcb,
  1859. &Dirent,
  1860. &DirentBcb );
  1861. ASSERT( Dirent && DirentBcb );
  1862. if (UpdateLastWriteTime || UpdateLastAccessTime) {
  1863. (VOID)FatNtTimeToFatTime( IrpContext,
  1864. &CurrentTime,
  1865. TRUE,
  1866. &CurrentFatTime,
  1867. NULL );
  1868. }
  1869. if (SetArchiveBit) {
  1870. Dirent->Attributes |= FILE_ATTRIBUTE_ARCHIVE;
  1871. FcbOrDcb->DirentFatFlags |= FILE_ATTRIBUTE_ARCHIVE;
  1872. NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
  1873. }
  1874. if (UpdateLastWriteTime) {
  1875. //
  1876. // Update its time of last write
  1877. //
  1878. FcbOrDcb->LastWriteTime = CurrentTime;
  1879. Dirent->LastWriteTime = CurrentFatTime;
  1880. //
  1881. // We call the notify package to report that the
  1882. // last modification time has changed.
  1883. //
  1884. NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
  1885. }
  1886. if (UpdateLastAccessTime) {
  1887. //
  1888. // Now we have to truncate the local time down
  1889. // to the current day, then convert back to UTC.
  1890. //
  1891. FcbOrDcb->LastAccessTime.QuadPart =
  1892. CurrentDay.QuadPart * FatOneDay.QuadPart;
  1893. ExLocalTimeToSystemTime( &FcbOrDcb->LastAccessTime,
  1894. &FcbOrDcb->LastAccessTime );
  1895. Dirent->LastAccessDate = CurrentFatTime.Date;
  1896. //
  1897. // We call the notify package to report that the
  1898. // last access time has changed.
  1899. //
  1900. NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
  1901. }
  1902. if (UpdateFileSize) {
  1903. //
  1904. // Perhaps we were called to make certain that the
  1905. // filesize on disc was updated - don't bother updating
  1906. // and firing the filter if nothing changed.
  1907. //
  1908. ASSERT( NodeType(FcbOrDcb) == FAT_NTC_FCB );
  1909. if (Dirent->FileSize != FcbOrDcb->Header.FileSize.LowPart) {
  1910. //
  1911. // Update the dirent file size
  1912. //
  1913. Dirent->FileSize = FcbOrDcb->Header.FileSize.LowPart;
  1914. //
  1915. // We call the notify package to report that the
  1916. // size has changed.
  1917. //
  1918. NotifyFilter |= FILE_NOTIFY_CHANGE_SIZE;
  1919. }
  1920. }
  1921. FatNotifyReportChange( IrpContext,
  1922. FcbOrDcb->Vcb,
  1923. FcbOrDcb,
  1924. NotifyFilter,
  1925. FILE_ACTION_MODIFIED );
  1926. //
  1927. // If all we did was update last access time,
  1928. // don't mark the volume dirty.
  1929. //
  1930. FatSetDirtyBcb( IrpContext,
  1931. DirentBcb,
  1932. NotifyFilter == FILE_NOTIFY_CHANGE_LAST_ACCESS ?
  1933. NULL : FcbOrDcb->Vcb,
  1934. TRUE );
  1935. } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ?
  1936. EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
  1937. FatResetExceptionState( IrpContext );
  1938. }
  1939. } finally {
  1940. FatUnpinBcb( IrpContext, DirentBcb );
  1941. }
  1942. }
  1943. }
  1944. //
  1945. // Internal support routine
  1946. //
  1947. UCHAR
  1948. FatComputeLfnChecksum (
  1949. PDIRENT Dirent
  1950. )
  1951. /*++
  1952. Routine Description:
  1953. This routine computes the Chicago long file name checksum.
  1954. Arguments:
  1955. Dirent - Specifies the dirent that we are to compute a checksum for.
  1956. Return Value:
  1957. The checksum.
  1958. --*/
  1959. {
  1960. ULONG i;
  1961. UCHAR Checksum;
  1962. PAGED_CODE();
  1963. Checksum = Dirent->FileName[0];
  1964. for (i=1; i < 11; i++) {
  1965. Checksum = ((Checksum & 1) ? 0x80 : 0) +
  1966. (Checksum >> 1) +
  1967. Dirent->FileName[i];
  1968. }
  1969. return Checksum;
  1970. }
  1971. #if 0 // It turns out Win95 is still creating short names without a ~
  1972. //
  1973. // Internal support routine
  1974. //
  1975. BOOLEAN
  1976. FatIsLfnPairValid (
  1977. PWCHAR Lfn,
  1978. ULONG LfnSize,
  1979. PDIRENT Dirent
  1980. )
  1981. /*++
  1982. Routine Description:
  1983. This routine does a few more checks to make sure that a LFN/short
  1984. name pairing is legitimate. Basically this is the test:
  1985. Pairing is valid if:
  1986. DIRENT has a ~ character ||
  1987. (LFN is 8.3 compliant &&
  1988. (LFN has extended character(s) ? TRUE :
  1989. LFN upcases to DIRENT))
  1990. When checking for the presence of a tilda character in the short
  1991. name, note that we purposely do a single byte search instead of
  1992. converting the name to UNICODE and looking there for the tilda.
  1993. This protects us from accidently missing the tilda if the
  1994. preceding byte is a lead byte in the current Oem code page,
  1995. but wasn't in the Oem code page that created the file.
  1996. Also note that if the LFN is longer than 12 characters, then the
  1997. second clause of the OR must be false.
  1998. Arguments:
  1999. Lfn - Points to a buffer of UNICODE chars.
  2000. LfnSize - This is the size of the LFN in characters.
  2001. Dirent - Specifies the dirent we are to consider.
  2002. Return Value:
  2003. TRUE if the Lfn/DIRENT form a legitimate pair, FALSE otherwise.
  2004. --*/
  2005. {
  2006. ULONG i;
  2007. BOOLEAN ExtendedChars;
  2008. ULONG DirentBuffer[3];
  2009. PUCHAR DirentName;
  2010. ULONG DirentIndex;
  2011. BOOLEAN DotEncountered;
  2012. //
  2013. // First, look for a tilda
  2014. //
  2015. for (i=0; i<11; i++) {
  2016. if (Dirent->FileName[i] == '~') {
  2017. return TRUE;
  2018. }
  2019. }
  2020. //
  2021. // No tilda. If the LFN is longer than 12 characters, then it can
  2022. // neither upcase to the DIRENT nor be 8.3 complient.
  2023. //
  2024. if (LfnSize > 12) {
  2025. return FALSE;
  2026. }
  2027. //
  2028. // Now see if the name is 8.3, and build an upcased DIRENT as well.
  2029. //
  2030. DirentBuffer[0] = 0x20202020;
  2031. DirentBuffer[1] = 0x20202020;
  2032. DirentBuffer[2] = 0x20202020;
  2033. DirentName = (PUCHAR)DirentBuffer;
  2034. ExtendedChars = FALSE;
  2035. DirentIndex = 0;
  2036. DotEncountered = FALSE;
  2037. for (i=0; i < LfnSize; i++) {
  2038. //
  2039. // Do dot transition work
  2040. //
  2041. if (Lfn[i] == L'.') {
  2042. if (DotEncountered ||
  2043. (i > 8) ||
  2044. ((LfnSize - i) > 4) ||
  2045. (i && Lfn[i-1] == L' ')) {
  2046. return FALSE;
  2047. }
  2048. DotEncountered = TRUE;
  2049. DirentIndex = 8;
  2050. continue;
  2051. }
  2052. //
  2053. // The character must be legal in order to be 8.3
  2054. //
  2055. if ((Lfn[i] < 0x80) &&
  2056. !FsRtlIsAnsiCharacterLegalFat((UCHAR)Lfn[i], FALSE)) {
  2057. return FALSE;
  2058. }
  2059. //
  2060. // If the name contains no extended chars, continue building DIRENT
  2061. //
  2062. if (!ExtendedChars) {
  2063. if (Lfn[i] > 0x7f) {
  2064. ExtendedChars = TRUE;
  2065. } else {
  2066. DirentName[DirentIndex++] = (UCHAR) (
  2067. Lfn[i] < 'a' ? Lfn[i] : Lfn[i] <= 'z' ? Lfn[i] - ('a' - 'A') : Lfn[i]);
  2068. }
  2069. }
  2070. }
  2071. //
  2072. // If the LFN ended in a space, or there was no dot and the name
  2073. // has more than 8 characters, then it is not 8.3 compliant.
  2074. //
  2075. if ((Lfn[LfnSize - 1] == L' ') ||
  2076. (!DotEncountered && (LfnSize > 8))) {
  2077. return FALSE;
  2078. }
  2079. //
  2080. // OK, now if we got this far then the LFN is 8dot3. If there are
  2081. // no extended characters, then we can also check to make sure that
  2082. // the LFN is only a case varient of the DIRENT.
  2083. //
  2084. if (!ExtendedChars &&
  2085. !RtlEqualMemory(Dirent->FileName, DirentName, 11)) {
  2086. return FALSE;
  2087. }
  2088. //
  2089. // We have now verified this pairing the very best we can without
  2090. // knowledge of the code page that the file was created under.
  2091. //
  2092. return TRUE;
  2093. }
  2094. #endif //0
  2095. //
  2096. // Internal support routine
  2097. //
  2098. VOID
  2099. FatRescanDirectory (
  2100. PIRP_CONTEXT IrpContext,
  2101. PDCB Dcb
  2102. )
  2103. /*++
  2104. Routine Description:
  2105. This routine rescans the given directory, finding the first unused
  2106. dirent, first deleted dirent, and setting the free dirent bitmap
  2107. appropriately.
  2108. Arguments:
  2109. Dcb - Supplies the directory to rescan.
  2110. Return Value:
  2111. None.
  2112. --*/
  2113. {
  2114. PBCB Bcb = NULL;
  2115. PDIRENT Dirent;
  2116. NTSTATUS Status;
  2117. ULONG UnusedVbo;
  2118. ULONG DeletedHint;
  2119. ULONG DirentIndex;
  2120. ULONG DirentsThisRun;
  2121. ULONG StartIndexOfThisRun;
  2122. enum RunType {
  2123. InitialRun,
  2124. FreeDirents,
  2125. AllocatedDirents,
  2126. } CurrentRun;
  2127. PAGED_CODE();
  2128. DebugTrace( 0, Dbg, "We must scan the whole directory.\n", 0);
  2129. UnusedVbo = 0;
  2130. DeletedHint = 0xffffffff;
  2131. //
  2132. // To start with, we have to find out if the first dirent is free.
  2133. //
  2134. CurrentRun = InitialRun;
  2135. DirentIndex =
  2136. StartIndexOfThisRun = 0;
  2137. try {
  2138. while ( TRUE ) {
  2139. BOOLEAN DirentDeleted;
  2140. //
  2141. // Read a dirent
  2142. //
  2143. FatReadDirent( IrpContext,
  2144. Dcb,
  2145. UnusedVbo,
  2146. &Bcb,
  2147. &Dirent,
  2148. &Status );
  2149. //
  2150. // If EOF, or we found a NEVER_USED entry, we exit the loop
  2151. //
  2152. if ( (Status == STATUS_END_OF_FILE ) ||
  2153. (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) {
  2154. break;
  2155. }
  2156. //
  2157. // If the dirent is DELETED, and it is the first one we found, set
  2158. // it in the deleted hint.
  2159. //
  2160. if (Dirent->FileName[0] == FAT_DIRENT_DELETED) {
  2161. DirentDeleted = TRUE;
  2162. if (DeletedHint == 0xffffffff) {
  2163. DeletedHint = UnusedVbo;
  2164. }
  2165. } else {
  2166. DirentDeleted = FALSE;
  2167. }
  2168. //
  2169. // Check for the first time through the loop, and determine
  2170. // the current run type.
  2171. //
  2172. if (CurrentRun == InitialRun) {
  2173. CurrentRun = DirentDeleted ?
  2174. FreeDirents : AllocatedDirents;
  2175. } else {
  2176. //
  2177. // Are we switching from a free run to an allocated run?
  2178. //
  2179. if ((CurrentRun == FreeDirents) && !DirentDeleted) {
  2180. DirentsThisRun = DirentIndex - StartIndexOfThisRun;
  2181. RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  2182. StartIndexOfThisRun,
  2183. DirentsThisRun );
  2184. CurrentRun = AllocatedDirents;
  2185. StartIndexOfThisRun = DirentIndex;
  2186. }
  2187. //
  2188. // Are we switching from an allocated run to a free run?
  2189. //
  2190. if ((CurrentRun == AllocatedDirents) && DirentDeleted) {
  2191. DirentsThisRun = DirentIndex - StartIndexOfThisRun;
  2192. RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  2193. StartIndexOfThisRun,
  2194. DirentsThisRun );
  2195. CurrentRun = FreeDirents;
  2196. StartIndexOfThisRun = DirentIndex;
  2197. }
  2198. }
  2199. //
  2200. // Move on to the next dirent.
  2201. //
  2202. UnusedVbo += sizeof(DIRENT);
  2203. Dirent += 1;
  2204. DirentIndex += 1;
  2205. }
  2206. //
  2207. // Now we have to record the final run we encoutered
  2208. //
  2209. DirentsThisRun = DirentIndex - StartIndexOfThisRun;
  2210. if ((CurrentRun == FreeDirents) || (CurrentRun == InitialRun)) {
  2211. RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  2212. StartIndexOfThisRun,
  2213. DirentsThisRun );
  2214. } else {
  2215. RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  2216. StartIndexOfThisRun,
  2217. DirentsThisRun );
  2218. }
  2219. //
  2220. // Now if there we bailed prematurely out of the loop because
  2221. // we hit an unused entry, set all the rest as free.
  2222. //
  2223. if (UnusedVbo < Dcb->Header.AllocationSize.LowPart) {
  2224. StartIndexOfThisRun = UnusedVbo / sizeof(DIRENT);
  2225. DirentsThisRun = (Dcb->Header.AllocationSize.LowPart -
  2226. UnusedVbo) / sizeof(DIRENT);
  2227. RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  2228. StartIndexOfThisRun,
  2229. DirentsThisRun);
  2230. }
  2231. } finally {
  2232. FatUnpinBcb( IrpContext, Bcb );
  2233. }
  2234. //
  2235. // If there weren't any DELETED entries, set the index to our current
  2236. // position.
  2237. //
  2238. if (DeletedHint == 0xffffffff) { DeletedHint = UnusedVbo; }
  2239. Dcb->Specific.Dcb.UnusedDirentVbo = UnusedVbo;
  2240. Dcb->Specific.Dcb.DeletedDirentHint = DeletedHint;
  2241. return;
  2242. }
  2243. //
  2244. // Internal support routine
  2245. //
  2246. ULONG
  2247. FatDefragDirectory (
  2248. IN PIRP_CONTEXT IrpContext,
  2249. IN PDCB Dcb,
  2250. IN ULONG DirentsNeeded
  2251. )
  2252. /*++
  2253. Routine Description:
  2254. This routine determines if the requested number of dirents can be found
  2255. in the directory, looking for deleted dirents and orphaned LFNs. If the
  2256. request can be satisifed, orphaned LFNs are marked as deleted, and deleted
  2257. dirents are all grouped together at the end of the directory.
  2258. Note that this routine is currently used only on the root directory, but
  2259. it is completely general and could be used on any directory.
  2260. Arguments:
  2261. Dcb - Supplies the directory to defrag.
  2262. Return Value:
  2263. The Index of the first dirent available for use, or -1 if the
  2264. request cannot be satisfied.
  2265. --*/
  2266. {
  2267. ULONG SavedIrpContextFlag;
  2268. PLIST_ENTRY Links;
  2269. ULONG ReturnValue;
  2270. PFCB Fcb;
  2271. PBCB Bcb = NULL;
  2272. PDIRENT Dirent = NULL;
  2273. UNICODE_STRING Lfn = {0,0,NULL};
  2274. LARGE_MCB Mcb;
  2275. BOOLEAN McbInitialized = FALSE;
  2276. BOOLEAN InvalidateFcbs = FALSE;
  2277. PUCHAR Directory;
  2278. PUCHAR UnusedDirents;
  2279. PUCHAR UnusedDirentBuffer = NULL;
  2280. PUCHAR UsedDirents;
  2281. PUCHAR UsedDirentBuffer = NULL;
  2282. PBCB *Bcbs = NULL;
  2283. ULONG Page;
  2284. ULONG PagesPinned;
  2285. ULONG DcbSize;
  2286. ULONG TotalBytesAllocated = 0;
  2287. PAGED_CODE();
  2288. //
  2289. // We assume we own the Vcb.
  2290. //
  2291. ASSERT( FatVcbAcquiredExclusive(IrpContext, Dcb->Vcb) );
  2292. //
  2293. // We will only attempt this on directories less than 0x40000 bytes
  2294. // long (by default on DOS the root directory is only 0x2000 long).
  2295. // This is to avoid a cache manager complication.
  2296. //
  2297. DcbSize = Dcb->Header.AllocationSize.LowPart;
  2298. if (DcbSize > 0x40000) {
  2299. return (ULONG)-1;
  2300. }
  2301. //
  2302. // Force wait to TRUE
  2303. //
  2304. SavedIrpContextFlag = IrpContext->Flags;
  2305. SetFlag( IrpContext->Flags,
  2306. IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_WRITE_THROUGH );
  2307. //
  2308. // Now acquire all open Fcbs in the Dcb exclusive.
  2309. //
  2310. for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
  2311. Links != &Dcb->Specific.Dcb.ParentDcbQueue;
  2312. Links = Links->Flink) {
  2313. Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
  2314. (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.Resource, TRUE );
  2315. }
  2316. try {
  2317. CCB Ccb;
  2318. ULONG QueryOffset = 0;
  2319. ULONG FoundOffset = 0;
  2320. ULONGLONG BytesUsed = 0;
  2321. NTSTATUS DontCare;
  2322. ULONG Run;
  2323. ULONG TotalRuns;
  2324. BOOLEAN Result;
  2325. PUCHAR Char;
  2326. //
  2327. // We are going to build a new bitmap that will show all orphaned
  2328. // LFNs as well as deleted dirents as available.
  2329. //
  2330. // Initialize our local CCB that will match all files and even
  2331. // a label if it is here.
  2332. //
  2333. RtlZeroMemory( &Ccb, sizeof(CCB) );
  2334. Ccb.Flags = CCB_FLAG_MATCH_ALL | CCB_FLAG_MATCH_VOLUME_ID;
  2335. //
  2336. // Init the Long File Name string.
  2337. //
  2338. Lfn.MaximumLength = 260 * sizeof(WCHAR);
  2339. Lfn.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
  2340. 260*sizeof(WCHAR),
  2341. TAG_FILENAME_BUFFER );
  2342. //
  2343. // Initalize the Mcb. We use this structure to keep track of runs
  2344. // of free and allocated dirents. Runs are identity allocations, and
  2345. // holes are free dirents.
  2346. //
  2347. FsRtlInitializeLargeMcb( &Mcb, PagedPool );
  2348. McbInitialized = TRUE;
  2349. do {
  2350. FatLocateDirent( IrpContext,
  2351. Dcb,
  2352. &Ccb,
  2353. QueryOffset,
  2354. &Dirent,
  2355. &Bcb,
  2356. &FoundOffset,
  2357. NULL,
  2358. &Lfn);
  2359. if (Dirent != NULL) {
  2360. ULONG LfnByteOffset;
  2361. //
  2362. // Compute the LfnByteOffset.
  2363. //
  2364. LfnByteOffset = FoundOffset -
  2365. FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT);
  2366. BytesUsed = FoundOffset - LfnByteOffset + sizeof(DIRENT);
  2367. //
  2368. // Set a run to represent all the dirents used for this
  2369. // file in the Dcb dir.
  2370. //
  2371. Result = FsRtlAddLargeMcbEntry( &Mcb,
  2372. LfnByteOffset,
  2373. LfnByteOffset,
  2374. BytesUsed );
  2375. ASSERT( Result );
  2376. //
  2377. // Move on to the next dirent.
  2378. //
  2379. TotalBytesAllocated += (ULONG) BytesUsed;
  2380. QueryOffset = FoundOffset + sizeof(DIRENT);
  2381. }
  2382. } while ((Dirent != NULL) && (QueryOffset < DcbSize));
  2383. if (Bcb != NULL) {
  2384. FatUnpinBcb( IrpContext, Bcb );
  2385. }
  2386. //
  2387. // If we need more dirents than are available, bail.
  2388. //
  2389. if (DirentsNeeded > (DcbSize - TotalBytesAllocated)/sizeof(DIRENT)) {
  2390. try_return(ReturnValue = (ULONG)-1);
  2391. }
  2392. //
  2393. // Now we are going to copy all the used and un-used parts of the
  2394. // directory to separate pool.
  2395. //
  2396. // Allocate these buffers and pin the entire directory.
  2397. //
  2398. UnusedDirents =
  2399. UnusedDirentBuffer = FsRtlAllocatePoolWithTag( PagedPool,
  2400. DcbSize - TotalBytesAllocated,
  2401. TAG_DIRENT );
  2402. UsedDirents =
  2403. UsedDirentBuffer = FsRtlAllocatePoolWithTag( PagedPool,
  2404. TotalBytesAllocated,
  2405. TAG_DIRENT );
  2406. PagesPinned = (DcbSize + (PAGE_SIZE - 1 )) / PAGE_SIZE;
  2407. Bcbs = FsRtlAllocatePoolWithTag( PagedPool,
  2408. PagesPinned * sizeof(PBCB),
  2409. TAG_BCB );
  2410. RtlZeroMemory( Bcbs, PagesPinned * sizeof(PBCB) );
  2411. for (Page = 0; Page < PagesPinned; Page += 1) {
  2412. ULONG PinSize;
  2413. //
  2414. // Don't try to pin beyond the Dcb size.
  2415. //
  2416. if ((Page + 1) * PAGE_SIZE > DcbSize) {
  2417. PinSize = DcbSize - (Page * PAGE_SIZE);
  2418. } else {
  2419. PinSize = PAGE_SIZE;
  2420. }
  2421. FatPrepareWriteDirectoryFile( IrpContext,
  2422. Dcb,
  2423. Page * PAGE_SIZE,
  2424. PinSize,
  2425. &Bcbs[Page],
  2426. &Dirent,
  2427. FALSE,
  2428. TRUE,
  2429. &DontCare );
  2430. if (Page == 0) {
  2431. Directory = (PUCHAR)Dirent;
  2432. }
  2433. }
  2434. TotalRuns = FsRtlNumberOfRunsInLargeMcb( &Mcb );
  2435. for (Run = 0; Run < TotalRuns; Run++) {
  2436. LBO Vbo;
  2437. LBO Lbo;
  2438. Result = FsRtlGetNextLargeMcbEntry( &Mcb,
  2439. Run,
  2440. &Vbo,
  2441. &Lbo,
  2442. &BytesUsed );
  2443. ASSERT(Result);
  2444. //
  2445. // Copy each run to their specific pool.
  2446. //
  2447. if (Lbo != -1) {
  2448. RtlCopyMemory( UsedDirents,
  2449. Directory + Vbo,
  2450. (ULONG) BytesUsed );
  2451. UsedDirents += BytesUsed;
  2452. } else {
  2453. RtlCopyMemory( UnusedDirents,
  2454. Directory + Vbo,
  2455. (ULONG) BytesUsed );
  2456. UnusedDirents += BytesUsed;
  2457. }
  2458. }
  2459. //
  2460. // Marking all the un-used dirents as "deleted". This will reclaim
  2461. // storage used by orphaned LFNs.
  2462. //
  2463. for (Char = UnusedDirentBuffer; Char < UnusedDirents; Char += sizeof(DIRENT)) {
  2464. *Char = FAT_DIRENT_DELETED;
  2465. }
  2466. //
  2467. // Now, for the permanent step. Copy the two pool buffer back to the
  2468. // real Dcb directory, and flush the Dcb directory
  2469. //
  2470. ASSERT( TotalBytesAllocated == (ULONG)(UsedDirents - UsedDirentBuffer) );
  2471. RtlCopyMemory( Directory, UsedDirentBuffer, TotalBytesAllocated );
  2472. RtlCopyMemory( Directory + TotalBytesAllocated,
  2473. UnusedDirentBuffer,
  2474. UnusedDirents - UnusedDirentBuffer );
  2475. //
  2476. // We need to unpin here so that the UnpinRepinned won't deadlock.
  2477. //
  2478. if (Bcbs) {
  2479. for (Page = 0; Page < PagesPinned; Page += 1) {
  2480. FatUnpinBcb( IrpContext, Bcbs[Page] );
  2481. }
  2482. ExFreePool(Bcbs);
  2483. Bcbs = NULL;
  2484. }
  2485. //
  2486. // Now make the free dirent bitmap reflect the new state of the Dcb
  2487. // directory.
  2488. //
  2489. RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  2490. 0,
  2491. TotalBytesAllocated / sizeof(DIRENT) );
  2492. RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  2493. TotalBytesAllocated / sizeof(DIRENT),
  2494. (DcbSize - TotalBytesAllocated) / sizeof(DIRENT) );
  2495. ReturnValue = TotalBytesAllocated / sizeof(DIRENT);
  2496. //
  2497. // Flush the directory to disk. If we raise, we will need to invalidate
  2498. // all of the children. Sorry, guys, but I can't figure out where you are
  2499. // now - if this failed I probably can't read the media either. And we
  2500. // probably purged the cache to boot.
  2501. //
  2502. try {
  2503. FatUnpinRepinnedBcbs( IrpContext );
  2504. } except(FsRtlIsNtstatusExpected(GetExceptionCode()) ?
  2505. EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
  2506. InvalidateFcbs = TRUE;
  2507. }
  2508. //
  2509. // OK, now nothing can go wrong. We have two more things to do.
  2510. // First, we have to fix up all the dirent offsets in any open Fcbs.
  2511. // If we cannot now find the Fcb, the file is marked invalid. Also,
  2512. // we skip deleted files.
  2513. //
  2514. for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
  2515. Links != &Dcb->Specific.Dcb.ParentDcbQueue;
  2516. Links = Links->Flink) {
  2517. PBCB TmpBcb = NULL;
  2518. ULONG TmpOffset;
  2519. PDIRENT TmpDirent = NULL;
  2520. ULONG PreviousLfnSpread;
  2521. Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
  2522. if (IsFileDeleted( IrpContext, Fcb )) {
  2523. continue;
  2524. }
  2525. //
  2526. // If we aren't already giving up, safely try to pick up the dirent
  2527. // to update the Fcb. If this raises, we have to give up and blow
  2528. // evenyone else away too.
  2529. //
  2530. if (!InvalidateFcbs) {
  2531. try {
  2532. FatLocateSimpleOemDirent( IrpContext,
  2533. Dcb,
  2534. &Fcb->ShortName.Name.Oem,
  2535. &TmpDirent,
  2536. &TmpBcb,
  2537. &TmpOffset );
  2538. } except(FsRtlIsNtstatusExpected(GetExceptionCode()) ?
  2539. EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
  2540. InvalidateFcbs = TRUE;
  2541. }
  2542. }
  2543. if (TmpBcb == NULL || InvalidateFcbs) {
  2544. FatUnpinBcb( IrpContext, TmpBcb );
  2545. FatMarkFcbCondition( IrpContext, Fcb, FcbBad, TRUE );
  2546. } else {
  2547. FatUnpinBcb( IrpContext, TmpBcb );
  2548. PreviousLfnSpread = Fcb->DirentOffsetWithinDirectory -
  2549. Fcb->LfnOffsetWithinDirectory;
  2550. Fcb->DirentOffsetWithinDirectory = TmpOffset;
  2551. Fcb->LfnOffsetWithinDirectory = TmpOffset - PreviousLfnSpread;
  2552. }
  2553. }
  2554. try_exit: NOTHING;
  2555. } finally {
  2556. //
  2557. // Free all our resources and stuff.
  2558. //
  2559. if (McbInitialized) {
  2560. FsRtlUninitializeLargeMcb( &Mcb );
  2561. }
  2562. if (Lfn.Buffer) {
  2563. ExFreePool( Lfn.Buffer );
  2564. }
  2565. if (UnusedDirentBuffer) {
  2566. ExFreePool( UnusedDirentBuffer );
  2567. }
  2568. if (UsedDirentBuffer) {
  2569. ExFreePool( UsedDirentBuffer );
  2570. }
  2571. if (Bcbs) {
  2572. for (Page = 0; Page < PagesPinned; Page += 1) {
  2573. FatUnpinBcb( IrpContext, Bcbs[Page] );
  2574. }
  2575. ExFreePool(Bcbs);
  2576. }
  2577. FatUnpinBcb( IrpContext, Bcb );
  2578. for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
  2579. Links != &Dcb->Specific.Dcb.ParentDcbQueue;
  2580. Links = Links->Flink) {
  2581. Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
  2582. ExReleaseResourceLite( Fcb->Header.Resource );
  2583. }
  2584. IrpContext->Flags = SavedIrpContextFlag;
  2585. }
  2586. //
  2587. // Now return the offset of the first free dirent to the caller.
  2588. //
  2589. return ReturnValue;
  2590. }