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.

2798 lines
75 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. DirSup.c
  5. Abstract:
  6. This module implements the dirent support routines for Rx.
  7. Author:
  8. DavidGoebel [DavidGoe] 08-Nov-90
  9. --*/
  10. // ----------------------joejoe-----------found-------------#include "RxProcs.h"
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. //
  14. // The Bug check file id for this module
  15. //
  16. #define BugCheckFileId (RDBSS_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. // RxConstructDot (
  27. // IN PRX_CONTEXT RxContext,
  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. #define RxConstructDot(RXCONTEXT,DCB,PARENT,DIRENT) { \
  36. \
  37. RtlCopyMemory( (PUCHAR)(DIRENT), ". ", 11 ); \
  38. (DIRENT)->Attributes = RDBSS_DIRENT_ATTR_DIRECTORY; \
  39. (DIRENT)->LastWriteTime = (PARENT)->LastWriteTime; \
  40. if (RxData.ChicagoMode) { \
  41. (DIRENT)->CreationTime = (PARENT)->CreationTime; \
  42. (DIRENT)->CreationMSec = (PARENT)->CreationMSec; \
  43. (DIRENT)->LastAccessDate = (PARENT)->LastAccessDate; \
  44. } \
  45. (DIRENT)->FirstClusterOfFile = (RDBSS_ENTRY)(DCB)->FirstClusterOfFile; \
  46. }
  47. //
  48. // VOID
  49. // RxConstructDotDot (
  50. // IN PRX_CONTEXT RxContext,
  51. // IN PDCB Directory,
  52. // IN PDIRENT ParentDirent,
  53. // IN OUT PDIRENT Dirent
  54. // );
  55. //
  56. // The following macro is called to initalize the ".." dirent.
  57. //
  58. #define RxConstructDotDot(RXCONTEXT,DCB,PARENT,DIRENT) { \
  59. \
  60. RtlCopyMemory( (PUCHAR)(DIRENT), ".. ", 11 ); \
  61. (DIRENT)->Attributes = RDBSS_DIRENT_ATTR_DIRECTORY; \
  62. (DIRENT)->LastWriteTime = (PARENT)->LastWriteTime; \
  63. if (RxData.ChicagoMode) { \
  64. (DIRENT)->CreationTime = (PARENT)->CreationTime; \
  65. (DIRENT)->CreationMSec = (PARENT)->CreationMSec; \
  66. (DIRENT)->LastAccessDate = (PARENT)->LastAccessDate; \
  67. } \
  68. (DIRENT)->FirstClusterOfFile = (RDBSS_ENTRY) ( \
  69. NodeType((DCB)->ParentDcb) == RDBSS_NTC_ROOT_DCB ? \
  70. 0 : (DCB)->ParentDcb->FirstClusterOfFile); \
  71. }
  72. //
  73. // VOID
  74. // RxConstructEndDirent (
  75. // IN PRX_CONTEXT RxContext,
  76. // IN OUT PDIRENT Dirent
  77. // );
  78. //
  79. // The following macro created the end dirent. Note that since the
  80. // dirent was zeroed, the first byte of the name already contains 0x0,
  81. // so there is nothing to do.
  82. //
  83. #define RxConstructEndDirent(RXCONTEXT,DIRENT) NOTHING
  84. //
  85. // VOID
  86. // RxReadDirent (
  87. // IN PRX_CONTEXT RxContext,
  88. // IN PDCB Dcb,
  89. // IN VBO Vbo,
  90. // OUT PBCB *Bcb,
  91. // OUT PVOID *Dirent,
  92. // OUT PRXSTATUS Status
  93. // );
  94. //
  95. //
  96. // This macro reads in a page of dirents when we step onto a new page,
  97. // or this is the first itteration of a loop and Bcb is NULL.
  98. //
  99. #define RxReadDirent(RXCONTEXT,DCB,VBO,BCB,DIRENT,STATUS) \
  100. if ((VBO) >= (DCB)->Header.AllocationSize.LowPart) { \
  101. *(STATUS) = RxStatus(END_OF_FILE); \
  102. RxUnpinBcb( (RXCONTEXT), *(BCB) ); \
  103. } else if ( ((VBO) % PAGE_SIZE == 0) || (*(BCB) == NULL) ) { \
  104. RxUnpinBcb( (RXCONTEXT), *(BCB) ); \
  105. RxReadDirectoryFile( (RXCONTEXT), \
  106. (DCB), \
  107. (VBO) & ~(PAGE_SIZE - 1), \
  108. PAGE_SIZE, \
  109. FALSE, \
  110. (BCB), \
  111. (PVOID *)(DIRENT), \
  112. (STATUS) ); \
  113. *(DIRENT) = (PVOID)((PUCHAR)*(DIRENT) + ((VBO) % PAGE_SIZE)); \
  114. }
  115. //
  116. // Internal support routines
  117. //
  118. UCHAR
  119. RxComputeLfnChecksum (
  120. PDIRENT Dirent
  121. );
  122. VOID
  123. RxRescanDirectory (
  124. PRX_CONTEXT RxContext,
  125. PDCB Dcb
  126. );
  127. ULONG
  128. RxDefragDirectory (
  129. IN PRX_CONTEXT RxContext,
  130. IN PDCB Dcb,
  131. IN ULONG DirentsNeeded
  132. );
  133. #ifdef ALLOC_PRAGMA
  134. #pragma alloc_text(PAGE, RxConstructDirent)
  135. #pragma alloc_text(PAGE, RxConstructLabelDirent)
  136. #pragma alloc_text(PAGE, RxCreateNewDirent)
  137. #pragma alloc_text(PAGE, RxDeleteDirent)
  138. #pragma alloc_text(PAGE, RxGetDirentFromFcbOrDcb)
  139. #pragma alloc_text(PAGE, RxInitializeDirectoryDirent)
  140. #pragma alloc_text(PAGE, RxIsDirectoryEmpty)
  141. #pragma alloc_text(PAGE, RxLocateDirent)
  142. #pragma alloc_text(PAGE, RxLocateSimpleOemDirent)
  143. #pragma alloc_text(PAGE, RxLocateVolumeLabel)
  144. #pragma alloc_text(PAGE, RxSetFileSizeInDirent)
  145. #pragma alloc_text(PAGE, RxComputeLfnChecksum)
  146. #pragma alloc_text(PAGE, RxRescanDirectory)
  147. #pragma alloc_text(PSGE, RxDefragDirectory)
  148. #endif
  149. ULONG
  150. RxCreateNewDirent (
  151. IN PRX_CONTEXT RxContext,
  152. IN PDCB ParentDirectory,
  153. IN ULONG DirentsNeeded
  154. )
  155. /*++
  156. Routine Description:
  157. This routine allocates on the disk a new dirent inside of the
  158. parent directory. If a new dirent cannot be allocated (i.e.,
  159. because the disk is full or the root directory is full) then
  160. it raises the appropriate status. The dirent itself is
  161. neither initialized nor pinned by this procedure.
  162. Arguments:
  163. ParentDirectory - Supplies the DCB for the directory in which
  164. to create the new dirent
  165. DirentsNeeded - This is the number of continginous dirents required
  166. Return Value:
  167. ByteOffset - Returns the VBO within the Parent directory where
  168. the dirent has been allocated
  169. --*/
  170. {
  171. VBO UnusedVbo;
  172. VBO DeletedHint;
  173. ULONG ByteOffset;
  174. PBCB Bcb = NULL;
  175. PDIRENT Dirent;
  176. RXSTATUS Status;
  177. PAGED_CODE();
  178. DebugTrace(+1, Dbg, "RxCreateNewDirent\n", 0);
  179. DebugTrace( 0, Dbg, " ParentDirectory = %08lx\n", ParentDirectory);
  180. //
  181. // If UnusedDirentVbo is within our current file allocation then we
  182. // don't have to search through the directory at all; we know just
  183. // where to put it.
  184. //
  185. // If UnusedDirentVbo is beyond the current file allocation then
  186. // there are no more unused dirents in the current allocation, though
  187. // upon adding another cluster of allocation UnusedDirentVbo
  188. // will point to an unused dirent. Haveing found no unused dirents
  189. // we use the DeletedDirentHint to try and find a deleted dirent in
  190. // the current allocation. In this also runs off the end of the file,
  191. // we finally have to break down and allocate another sector. Note
  192. // that simply writing beyond the current allocation will automatically
  193. // do just this.
  194. //
  195. // We also must deal with the special case where UnusedDirentVbo and
  196. // DeletedDirentHint have yet to be initialized. In this case we must
  197. // first walk through the directory looking for the first deleted entry
  198. // first unused dirent. After this point we continue as before.
  199. // This virgin state is denoted by the special value of 0xffffffff.
  200. //
  201. UnusedVbo = ParentDirectory->Specific.Dcb.UnusedDirentVbo;
  202. DeletedHint = ParentDirectory->Specific.Dcb.DeletedDirentHint;
  203. //
  204. // Check for our first call to this routine with this Dcb. If so
  205. // we have to correctly set the two hints in the Dcb.
  206. //
  207. if (UnusedVbo == 0xffffffff) {
  208. RxRescanDirectory( RxContext, ParentDirectory );
  209. UnusedVbo = ParentDirectory->Specific.Dcb.UnusedDirentVbo;
  210. DeletedHint = ParentDirectory->Specific.Dcb.DeletedDirentHint;
  211. }
  212. //
  213. // Now we know that UnusedDirentVbo and DeletedDirentHint are correctly
  214. // set so we check if there is already an unused dirent in the the
  215. // current allocation. This is the easy case.
  216. //
  217. DebugTrace( 0, Dbg, " UnusedVbo = %08lx\n", UnusedVbo);
  218. DebugTrace( 0, Dbg, " DeletedHint = %08lx\n", DeletedHint);
  219. if ( UnusedVbo + (DirentsNeeded * sizeof(DIRENT)) <=
  220. ParentDirectory->Header.AllocationSize.LowPart ) {
  221. //
  222. // Get this unused dirent for the caller. We have a
  223. // sporting chance that we won't have to wait.
  224. //
  225. DebugTrace( 0, Dbg, "There is a never used entry.\n", 0);
  226. ByteOffset = UnusedVbo;
  227. UnusedVbo += DirentsNeeded * sizeof(DIRENT);
  228. } else {
  229. //
  230. // Life is tough. We have to march from the DeletedDirentHint
  231. // looking for a deleted dirent. If we get to EOF without finding
  232. // one, we will have to allocate a new cluster.
  233. //
  234. ByteOffset =
  235. RtlFindClearBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
  236. DirentsNeeded,
  237. DeletedHint / sizeof(DIRENT) );
  238. //
  239. // Do a quick check for a root directory allocation that failed
  240. // simply because of fragmentation. Also, only attempt to defrag
  241. // if the length is less that 0x40000. This is to avoid
  242. // complications arrising from crossing a cache manager VMCB block
  243. // (by default on DOS the root directory is only 0x2000 long).
  244. //
  245. if ((ByteOffset == -1) &&
  246. (NodeType(ParentDirectory) == RDBSS_NTC_ROOT_DCB) &&
  247. (ParentDirectory->Header.AllocationSize.LowPart <= 0x40000)) {
  248. ByteOffset = RxDefragDirectory( RxContext, ParentDirectory, DirentsNeeded );
  249. }
  250. if (ByteOffset != -1) {
  251. //
  252. // If we consuemed deleted dirents at Deleted Hint, update.
  253. // We also may have consumed some un-used dirents as well,
  254. // so be sure to check for that as well.
  255. //
  256. ByteOffset *= sizeof(DIRENT);
  257. if (ByteOffset == DeletedHint) {
  258. DeletedHint += DirentsNeeded * sizeof(DIRENT);
  259. }
  260. if (ByteOffset + DirentsNeeded * sizeof(DIRENT) > UnusedVbo) {
  261. UnusedVbo = ByteOffset + DirentsNeeded * sizeof(DIRENT);
  262. }
  263. } else {
  264. //
  265. // We are going to have to allocate another cluster. Do
  266. // so, update both the UnusedVbo and the DeletedHint and bail.
  267. //
  268. DebugTrace( 0, Dbg, "We have to allocate another cluster.\n", 0);
  269. //
  270. // Make sure we are not trying to expand the root directory.
  271. //
  272. if ( NodeType(ParentDirectory) == RDBSS_NTC_ROOT_DCB ) {
  273. DebugTrace(0, Dbg, "Disk Full. Raise Status.\n", 0);
  274. RxRaiseStatus( RxContext, RxStatus(DISK_FULL) );
  275. }
  276. //
  277. // Take the last dirent(s) in this cluster. We will allocate
  278. // another below.
  279. //
  280. ByteOffset = UnusedVbo;
  281. UnusedVbo += DirentsNeeded * sizeof(DIRENT);
  282. //
  283. // OK, touch the dirent now to cause the space to get allocated.
  284. //
  285. Bcb = NULL;
  286. try {
  287. ULONG Offset;
  288. ULONG ClusterSize;
  289. ClusterSize =
  290. 1 << ParentDirectory->Vcb->AllocationSupport.LogOfBytesPerCluster;
  291. Offset = UnusedVbo & ~(ClusterSize - 1);
  292. RxPrepareWriteDirectoryFile( RxContext,
  293. ParentDirectory,
  294. Offset,
  295. sizeof(DIRENT),
  296. &Bcb,
  297. &Dirent,
  298. FALSE,
  299. &Status );
  300. } finally {
  301. RxUnpinBcb( RxContext, Bcb );
  302. }
  303. }
  304. }
  305. //
  306. // If we are only requesting a single dirent, and we did not get the
  307. // first dirent in a directory, then check that the preceding dirent
  308. // is not an orphaned LFN. If it is, then mark it deleted. Thus
  309. // reducing the possibility of an accidental pairing.
  310. //
  311. // Only do this when we are in Chicago Mode.
  312. //
  313. Bcb = NULL;
  314. if (RxData.ChicagoMode &&
  315. (DirentsNeeded == 1) &&
  316. (ByteOffset > (NodeType(ParentDirectory) == RDBSS_NTC_ROOT_DCB ?
  317. 0 : 2 * sizeof(DIRENT)))) {
  318. try {
  319. RxReadDirent( RxContext,
  320. ParentDirectory,
  321. ByteOffset - sizeof(DIRENT),
  322. &Bcb,
  323. &Dirent,
  324. &Status );
  325. if ((Status != RxStatus(SUCCESS)) ||
  326. (Dirent->FileName[0] == RDBSS_DIRENT_NEVER_USED)) {
  327. RxPopUpFileCorrupt( RxContext, ParentDirectory );
  328. RxRaiseStatus( RxContext, RxStatus(FILE_CORRUPT_ERROR) );
  329. }
  330. if ((Dirent->Attributes == RDBSS_DIRENT_ATTR_LFN) &&
  331. (Dirent->FileName[0] != RDBSS_DIRENT_DELETED)) {
  332. //
  333. // Pin it, mark it, and set it dirty.
  334. //
  335. RxPinMappedData( RxContext,
  336. ParentDirectory,
  337. ByteOffset - sizeof(DIRENT),
  338. sizeof(DIRENT),
  339. &Bcb );
  340. Dirent->FileName[0] = RDBSS_DIRENT_DELETED;
  341. RxSetDirtyBcb( RxContext, Bcb, ParentDirectory->Vcb );
  342. ASSERT( RtlAreBitsSet( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
  343. (ByteOffset - sizeof(DIRENT))/ sizeof(DIRENT),
  344. DirentsNeeded ) );
  345. RtlClearBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
  346. (ByteOffset - sizeof(DIRENT))/ sizeof(DIRENT),
  347. DirentsNeeded );
  348. }
  349. } finally {
  350. RxUnpinBcb( RxContext, Bcb );
  351. }
  352. }
  353. //
  354. // Assert that the dirents are in fact unused
  355. //
  356. try {
  357. ULONG i;
  358. Bcb = NULL;
  359. for (i = 0; i < DirentsNeeded; i++) {
  360. RxReadDirent( RxContext,
  361. ParentDirectory,
  362. ByteOffset + i*sizeof(DIRENT),
  363. &Bcb,
  364. &Dirent,
  365. &Status );
  366. if ((Status != RxStatus(SUCCESS)) ||
  367. ((Dirent->FileName[0] != RDBSS_DIRENT_NEVER_USED) &&
  368. (Dirent->FileName[0] != RDBSS_DIRENT_DELETED))) {
  369. RxPopUpFileCorrupt( RxContext, ParentDirectory );
  370. RxRaiseStatus( RxContext, RxStatus(FILE_CORRUPT_ERROR) );
  371. }
  372. }
  373. } finally {
  374. RxUnpinBcb( RxContext, Bcb );
  375. }
  376. //
  377. // Set the Bits in the bitmap and move the Unused Dirent Vbo.
  378. //
  379. ASSERT( RtlAreBitsClear( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
  380. ByteOffset / sizeof(DIRENT),
  381. DirentsNeeded ) );
  382. RtlSetBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap,
  383. ByteOffset / sizeof(DIRENT),
  384. DirentsNeeded );
  385. //
  386. // Save the newly computed values in the Parent Directory Fcb
  387. //
  388. ParentDirectory->Specific.Dcb.UnusedDirentVbo = UnusedVbo;
  389. ParentDirectory->Specific.Dcb.DeletedDirentHint = DeletedHint;
  390. DebugTrace(-1, Dbg, "RxCreateNewDirent -> (VOID)\n", 0);
  391. return ByteOffset;
  392. }
  393. VOID
  394. RxInitializeDirectoryDirent (
  395. IN PRX_CONTEXT RxContext,
  396. IN PDCB Dcb,
  397. IN PDIRENT ParentDirent
  398. )
  399. /*++
  400. Routine Description:
  401. This routine converts a dirent into a directory on the disk. It does this
  402. setting the directory flag in the dirent, and by allocating the necessary
  403. space for the "." and ".." dirents and initializing them.
  404. If a new dirent cannot be allocated (i.e., because the disk is full) then
  405. it raises the appropriate status.
  406. Arguments:
  407. Dcb - Supplies the Dcb denoting the file that is to be made into a
  408. directory. This must be input a completely empty file with
  409. an allocation size of zero.
  410. ParentDirent - Provides the parent Dirent for a time-stamp model.
  411. Return Value:
  412. None.
  413. --*/
  414. {
  415. PBCB Bcb;
  416. PVOID Buffer;
  417. RXSTATUS DontCare;
  418. PAGED_CODE();
  419. DebugTrace(+1, Dbg, "RxInitializeDirectoryDirent\n", 0);
  420. DebugTrace( 0, Dbg, " Dcb = %08lx\n", Dcb);
  421. //
  422. // Assert that we are not attempting this on the root directory.
  423. //
  424. ASSERT( NodeType(Dcb) != RDBSS_NTC_ROOT_DCB );
  425. //
  426. // Assert that this is only attempted on newly created directories.
  427. //
  428. ASSERT( Dcb->Header.AllocationSize.LowPart == 0 );
  429. //
  430. // Prepare the directory file for writing. Note that we can use a single
  431. // Bcb for these two entries because we know they are the first two in
  432. // the directory, and thus together do not span a page boundry. Also
  433. // note that we prepare write 2 entries: one for "." and one for "..".
  434. // The end of directory marker is automatically set since the whole
  435. // directory is initially zero (DIRENT_NEVER_USED).
  436. //
  437. RxPrepareWriteDirectoryFile( RxContext,
  438. Dcb,
  439. 0,
  440. 2 * sizeof(DIRENT),
  441. &Bcb,
  442. &Buffer,
  443. FALSE,
  444. &DontCare );
  445. ASSERT( NT_SUCCESS( DontCare ));
  446. //
  447. // Add the . and .. entries
  448. //
  449. RxConstructDot( RxContext, Dcb, ParentDirent, (PDIRENT)Buffer + 0);
  450. RxConstructDotDot( RxContext, Dcb, ParentDirent, (PDIRENT)Buffer + 1);
  451. //
  452. // Unpin the buffer and return to the caller.
  453. //
  454. RxUnpinBcb( RxContext, Bcb );
  455. DebugTrace(-1, Dbg, "RxInitializeDirectoryDirent -> (VOID)\n", 0);
  456. return;
  457. }
  458. VOID
  459. RxDeleteDirent (
  460. IN PRX_CONTEXT RxContext,
  461. IN PFCB FcbOrDcb,
  462. IN PDELETE_CONTEXT DeleteContext OPTIONAL,
  463. IN BOOLEAN DeleteEa
  464. )
  465. /*++
  466. Routine Description:
  467. This routine Deletes on the disk the indicated dirent. It does
  468. this by marking the dirent as deleted.
  469. Arguments:
  470. FcbOrDcb - Supplies the FCB/DCB for the file/directory being
  471. deleted. For a file the file size and allocation must be zero.
  472. (Zero allocation is implied by a zero cluster index).
  473. For a directory the allocation must be zero.
  474. DeleteContext - This variable, if speicified, may be used to preserve
  475. the file size and first cluster of file information in the dirent
  476. fot the benefit of unerase utilities.
  477. DeleteEa - Tells us whether to delete the EA and whether to check
  478. for no allocation/ Mainly TRUE. FALSE passed in from rename.
  479. Return Value:
  480. None.
  481. --*/
  482. {
  483. PBCB Bcb = NULL;
  484. PDIRENT Dirent;
  485. RXSTATUS DontCare;
  486. ULONG Offset;
  487. ULONG DirentsToDelete;
  488. PAGED_CODE();
  489. DebugTrace(+1, Dbg, "RxDeleteDirent\n", 0);
  490. DebugTrace( 0, Dbg, " FcbOrDcb = %08lx\n", FcbOrDcb);
  491. //
  492. // Assert that we are not attempting this on the root directory.
  493. //
  494. ASSERT( NodeType(FcbOrDcb) != RDBSS_NTC_ROOT_DCB );
  495. //
  496. // Make sure all requests have zero allocation/file size
  497. //
  498. if (DeleteEa &&
  499. ((FcbOrDcb->Header.AllocationSize.LowPart != 0) ||
  500. ((NodeType(FcbOrDcb) == RDBSS_NTC_FCB) &&
  501. (FcbOrDcb->Header.FileSize.LowPart != 0)))) {
  502. DebugTrace( 0, Dbg, "Called with non zero allocation/file size.\n", 0);
  503. RxBugCheck( 0, 0, 0 );
  504. }
  505. //
  506. // Now, mark the dirents deleted, unpin the Bcb, and return to the caller.
  507. // Assert that there isn't any allocation associated with this dirent.
  508. //
  509. // Note that this loop will end with Dirent pointing to the short name.
  510. //
  511. for ( Offset = FcbOrDcb->LfnOffsetWithinDirectory;
  512. Offset <= FcbOrDcb->DirentOffsetWithinDirectory;
  513. Offset += sizeof(DIRENT), Dirent += 1 ) {
  514. //
  515. // If we stepped onto a new page, or this is the first itteration,
  516. // unpin the old page, and pin the new one.
  517. //
  518. if ((Offset == FcbOrDcb->LfnOffsetWithinDirectory) ||
  519. ((Offset & (PAGE_SIZE - 1)) == 0)) {
  520. RxUnpinBcb( RxContext, Bcb );
  521. RxPrepareWriteDirectoryFile( RxContext,
  522. FcbOrDcb->ParentDcb,
  523. Offset,
  524. sizeof(DIRENT),
  525. &Bcb,
  526. (PVOID *)&Dirent,
  527. FALSE,
  528. &DontCare );
  529. }
  530. ASSERT( (Dirent->FirstClusterOfFile == 0) || !DeleteEa );
  531. Dirent->FileName[0] = RDBSS_DIRENT_DELETED;
  532. }
  533. //
  534. // Back Dirent off by one to point back to the short dirent.
  535. //
  536. Dirent -= 1;
  537. //
  538. // If there are extended attributes for this dirent, we will attempt
  539. // to remove them. We ignore any errors in removing Eas.
  540. //
  541. if (DeleteEa && (Dirent->ExtendedAttributes != 0)) {
  542. try {
  543. RxDeleteEa( RxContext,
  544. FcbOrDcb->Vcb,
  545. Dirent->ExtendedAttributes,
  546. &FcbOrDcb->ShortName.Name.Oem );
  547. } except(RxExceptionFilter( RxContext, GetExceptionInformation() )) {
  548. //
  549. // We catch all exceptions that Rx catches, but don't do
  550. // anything with them.
  551. //
  552. }
  553. }
  554. //
  555. // Now clear the bits in the free dirent mask.
  556. //
  557. DirentsToDelete = (FcbOrDcb->DirentOffsetWithinDirectory -
  558. FcbOrDcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1;
  559. ASSERT( (FcbOrDcb->ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) ||
  560. RtlAreBitsSet( &FcbOrDcb->ParentDcb->Specific.Dcb.FreeDirentBitmap,
  561. FcbOrDcb->LfnOffsetWithinDirectory / sizeof(DIRENT),
  562. DirentsToDelete ) );
  563. RtlClearBits( &FcbOrDcb->ParentDcb->Specific.Dcb.FreeDirentBitmap,
  564. FcbOrDcb->LfnOffsetWithinDirectory / sizeof(DIRENT),
  565. DirentsToDelete );
  566. //
  567. // Now, if the caller specified a DeleteContext, use it.
  568. //
  569. if ( ARGUMENT_PRESENT( DeleteContext ) ) {
  570. Dirent->FileSize = DeleteContext->FileSize;
  571. Dirent->FirstClusterOfFile = (RDBSS_ENTRY)DeleteContext->FirstClusterOfFile;
  572. }
  573. //
  574. // If this newly deleted dirent is before the DeletedDirentHint, change
  575. // the DeletedDirentHint to point here.
  576. //
  577. if (FcbOrDcb->DirentOffsetWithinDirectory <
  578. FcbOrDcb->ParentDcb->Specific.Dcb.DeletedDirentHint) {
  579. FcbOrDcb->ParentDcb->Specific.Dcb.DeletedDirentHint =
  580. FcbOrDcb->LfnOffsetWithinDirectory;
  581. }
  582. RxUnpinBcb( RxContext, Bcb );
  583. DebugTrace(-1, Dbg, "RxDeleteDirent -> (VOID)\n", 0);
  584. return;
  585. }
  586. VOID
  587. RxLocateDirent (
  588. IN PRX_CONTEXT RxContext,
  589. IN PDCB ParentDirectory,
  590. IN PFOBX Fobx,
  591. IN VBO OffsetToStartSearchFrom,
  592. OUT PDIRENT *Dirent,
  593. OUT PBCB *Bcb,
  594. OUT PVBO ByteOffset,
  595. OUT PUNICODE_STRING LongFileName OPTIONAL
  596. )
  597. /*++
  598. Routine Description:
  599. This routine locates on the disk an undelted dirent matching a given name.
  600. Arguments:
  601. ParentDirectory - Supplies the DCB for the directory in which
  602. to search
  603. Fobx - Contains a context control block with all matching information.
  604. OffsetToStartSearchFrom - Supplies the VBO within the parent directory
  605. from which to start looking for another real dirent.
  606. Dirent - Receives a pointer to the located dirent if one was found
  607. or NULL otherwise.
  608. Bcb - Receives the Bcb for the located dirent if one was found or
  609. NULL otherwise.
  610. ByteOffset - Receives the VBO within the Parent directory for
  611. the located dirent if one was found, or 0 otherwise.
  612. LongFileName - If specified, this parameter returns the long file name
  613. associated with the returned dirent. Note that it is the caller's
  614. responsibility to provide the buffer (and set MaximumLength
  615. accordingly) for this unicode string. The Length field is reset
  616. to 0 by this routine on invocation.
  617. Return Value:
  618. None.
  619. --*/
  620. {
  621. RXSTATUS Status;
  622. OEM_STRING Name;
  623. UCHAR NameBuffer[12];
  624. UNICODE_STRING LocalUpcasedLfn;
  625. UNICODE_STRING PoolUpcasedLfn;
  626. PUNICODE_STRING UpcasedLfn;
  627. WCHAR LocalLfnBuffer[32];
  628. BOOLEAN LfnInProgress = FALSE;
  629. UCHAR LfnChecksum;
  630. ULONG LfnSize;
  631. ULONG LfnIndex;
  632. UCHAR Ordinal;
  633. VBO LfnByteOffset;
  634. TimerStart(Dbg);
  635. PAGED_CODE();
  636. DebugTrace(+1, Dbg, "RxLocateDirent\n", 0);
  637. DebugTrace( 0, Dbg, " ParentDirectory = %08lx\n", ParentDirectory);
  638. // DebugTrace( 0, Dbg, " FileName = %08lx\n", FileName);
  639. DebugTrace( 0, Dbg, " Fobx = %08lx\n", Fobx);
  640. DebugTrace( 0, Dbg, " OffsetToStartSearchFrom = %08lx\n", OffsetToStartSearchFrom);
  641. DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent);
  642. DebugTrace( 0, Dbg, " Bcb = %08lx\n", Bcb);
  643. DebugTrace( 0, Dbg, " ByteOffset = %08lx\n", ByteOffset);
  644. //nolonger DebugTrace( 0, Dbg, "We are looking for the dirent %wZ.\n", FileName);
  645. //
  646. // The algorithm here is pretty simple. We just walk through the
  647. // parent directory until we:
  648. //
  649. // A) Find a matching entry.
  650. // B) Can't Wait
  651. // C) Hit the End of Directory
  652. // D) Hit Eof
  653. //
  654. // In the first case we found it, in the latter three cases we did not.
  655. //
  656. //
  657. // Set up the strings that receives file names from our search
  658. //
  659. Name.MaximumLength = 12;
  660. Name.Buffer = NameBuffer;
  661. PoolUpcasedLfn.Length =
  662. PoolUpcasedLfn.MaximumLength = 0;
  663. PoolUpcasedLfn.Buffer = NULL;
  664. LocalUpcasedLfn.Length = 0;
  665. LocalUpcasedLfn.MaximumLength = 32*sizeof(WCHAR);
  666. LocalUpcasedLfn.Buffer = LocalLfnBuffer;
  667. //
  668. // If we were given a non-NULL Bcb, compute the new Dirent address
  669. // from the prior one, or unpin the Bcb if the new Dirent is not pinned.
  670. //
  671. if (*Bcb != NULL) {
  672. if ((OffsetToStartSearchFrom / PAGE_SIZE) == (*ByteOffset / PAGE_SIZE)) {
  673. *Dirent += (OffsetToStartSearchFrom - *ByteOffset) / sizeof(DIRENT);
  674. } else {
  675. RxUnpinBcb( RxContext, *Bcb );
  676. }
  677. }
  678. //
  679. // Init the Lfn if we were given one.
  680. //
  681. if (ARGUMENT_PRESENT(LongFileName)) {
  682. LongFileName->Length = 0;
  683. }
  684. //
  685. // Round up OffsetToStartSearchFrom to the nearest Dirent, and store
  686. // in ByteOffset. Note that this wipes out the prior value.
  687. //
  688. *ByteOffset = (OffsetToStartSearchFrom + (sizeof(DIRENT) - 1))
  689. & ~(sizeof(DIRENT) - 1);
  690. try {
  691. while ( TRUE ) {
  692. BOOLEAN FoundValidLfn;
  693. //
  694. // Try to read in the dirent
  695. //
  696. RxReadDirent( RxContext,
  697. ParentDirectory,
  698. *ByteOffset,
  699. Bcb,
  700. Dirent,
  701. &Status );
  702. //
  703. // If End Directory dirent or EOF, set all out parameters to
  704. // indicate entry not found and, like, bail.
  705. //
  706. // Note that the order of evaluation here is important since we
  707. // cannot check the first character of the dirent until after we
  708. // know we are not beyond EOF
  709. //
  710. if ((Status == RxStatus(END_OF_FILE)) ||
  711. ((*Dirent)->FileName[0] == RDBSS_DIRENT_NEVER_USED)) {
  712. DebugTrace( 0, Dbg, "End of directory: entry not found.\n", 0);
  713. //
  714. // If there is a Bcb, unpin it and set it to null
  715. //
  716. RxUnpinBcb( RxContext, *Bcb );
  717. *Dirent = NULL;
  718. *ByteOffset = 0;
  719. break;
  720. }
  721. //
  722. // If we have wandered onto an LFN entry, try to interpret it.
  723. //
  724. if (RxData.ChicagoMode &&
  725. ARGUMENT_PRESENT(LongFileName) &&
  726. ((*Dirent)->Attributes == RDBSS_DIRENT_ATTR_LFN)) {
  727. PLFN_DIRENT Lfn;
  728. Lfn = (PLFN_DIRENT)*Dirent;
  729. if (LfnInProgress) {
  730. //
  731. // Check for a propper continuation of the Lfn in progress.
  732. //
  733. if ((Lfn->Ordinal & RDBSS_LAST_LONG_ENTRY) ||
  734. (Lfn->Ordinal == 0) ||
  735. (Lfn->Ordinal != Ordinal - 1) ||
  736. (Lfn->Type != RDBSS_LONG_NAME_COMP) ||
  737. (Lfn->Checksum != LfnChecksum) ||
  738. (Lfn->MustBeZero != 0)) {
  739. //
  740. // The Lfn is not propper, stop constructing it.
  741. //
  742. LfnInProgress = FALSE;
  743. } else {
  744. ASSERT( ((LfnIndex % 13) == 0) && LfnIndex );
  745. LfnIndex -= 13;
  746. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+0],
  747. &Lfn->Name1[0],
  748. 5*sizeof(WCHAR) );
  749. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+5],
  750. &Lfn->Name2[0],
  751. 6 * sizeof(WCHAR) );
  752. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+11],
  753. &Lfn->Name3[0],
  754. 2 * sizeof(WCHAR) );
  755. Ordinal = Lfn->Ordinal;
  756. LfnByteOffset = *ByteOffset;
  757. }
  758. }
  759. //
  760. // Now check (maybe again) if should analyse this entry
  761. // for a possible last entry.
  762. //
  763. if ((!LfnInProgress) &&
  764. (Lfn->Ordinal & RDBSS_LAST_LONG_ENTRY) &&
  765. ((Lfn->Ordinal & ~RDBSS_LAST_LONG_ENTRY) <= MAX_LFN_DIRENTS) &&
  766. (Lfn->Type == RDBSS_LONG_NAME_COMP) &&
  767. (Lfn->MustBeZero == 0)) {
  768. BOOLEAN CheckTail = FALSE;
  769. Ordinal = Lfn->Ordinal & ~RDBSS_LAST_LONG_ENTRY;
  770. LfnIndex = (Ordinal - 1) * 13;
  771. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+0],
  772. &Lfn->Name1[0],
  773. 5*sizeof(WCHAR));
  774. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+5],
  775. &Lfn->Name2[0],
  776. 6 * sizeof(WCHAR) );
  777. RtlCopyMemory( &LongFileName->Buffer[LfnIndex+11],
  778. &Lfn->Name3[0],
  779. 2 * sizeof(WCHAR) );
  780. //
  781. // Now compute the Lfn size and make sure that the tail
  782. // bytes are correct.
  783. //
  784. while (LfnIndex != (ULONG)Ordinal * 13) {
  785. if (!CheckTail) {
  786. if (LongFileName->Buffer[LfnIndex] == 0x0000) {
  787. LfnSize = LfnIndex;
  788. CheckTail = TRUE;
  789. }
  790. } else {
  791. if (LongFileName->Buffer[LfnIndex] != 0xffff) {
  792. break;
  793. }
  794. }
  795. LfnIndex += 1;
  796. }
  797. //
  798. // If we exited this loop prematurely, the LFN is not valid.
  799. //
  800. if (LfnIndex == (ULONG)Ordinal * 13) {
  801. //
  802. // If we didn't find the NULL terminator, then the size
  803. // is LfnIndex.
  804. //
  805. if (!CheckTail) {
  806. LfnSize = LfnIndex;
  807. }
  808. LfnIndex -= 13;
  809. LfnInProgress = TRUE;
  810. LfnChecksum = Lfn->Checksum;
  811. LfnByteOffset = *ByteOffset;
  812. }
  813. }
  814. //
  815. // Move on to the next dirent.
  816. //
  817. goto GetNextDirent;
  818. }
  819. //
  820. // If the file is not deleted and is not the volume label, check
  821. // for a match.
  822. //
  823. if ( ((*Dirent)->FileName[0] == RDBSS_DIRENT_DELETED) ||
  824. FlagOn((*Dirent)->Attributes, RDBSS_DIRENT_ATTR_VOLUME_ID)) {
  825. //
  826. // Move on to the next dirent.
  827. //
  828. goto GetNextDirent;
  829. }
  830. //
  831. // We may have just stepped off a valid Lfn run. Check to see if
  832. // it is indeed valid for the following dirent.
  833. //
  834. if (LfnInProgress &&
  835. (*ByteOffset == LfnByteOffset + sizeof(DIRENT)) &&
  836. (LfnIndex == 0) &&
  837. (RxComputeLfnChecksum(*Dirent) == LfnChecksum)) {
  838. ASSERT( Ordinal == 1);
  839. FoundValidLfn = TRUE;
  840. LongFileName->Length = (USHORT)(LfnSize * sizeof(WCHAR));
  841. } else {
  842. FoundValidLfn = FALSE;
  843. }
  844. //
  845. // If we are supposed to match all entries, then match this entry.
  846. //
  847. if (FlagOn(Fobx->Flags, FOBX_FLAG_MATCH_ALL)) {
  848. break;
  849. }
  850. //
  851. // Check against the short name given if one was.
  852. //
  853. if (TRUE){ //!FlagOn( Fobx->Flags, FOBX_FLAG_SKIP_SHORT_NAME_COMPARE )) {
  854. if (Fobx->ContainsWildCards) {
  855. //
  856. // If we get one, note that all out parameters are already set.
  857. //
  858. (VOID)Rx8dot3ToString( RxContext, (*Dirent), FALSE, &Name );
  859. //
  860. // For rx we special case the ".." dirent because we want it to
  861. // match ????????.??? and to do that we change ".." to "." before
  862. // calling the Fsrtl routine. But only do this if the expression
  863. // is greater than one character long.
  864. //
  865. ASSERT(FALSE); //this shouldn't be called...get rid of it later joejoe
  866. /*
  867. if ((Name.Length == 2) &&
  868. (Name.Buffer[0] == '.') &&
  869. (Name.Buffer[1] == '.') &&
  870. (Fobx->OemQueryTemplate.Wild.Length > 1)) {
  871. Name.Length = 1;
  872. }
  873. if (RxIsNameInExpression( RxContext,
  874. Fobx->OemQueryTemplate.Wild,
  875. Name)) {
  876. DebugTrace( 0, Dbg, "Entry found: Name = \"%wZ\"\n", &Name);
  877. DebugTrace( 0, Dbg, " VBO = %08lx\n", *ByteOffset);
  878. break;
  879. }
  880. */
  881. } else {
  882. //
  883. // Do the quickest 8.3 equivalency check possible
  884. //
  885. ASSERT(FALSE);
  886. // if (!FlagOn((*Dirent)->Attributes, RDBSS_DIRENT_ATTR_VOLUME_ID) &&
  887. // (*(PULONG)&(Fobx->OemQueryTemplate.Constant[0]) == *(PULONG)&((*Dirent)->FileName[0])) &&
  888. // (*(PULONG)&(Fobx->OemQueryTemplate.Constant[4]) == *(PULONG)&((*Dirent)->FileName[4])) &&
  889. // (*(PUSHORT)&(Fobx->OemQueryTemplate.Constant[8]) == *(PUSHORT)&((*Dirent)->FileName[8])) &&
  890. // (*(PUCHAR)&(Fobx->OemQueryTemplate.Constant[10]) == *(PUCHAR)&((*Dirent)->FileName[10]))) {
  891. //
  892. // DebugTrace( 0, Dbg, "Entry found.\n", 0);
  893. //
  894. // break;
  895. // }
  896. }
  897. }
  898. //
  899. // No matches were found with the short name. If an LFN exists,
  900. // use it for the search.
  901. //
  902. if (FoundValidLfn) {
  903. //
  904. // First do a quick check here for different sized constant
  905. // name and expression before upcasing.
  906. //
  907. if (!Fobx->ContainsWildCards &&
  908. Fobx->UnicodeQueryTemplate.Length != (USHORT)(LfnSize * sizeof(WCHAR))) {
  909. //
  910. // Move on to the next dirent.
  911. //
  912. FoundValidLfn = FALSE;
  913. LongFileName->Length = 0;
  914. goto GetNextDirent;
  915. }
  916. //
  917. // We need to upcase the name we found.
  918. // We need a buffer. Try to avoid doing an allocation.
  919. //
  920. if (LongFileName->Length <= 32*sizeof(WCHAR)) {
  921. UpcasedLfn = &LocalUpcasedLfn;
  922. } else if (LongFileName->Length <= PoolUpcasedLfn.MaximumLength) {
  923. UpcasedLfn = &PoolUpcasedLfn;
  924. } else {
  925. //
  926. // Free the old buffer now, and get a new one.
  927. //
  928. if (PoolUpcasedLfn.Buffer) {
  929. ExFreePool( PoolUpcasedLfn.Buffer );
  930. PoolUpcasedLfn.Buffer = NULL;
  931. }
  932. PoolUpcasedLfn.Buffer =
  933. FsRtlAllocatePool( PagedPool,
  934. LongFileName->Length );
  935. PoolUpcasedLfn.MaximumLength = LongFileName->Length;
  936. UpcasedLfn = &PoolUpcasedLfn;
  937. }
  938. Status = RtlUpcaseUnicodeString( UpcasedLfn,
  939. LongFileName,
  940. FALSE );
  941. if (!NT_SUCCESS(Status)) {
  942. RxNormalizeAndRaiseStatus( RxContext, Status );
  943. }
  944. //
  945. // OK, We are going to assume that the passed in UnicodeFileName
  946. // has already been upcased.
  947. //
  948. if (Fobx->ContainsWildCards) {
  949. if (FsRtlIsNameInExpression( &Fobx->UnicodeQueryTemplate,
  950. UpcasedLfn,
  951. TRUE,
  952. NULL )) {
  953. break;
  954. }
  955. } else {
  956. if (FsRtlAreNamesEqual( &Fobx->UnicodeQueryTemplate,
  957. UpcasedLfn,
  958. FALSE,
  959. NULL )) {
  960. break;
  961. }
  962. }
  963. }
  964. //
  965. // This long name was not a match. Zero out the Length field.
  966. //
  967. if (FoundValidLfn) {
  968. FoundValidLfn = FALSE;
  969. LongFileName->Length = 0;
  970. }
  971. GetNextDirent:
  972. //
  973. // Move on to the next dirent.
  974. //
  975. *ByteOffset += sizeof(DIRENT);
  976. *Dirent += 1;
  977. }
  978. } finally {
  979. if (PoolUpcasedLfn.Buffer != NULL) {
  980. ExFreePool( PoolUpcasedLfn.Buffer );
  981. }
  982. }
  983. DebugTrace(-1, Dbg, "RxLocateDirent -> (VOID)\n", 0);
  984. TimerStop(Dbg,"RxLocateDirent");
  985. return;
  986. }
  987. VOID
  988. RxLocateSimpleOemDirent (
  989. IN PRX_CONTEXT RxContext,
  990. IN PDCB ParentDirectory,
  991. IN POEM_STRING FileName,
  992. OUT PDIRENT *Dirent,
  993. OUT PBCB *Bcb,
  994. OUT PVBO ByteOffset
  995. )
  996. /*++
  997. Routine Description:
  998. This routine locates on the disk an undelted simple Oem dirent. By simple
  999. I mean that FileName cannot contain any extended characters, and we do
  1000. not search LFNs or return them.
  1001. Arguments:
  1002. ParentDirectory - Supplies the DCB for the directory in which
  1003. to search
  1004. FileName - Supplies the filename to search for. The name may contain
  1005. wild cards
  1006. OffsetToStartSearchFrom - Supplies the VBO within the parent directory
  1007. from which to start looking for another real dirent.
  1008. Dirent - Receives a pointer to the located dirent if one was found
  1009. or NULL otherwise.
  1010. Bcb - Receives the Bcb for the located dirent if one was found or
  1011. NULL otherwise.
  1012. ByteOffset - Receives the VBO within the Parent directory for
  1013. the located dirent if one was found, or 0 otherwise.
  1014. Return Value:
  1015. None.
  1016. --*/
  1017. {
  1018. FOBX LocalFobx;
  1019. PAGED_CODE();
  1020. //
  1021. // Note, this routine is called rarely, so performance is not critical.
  1022. // Just fill in a Fobx structure on my stack with the values that are
  1023. // required.
  1024. //
  1025. ASSERT(FALSE); //this should never be called
  1026. // RxStringTo8dot3( RxContext,
  1027. // *FileName,
  1028. // &LocalFobx.OemQueryTemplate.Constant );
  1029. LocalFobx.ContainsWildCards = FALSE;
  1030. LocalFobx.Flags = 0;
  1031. RxLocateDirent( RxContext,
  1032. ParentDirectory,
  1033. &LocalFobx,
  1034. 0,
  1035. Dirent,
  1036. Bcb,
  1037. ByteOffset,
  1038. NULL );
  1039. return;
  1040. }
  1041. VOID
  1042. RxLocateVolumeLabel (
  1043. IN PRX_CONTEXT RxContext,
  1044. IN PVCB Vcb,
  1045. OUT PDIRENT *Dirent,
  1046. OUT PBCB *Bcb,
  1047. OUT PVBO ByteOffset
  1048. )
  1049. /*++
  1050. Routine Description:
  1051. This routine locates on the disk a dirent representing the volume
  1052. label. It does this by searching the root directory for a special
  1053. volume label dirent.
  1054. Arguments:
  1055. Vcb - Supplies the VCB for the volume to search
  1056. Dirent - Receives a pointer to the located dirent if one was found
  1057. or NULL otherwise.
  1058. Bcb - Receives the Bcb for the located dirent if one was found or
  1059. NULL otherwise.
  1060. ByteOffset - Receives the VBO within the Parent directory for
  1061. the located dirent if one was found, or 0 otherwise.
  1062. Return Value:
  1063. None.
  1064. --*/
  1065. {
  1066. RXSTATUS Status;
  1067. PAGED_CODE();
  1068. DebugTrace(+1, Dbg, "RxLocateVolumeLabel\n", 0);
  1069. DebugTrace( 0, Dbg, " Vcb = %08lx\n", Vcb);
  1070. DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent);
  1071. DebugTrace( 0, Dbg, " Bcb = %08lx\n", Bcb);
  1072. DebugTrace( 0, Dbg, " ByteOffset = %08lx\n", ByteOffset);
  1073. //
  1074. // The algorithm here is really simple. We just walk through the
  1075. // root directory until we:
  1076. //
  1077. // A) Find the non-deleted volume label
  1078. // B) Can't Wait
  1079. // C) Hit the End of Directory
  1080. // D) Hit Eof
  1081. //
  1082. // In the first case we found it, in the latter three cases we did not.
  1083. //
  1084. *Bcb = NULL;
  1085. *ByteOffset = 0;
  1086. while ( TRUE ) {
  1087. //
  1088. // Try to read in the dirent
  1089. //
  1090. RxReadDirent( RxContext,
  1091. Vcb->RootDcb,
  1092. *ByteOffset,
  1093. Bcb,
  1094. Dirent,
  1095. &Status );
  1096. //
  1097. // If End Directory dirent or EOF, set all out parameters to
  1098. // indicate volume label not found and, like, bail.
  1099. //
  1100. // Note that the order of evaluation here is important since we cannot
  1101. // check the first character of the dirent until after we know we
  1102. // are not beyond EOF
  1103. //
  1104. if ((Status == RxStatus(END_OF_FILE)) || ((*Dirent)->FileName[0] ==
  1105. RDBSS_DIRENT_NEVER_USED)) {
  1106. DebugTrace( 0, Dbg, "Volume label not found.\n", 0);
  1107. //
  1108. // If there is a Bcb, unpin it and set it to null
  1109. //
  1110. RxUnpinBcb( RxContext, *Bcb );
  1111. *Dirent = NULL;
  1112. *ByteOffset = 0;
  1113. break;
  1114. }
  1115. //
  1116. // If the entry is the non-deleted volume label break from the loop.
  1117. //
  1118. // Note that all out parameters are already correctly set.
  1119. //
  1120. if ((((*Dirent)->Attributes & ~RDBSS_DIRENT_ATTR_ARCHIVE) == RDBSS_DIRENT_ATTR_VOLUME_ID) &&
  1121. ((*Dirent)->FileName[0] != RDBSS_DIRENT_DELETED)) {
  1122. DebugTrace( 0, Dbg, "Volume label found at VBO = %08lx\n", *ByteOffset);
  1123. //
  1124. // We may set this dirty, so pin it.
  1125. //
  1126. RxPinMappedData( RxContext,
  1127. Vcb->RootDcb,
  1128. *ByteOffset,
  1129. sizeof(DIRENT),
  1130. Bcb );
  1131. break;
  1132. }
  1133. //
  1134. // Move on to the next dirent.
  1135. //
  1136. *ByteOffset += sizeof(DIRENT);
  1137. *Dirent += 1;
  1138. }
  1139. DebugTrace(-1, Dbg, "RxLocateVolumeLabel -> (VOID)\n", 0);
  1140. return;
  1141. }
  1142. VOID
  1143. RxGetDirentFromFcbOrDcb (
  1144. IN PRX_CONTEXT RxContext,
  1145. IN PFCB FcbOrDcb,
  1146. OUT PDIRENT *Dirent,
  1147. OUT PBCB *Bcb
  1148. )
  1149. /*++
  1150. Routine Description:
  1151. This routine reads locates on the disk the dirent denoted by the
  1152. specified Fcb/Dcb.
  1153. Arguments:
  1154. FcbOrDcb - Supplies the FCB/DCB for the file/directory whose dirent
  1155. we are trying to read in. This must not be the root dcb.
  1156. Dirent - Receives a pointer to the dirent
  1157. Bcb - Receives the Bcb for the dirent
  1158. Return Value:
  1159. None.
  1160. --*/
  1161. {
  1162. RXSTATUS DontCare;
  1163. PAGED_CODE();
  1164. DebugTrace(+1, Dbg, "RxGetDirentFromFcbOrDcb\n", 0);
  1165. DebugTrace( 0, Dbg, " FcbOrDcb = %08lx\n", FcbOrDcb);
  1166. DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent);
  1167. DebugTrace( 0, Dbg, " Bcb = %08lx\n", Bcb);
  1168. //
  1169. // Assert that we are not attempting this on the root directory.
  1170. //
  1171. ASSERT( NodeType(FcbOrDcb) != RDBSS_NTC_ROOT_DCB );
  1172. //
  1173. // We know the offset of the dirent within the directory file,
  1174. // so we just read it (with pinning).
  1175. //
  1176. RxReadDirectoryFile( RxContext,
  1177. FcbOrDcb->ParentDcb,
  1178. FcbOrDcb->DirentOffsetWithinDirectory,
  1179. sizeof(DIRENT),
  1180. TRUE,
  1181. Bcb,
  1182. (PVOID *)Dirent,
  1183. &DontCare );
  1184. ASSERT( NT_SUCCESS( DontCare ));
  1185. //
  1186. // We should never be accessing a deleted entry (except on removeable
  1187. // media where the dirent may have changed out from underneath us).
  1188. //
  1189. ASSERT( FlagOn( FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) ?
  1190. TRUE : (BOOLEAN)((*Dirent)->FileName[0] != RDBSS_DIRENT_DELETED ));
  1191. DebugTrace(-1, Dbg, "RxGetDirentFromFcbOrDcb -> (VOID)\n", 0);
  1192. return;
  1193. }
  1194. BOOLEAN
  1195. RxIsDirectoryEmpty (
  1196. IN PRX_CONTEXT RxContext,
  1197. IN PDCB Dcb
  1198. )
  1199. /*++
  1200. Routine Description:
  1201. This routine indicates to the caller if the specified directory
  1202. is empty. (i.e., it is not the root dcb and it only contains
  1203. the "." and ".." entries, or deleted files).
  1204. Arguments:
  1205. Dcb - Supplies the DCB for the directory being queried.
  1206. Return Value:
  1207. BOOLEAN - Returns TRUE if the directory is empty and
  1208. FALSE if the directory and is not empty.
  1209. --*/
  1210. {
  1211. PBCB Bcb;
  1212. ULONG ByteOffset;
  1213. PDIRENT Dirent;
  1214. BOOLEAN IsDirectoryEmpty;
  1215. RXSTATUS Status;
  1216. PAGED_CODE();
  1217. DebugTrace(+1, Dbg, "RxIsDirectoryEmpty\n", 0);
  1218. DebugTrace( 0, Dbg, " Dcb = %08lx\n", Dcb);
  1219. // DebugTrace( 0, Dbg, " IsDirectoryEmpty = %08lx\n", IsDirectoryEmpty);
  1220. //
  1221. // Check to see if the first entry is an and of directory marker.
  1222. // For the root directory we check at Vbo = 0, for normal directories
  1223. // we check after the "." and ".." entries.
  1224. //
  1225. ByteOffset = (NodeType(Dcb) == RDBSS_NTC_ROOT_DCB) ? 0 : 2*sizeof(DIRENT);
  1226. //
  1227. // We just march through the directory looking for anything other
  1228. // than deleted files, LFNs, an EOF, or end of directory marker.
  1229. //
  1230. Bcb = NULL;
  1231. while ( TRUE ) {
  1232. //
  1233. // Try to read in the dirent
  1234. //
  1235. RxReadDirent( RxContext,
  1236. Dcb,
  1237. ByteOffset,
  1238. &Bcb,
  1239. &Dirent,
  1240. &Status );
  1241. //
  1242. // If End Directory dirent or EOF, set IsDirectoryEmpty to TRUE and,
  1243. // like, bail.
  1244. //
  1245. // Note that the order of evaluation here is important since we cannot
  1246. // check the first character of the dirent until after we know we
  1247. // are not beyond EOF
  1248. //
  1249. if ((Status == RxStatus(END_OF_FILE)) ||
  1250. (Dirent->FileName[0] == RDBSS_DIRENT_NEVER_USED)) {
  1251. DebugTrace( 0, Dbg, "Empty. Last exempt entry at VBO = %08lx\n", ByteOffset);
  1252. IsDirectoryEmpty = TRUE;
  1253. break;
  1254. }
  1255. //
  1256. // If this dirent is NOT deleted or an LFN set IsDirectoryEmpty to
  1257. // FALSE and, like, bail.
  1258. //
  1259. if ((Dirent->FileName[0] != RDBSS_DIRENT_DELETED) &&
  1260. (Dirent->Attributes != RDBSS_DIRENT_ATTR_LFN)) {
  1261. DebugTrace( 0, Dbg, "Not Empty. First entry at VBO = %08lx\n", ByteOffset);
  1262. IsDirectoryEmpty = FALSE;
  1263. break;
  1264. }
  1265. //
  1266. // Move on to the next dirent.
  1267. //
  1268. ByteOffset += sizeof(DIRENT);
  1269. Dirent += 1;
  1270. }
  1271. RxUnpinBcb( RxContext, Bcb );
  1272. DebugTrace(-1, Dbg, "RxIsDirectoryEmpty -> %ld\n", IsDirectoryEmpty);
  1273. return IsDirectoryEmpty;
  1274. }
  1275. VOID
  1276. RxConstructDirent (
  1277. IN PRX_CONTEXT RxContext,
  1278. IN OUT PDIRENT Dirent,
  1279. IN POEM_STRING FileName,
  1280. IN BOOLEAN ComponentReallyLowercase,
  1281. IN BOOLEAN ExtensionReallyLowercase,
  1282. IN PUNICODE_STRING Lfn OPTIONAL,
  1283. IN UCHAR Attributes,
  1284. IN BOOLEAN ZeroAndSetTimeFields
  1285. )
  1286. /*++
  1287. Routine Description:
  1288. This routine modifies the fields of a dirent.
  1289. Arguments:
  1290. Dirent - Supplies the dirent being modified.
  1291. FileName - Supplies the name to store in the Dirent. This
  1292. name must not contain wildcards.
  1293. ComponentReallyLowercase - This boolean indicates that the User Specified
  1294. compoent name was really all a-z and < 0x80 characters. We set the
  1295. magic bit in this case.
  1296. ExtensionReallyLowercase - Same as above, but for the extension.
  1297. Lfn - May supply a long file name.
  1298. Attributes - Supplies the attributes to store in the dirent
  1299. ZeroAndSetTimeFields - Tells whether or not to initially zero the dirent
  1300. and update the time fields.
  1301. Return Value:
  1302. None.
  1303. --*/
  1304. {
  1305. PAGED_CODE();
  1306. DebugTrace(+1, Dbg, "RxConstructDirent\n", 0);
  1307. DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent);
  1308. DebugTrace( 0, Dbg, " FileName = %wZ\n", FileName);
  1309. DebugTrace( 0, Dbg, " Attributes = %08lx\n", Attributes);
  1310. if (ZeroAndSetTimeFields) {
  1311. RtlZeroMemory( Dirent, sizeof(DIRENT) );
  1312. }
  1313. //
  1314. // We just merrily go and fill up the dirent with the fields given.
  1315. //
  1316. RxStringTo8dot3( RxContext, *FileName, (PRDBSS8DOT3)&Dirent->FileName[0] );
  1317. //
  1318. // Use the current time for all time fields.
  1319. //
  1320. if (ZeroAndSetTimeFields) {
  1321. LARGE_INTEGER Time;
  1322. KeQuerySystemTime( &Time );
  1323. if (!RxNtTimeToRxTime( RxContext, Time, &Dirent->LastWriteTime )) {
  1324. DebugTrace( 0, Dbg, "Current time invalid.\n", 0);
  1325. RtlZeroMemory( &Dirent->LastWriteTime, sizeof(RDBSS_TIME_STAMP) );
  1326. Time.LowPart = 0;
  1327. }
  1328. if (RxData.ChicagoMode) {
  1329. Dirent->CreationTime = Dirent->LastWriteTime;
  1330. Dirent->CreationMSec =
  1331. (UCHAR)(((Time.LowPart + AlmostTenMSec) % TwoSeconds) / TenMSec);
  1332. Dirent->LastAccessDate = Dirent->LastWriteTime.Date;
  1333. }
  1334. }
  1335. //
  1336. // Copy the attributes
  1337. //
  1338. Dirent->Attributes = Attributes;
  1339. //
  1340. // Set the magic bit here, to tell dirctrl.c that this name is really
  1341. // lowercase.
  1342. //
  1343. Dirent->NtByte = 0;
  1344. if (ComponentReallyLowercase) {
  1345. SetFlag( Dirent->NtByte, RDBSS_DIRENT_NT_BYTE_8_LOWER_CASE );
  1346. }
  1347. if (ExtensionReallyLowercase) {
  1348. SetFlag( Dirent->NtByte, RDBSS_DIRENT_NT_BYTE_3_LOWER_CASE );
  1349. }
  1350. //
  1351. // See if we have to create an Lfn entry
  1352. //
  1353. if (ARGUMENT_PRESENT(Lfn)) {
  1354. UCHAR DirentChecksum;
  1355. UCHAR DirentsInLfn;
  1356. UCHAR LfnOrdinal;
  1357. PWCHAR LfnBuffer;
  1358. PLFN_DIRENT LfnDirent;
  1359. ASSERT( RxData.ChicagoMode );
  1360. DirentChecksum = RxComputeLfnChecksum( Dirent );
  1361. LfnOrdinal =
  1362. DirentsInLfn = RDBSS_LFN_DIRENTS_NEEDED(Lfn);
  1363. LfnBuffer = &Lfn->Buffer[(DirentsInLfn - 1) * 13];
  1364. ASSERT( DirentsInLfn <= MAX_LFN_DIRENTS );
  1365. for (LfnDirent = (PLFN_DIRENT)Dirent - DirentsInLfn;
  1366. LfnDirent < (PLFN_DIRENT)Dirent;
  1367. LfnDirent += 1, LfnOrdinal -= 1, LfnBuffer -= 13) {
  1368. WCHAR FinalLfnBuffer[13];
  1369. PWCHAR Buffer;
  1370. //
  1371. // We need to special case the "final" dirent.
  1372. //
  1373. if (LfnOrdinal == DirentsInLfn) {
  1374. ULONG i;
  1375. ULONG RemainderChars;
  1376. RemainderChars = (Lfn->Length / sizeof(WCHAR)) % 13;
  1377. LfnDirent->Ordinal = LfnOrdinal | RDBSS_LAST_LONG_ENTRY;
  1378. if (RemainderChars != 0) {
  1379. RtlCopyMemory( &FinalLfnBuffer,
  1380. LfnBuffer,
  1381. RemainderChars * sizeof(WCHAR) );
  1382. for (i = RemainderChars; i < 13; i++) {
  1383. //
  1384. // Figure out which character to use.
  1385. //
  1386. if (i == RemainderChars) {
  1387. FinalLfnBuffer[i] = 0x0000;
  1388. } else {
  1389. FinalLfnBuffer[i] = 0xffff;
  1390. }
  1391. }
  1392. Buffer = FinalLfnBuffer;
  1393. } else {
  1394. Buffer = LfnBuffer;
  1395. }
  1396. } else {
  1397. LfnDirent->Ordinal = LfnOrdinal;
  1398. Buffer = LfnBuffer;
  1399. }
  1400. //
  1401. // Now fill in the name.
  1402. //
  1403. RtlCopyMemory( &LfnDirent->Name1[0],
  1404. &Buffer[0],
  1405. 5 * sizeof(WCHAR) );
  1406. RtlCopyMemory( &LfnDirent->Name2[0],
  1407. &Buffer[5],
  1408. 6 * sizeof(WCHAR) );
  1409. RtlCopyMemory( &LfnDirent->Name3[0],
  1410. &Buffer[11],
  1411. 2 * sizeof(WCHAR) );
  1412. //
  1413. // And the other fields
  1414. //
  1415. LfnDirent->Attributes = RDBSS_DIRENT_ATTR_LFN;
  1416. LfnDirent->Type = 0;
  1417. LfnDirent->Checksum = DirentChecksum;
  1418. LfnDirent->MustBeZero = 0;
  1419. }
  1420. }
  1421. DebugTrace(-1, Dbg, "RxConstructDirent -> (VOID)\n", 0);
  1422. return;
  1423. }
  1424. VOID
  1425. RxConstructLabelDirent (
  1426. IN PRX_CONTEXT RxContext,
  1427. IN OUT PDIRENT Dirent,
  1428. IN POEM_STRING Label
  1429. )
  1430. /*++
  1431. Routine Description:
  1432. This routine modifies the fields of a dirent to be used for a label.
  1433. Arguments:
  1434. Dirent - Supplies the dirent being modified.
  1435. Label - Supplies the name to store in the Dirent. This
  1436. name must not contain wildcards.
  1437. Return Value:
  1438. None.
  1439. --*/
  1440. {
  1441. PAGED_CODE();
  1442. DebugTrace(+1, Dbg, "RxConstructLabelDirent\n", 0);
  1443. DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent);
  1444. DebugTrace( 0, Dbg, " Label = %wZ\n", Label);
  1445. RtlZeroMemory( Dirent, sizeof(DIRENT) );
  1446. //
  1447. // We just merrily go and fill up the dirent with the fields given.
  1448. //
  1449. RtlCopyMemory( Dirent->FileName, Label->Buffer, Label->Length );
  1450. //
  1451. // Pad the label with spaces, not nulls.
  1452. //
  1453. RtlFillMemory( &Dirent->FileName[Label->Length], 11 - Label->Length, ' ');
  1454. Dirent->LastWriteTime = RxGetCurrentRxTime( RxContext );
  1455. Dirent->Attributes = RDBSS_DIRENT_ATTR_VOLUME_ID;
  1456. Dirent->ExtendedAttributes = 0;
  1457. Dirent->FileSize = 0;
  1458. DebugTrace(-1, Dbg, "RxConstructLabelDirent -> (VOID)\n", 0);
  1459. return;
  1460. }
  1461. VOID
  1462. RxSetFileSizeInDirent (
  1463. IN PRX_CONTEXT RxContext,
  1464. IN PFCB Fcb,
  1465. IN PULONG AlternativeFileSize OPTIONAL
  1466. )
  1467. /*++
  1468. Routine Description:
  1469. This routine saves the file size in an fcb into its dirent.
  1470. Arguments:
  1471. Fcb - Supplies the Fcb being referenced
  1472. AlternativeFileSize - If non-null we use the ULONG it points to as
  1473. the new file size. Otherwise we use the one in the Fcb.
  1474. Return Value:
  1475. None.
  1476. --*/
  1477. {
  1478. PDIRENT Dirent;
  1479. PBCB DirentBcb;
  1480. PAGED_CODE();
  1481. //****ASSERT( !FlagOn( Fcb->DirentRxFlags, RDBSS_DIRENT_ATTR_READ_ONLY ));
  1482. RxGetDirentFromFcbOrDcb( RxContext,
  1483. Fcb,
  1484. &Dirent,
  1485. &DirentBcb );
  1486. try {
  1487. Dirent->FileSize = ARGUMENT_PRESENT( AlternativeFileSize ) ?
  1488. *AlternativeFileSize : Fcb->Header.FileSize.LowPart;
  1489. RxSetDirtyBcb( RxContext, DirentBcb, Fcb->Vcb );
  1490. } finally {
  1491. RxUnpinBcb( RxContext, DirentBcb );
  1492. }
  1493. }
  1494. //
  1495. // Internal support routine
  1496. //
  1497. UCHAR
  1498. RxComputeLfnChecksum (
  1499. PDIRENT Dirent
  1500. )
  1501. /*++
  1502. Routine Description:
  1503. This routine computes the Chicago long file name checksum.
  1504. Arguments:
  1505. Dirent - Specifies the dirent that we are to compute a checksum for.
  1506. Return Value:
  1507. The checksum.
  1508. --*/
  1509. {
  1510. ULONG i;
  1511. UCHAR Checksum;
  1512. PAGED_CODE();
  1513. Checksum = Dirent->FileName[0];
  1514. for (i=1; i < 11; i++) {
  1515. Checksum = ((Checksum & 1) ? 0x80 : 0) +
  1516. (Checksum >> 1) +
  1517. Dirent->FileName[i];
  1518. }
  1519. return Checksum;
  1520. }
  1521. //
  1522. // Internal support routine
  1523. //
  1524. VOID
  1525. RxRescanDirectory (
  1526. PRX_CONTEXT RxContext,
  1527. PDCB Dcb
  1528. )
  1529. /*++
  1530. Routine Description:
  1531. This routine rescans the given directory, finding the first unused
  1532. dirent, first deleted dirent, and setting the free dirent bitmap
  1533. appropriately.
  1534. Arguments:
  1535. Dcb - Supplies the directory to rescan.
  1536. Return Value:
  1537. None.
  1538. --*/
  1539. {
  1540. PBCB Bcb = NULL;
  1541. PDIRENT Dirent;
  1542. RXSTATUS Status;
  1543. ULONG UnusedVbo;
  1544. ULONG DeletedHint;
  1545. ULONG DirentIndex;
  1546. ULONG DirentsThisRun;
  1547. ULONG StartIndexOfThisRun;
  1548. enum RunType {
  1549. InitialRun,
  1550. FreeDirents,
  1551. AllocatedDirents,
  1552. } CurrentRun;
  1553. PAGED_CODE();
  1554. DebugTrace( 0, Dbg, "We must scan the whole directory.\n", 0);
  1555. UnusedVbo = 0;
  1556. DeletedHint = 0xffffffff;
  1557. //
  1558. // To start with, we have to find out if the first dirent is free.
  1559. //
  1560. CurrentRun = InitialRun;
  1561. DirentIndex =
  1562. StartIndexOfThisRun = 0;
  1563. while ( TRUE ) {
  1564. BOOLEAN DirentDeleted;
  1565. //
  1566. // Read a dirent
  1567. //
  1568. RxReadDirent( RxContext,
  1569. Dcb,
  1570. UnusedVbo,
  1571. &Bcb,
  1572. &Dirent,
  1573. &Status );
  1574. //
  1575. // If EOF, or we found a NEVER_USED entry, we exit the loop
  1576. //
  1577. if ( (Status == RxStatus(END_OF_FILE) ) ||
  1578. (Dirent->FileName[0] == RDBSS_DIRENT_NEVER_USED)) {
  1579. break;
  1580. }
  1581. //
  1582. // If the dirent is DELETED, and it is the first one we found, set
  1583. // it in the deleted hint.
  1584. //
  1585. if (Dirent->FileName[0] == RDBSS_DIRENT_DELETED) {
  1586. DirentDeleted = TRUE;
  1587. if (DeletedHint == 0xffffffff) {
  1588. DeletedHint = UnusedVbo;
  1589. }
  1590. } else {
  1591. DirentDeleted = FALSE;
  1592. }
  1593. //
  1594. // Check for the first time through the loop, and determine
  1595. // the current run type.
  1596. //
  1597. if (CurrentRun == InitialRun) {
  1598. CurrentRun = DirentDeleted ?
  1599. FreeDirents : AllocatedDirents;
  1600. } else {
  1601. //
  1602. // Are we switching from a free run to an allocated run?
  1603. //
  1604. if ((CurrentRun == FreeDirents) && !DirentDeleted) {
  1605. DirentsThisRun = DirentIndex - StartIndexOfThisRun;
  1606. RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  1607. StartIndexOfThisRun,
  1608. DirentsThisRun );
  1609. CurrentRun = AllocatedDirents;
  1610. StartIndexOfThisRun = DirentIndex;
  1611. }
  1612. //
  1613. // Are we switching from an allocated run to a free run?
  1614. //
  1615. if ((CurrentRun == AllocatedDirents) && DirentDeleted) {
  1616. DirentsThisRun = DirentIndex - StartIndexOfThisRun;
  1617. RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  1618. StartIndexOfThisRun,
  1619. DirentsThisRun );
  1620. CurrentRun = FreeDirents;
  1621. StartIndexOfThisRun = DirentIndex;
  1622. }
  1623. }
  1624. //
  1625. // Move on to the next dirent.
  1626. //
  1627. UnusedVbo += sizeof(DIRENT);
  1628. Dirent += 1;
  1629. DirentIndex += 1;
  1630. }
  1631. //
  1632. // Now we have to record the final run we encoutered
  1633. //
  1634. DirentsThisRun = DirentIndex - StartIndexOfThisRun;
  1635. if ((CurrentRun == FreeDirents) || (CurrentRun == InitialRun)) {
  1636. RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  1637. StartIndexOfThisRun,
  1638. DirentsThisRun );
  1639. } else {
  1640. RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  1641. StartIndexOfThisRun,
  1642. DirentsThisRun );
  1643. }
  1644. //
  1645. // Now if there we bailed prematurely out of the loop because
  1646. // we hit an unused entry, set all the rest as free.
  1647. //
  1648. if (UnusedVbo < Dcb->Header.AllocationSize.LowPart) {
  1649. StartIndexOfThisRun = UnusedVbo / sizeof(DIRENT);
  1650. DirentsThisRun = (Dcb->Header.AllocationSize.LowPart -
  1651. UnusedVbo) / sizeof(DIRENT);
  1652. RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  1653. StartIndexOfThisRun,
  1654. DirentsThisRun);
  1655. }
  1656. //
  1657. // If there weren't any DELETED entries, set the index to our current
  1658. // position.
  1659. //
  1660. if (DeletedHint == 0xffffffff) { DeletedHint = UnusedVbo; }
  1661. RxUnpinBcb( RxContext, Bcb );
  1662. Dcb->Specific.Dcb.UnusedDirentVbo = UnusedVbo;
  1663. Dcb->Specific.Dcb.DeletedDirentHint = DeletedHint;
  1664. return;
  1665. }
  1666. //
  1667. // Internal support routine
  1668. //
  1669. ULONG
  1670. RxDefragDirectory (
  1671. IN PRX_CONTEXT RxContext,
  1672. IN PDCB Dcb,
  1673. IN ULONG DirentsNeeded
  1674. )
  1675. /*++
  1676. Routine Description:
  1677. This routine determines if the requested number of dirents can be found
  1678. in the directory, looking for deleted dirents and orphaned LFNs. If the
  1679. request can be satisifed, orphaned LFNs are marked as deleted, and deleted
  1680. dirents are all grouped together at the end of the directory.
  1681. Note that this routine is currently used only on the root directory, but
  1682. it is completely general and could be used on any directory.
  1683. Arguments:
  1684. Dcb - Supplies the directory to defrag.
  1685. Return Value:
  1686. The Index of the first dirent available for use, or -1 if the
  1687. request cannot be satisfied.
  1688. --*/
  1689. {
  1690. ULONG SavedRxContextFlag;
  1691. PLIST_ENTRY Links;
  1692. ULONG ReturnValue;
  1693. PFCB Fcb;
  1694. PBCB Bcb = NULL;
  1695. PDIRENT Dirent = NULL;
  1696. UNICODE_STRING Lfn = {0,0,NULL};
  1697. MCB Mcb;
  1698. BOOLEAN McbInitialized = FALSE;
  1699. PUCHAR Directory;
  1700. PUCHAR UnusedDirents;
  1701. PUCHAR UnusedDirentBuffer = NULL;
  1702. PUCHAR UsedDirents;
  1703. PUCHAR UsedDirentBuffer = NULL;
  1704. PBCB *Bcbs = NULL;
  1705. ULONG Page;
  1706. ULONG PagesPinned;
  1707. ULONG DcbSize;
  1708. ULONG TotalBytesAllocated = 0;
  1709. PAGED_CODE();
  1710. //
  1711. // We assume we own the Vcb.
  1712. //
  1713. ASSERT( RxVcbAcquiredExclusive(RxContext, Dcb->Vcb) );
  1714. //
  1715. // We will only attempt this on directories less than 0x40000 bytes
  1716. // long (by default on DOS the root directory is only 0x2000 long).
  1717. // This is to avoid a cache manager complication.
  1718. //
  1719. DcbSize = Dcb->Header.AllocationSize.LowPart;
  1720. if (DcbSize > 0x40000) {
  1721. return (ULONG)-1;
  1722. }
  1723. //
  1724. // Force wait to TRUE
  1725. //
  1726. SavedRxContextFlag = RxContext->Flags;
  1727. SetFlag( RxContext->Flags,
  1728. RX_CONTEXT_FLAG_WAIT | RX_CONTEXT_FLAG_WRITE_THROUGH );
  1729. //
  1730. // Now acquire all open Fcbs in the Dcb exclusive.
  1731. //
  1732. for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
  1733. Links != &Dcb->Specific.Dcb.ParentDcbQueue;
  1734. Links = Links->Flink) {
  1735. Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
  1736. (VOID)ExAcquireResourceExclusive( Fcb->Header.Resource, TRUE );
  1737. }
  1738. try {
  1739. FOBX Fobx;
  1740. ULONG BytesUsed = 0;
  1741. ULONG QueryOffset = 0;
  1742. ULONG FoundOffset = 0;
  1743. RXSTATUS DontCare;
  1744. ULONG Run;
  1745. ULONG TotalRuns;
  1746. BOOLEAN Result;
  1747. PUCHAR Char;
  1748. //
  1749. // We are going to build a new, bitmap that will show all orphaned
  1750. // LFNs as well as deleted dirents as available.
  1751. //
  1752. // Initialize our local FOBX that will match all files.
  1753. //
  1754. RtlZeroMemory( &Fobx, sizeof(FOBX) );
  1755. Fobx.Flags = FOBX_FLAG_MATCH_ALL;
  1756. //
  1757. // Init the Long File Name string.
  1758. //
  1759. Lfn.MaximumLength = 260 * sizeof(WCHAR);
  1760. Lfn.Buffer = FsRtlAllocatePool( PagedPool, 260*sizeof(WCHAR) );
  1761. //
  1762. // Initalize the Mcb. We use this structure to keep track of runs
  1763. // of free and allocated dirents. Runs are identity allocations, and
  1764. // holes are free dirents.
  1765. //
  1766. FsRtlInitializeMcb( &Mcb, PagedPool );
  1767. McbInitialized = TRUE;
  1768. do {
  1769. RxLocateDirent( RxContext,
  1770. Dcb,
  1771. &Fobx,
  1772. QueryOffset,
  1773. &Dirent,
  1774. &Bcb,
  1775. &FoundOffset,
  1776. &Lfn );
  1777. if (Dirent != NULL) {
  1778. ULONG BytesUsed;
  1779. ULONG LfnByteOffset;
  1780. //
  1781. // Compute the LfnByteOffset.
  1782. //
  1783. LfnByteOffset = FoundOffset -
  1784. RDBSS_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT);
  1785. BytesUsed = FoundOffset - LfnByteOffset + sizeof(DIRENT);
  1786. //
  1787. // Set a run to represent all the dirent used for this
  1788. // file in the Dcb dir. We add 0x40000 to the LBN so that
  1789. // it will always to non-zero and thus not confused with a
  1790. // hole.
  1791. //
  1792. Result = FsRtlAddMcbEntry( &Mcb,
  1793. LfnByteOffset,
  1794. LfnByteOffset + 0x40000,
  1795. BytesUsed );
  1796. ASSERT( Result );
  1797. //
  1798. // Move on to the next dirent.
  1799. //
  1800. TotalBytesAllocated += BytesUsed;
  1801. QueryOffset = FoundOffset + sizeof(DIRENT);
  1802. }
  1803. } while ((Dirent != NULL) && (QueryOffset < DcbSize));
  1804. if (Bcb != NULL) {
  1805. RxUnpinBcb( RxContext, Bcb );
  1806. }
  1807. //
  1808. // If we need more dirents than are available, bail.
  1809. //
  1810. if (DirentsNeeded > (DcbSize - TotalBytesAllocated)/sizeof(DIRENT)) {
  1811. try_return(ReturnValue = (ULONG)-1);
  1812. }
  1813. //
  1814. // Now we are going to copy all the used and un-used parts of the
  1815. // directory to separate pool.
  1816. //
  1817. // Allocate these buffers and pin the entire directory.
  1818. //
  1819. UnusedDirents =
  1820. UnusedDirentBuffer = FsRtlAllocatePool( PagedPool,
  1821. DcbSize - TotalBytesAllocated );
  1822. UsedDirents =
  1823. UsedDirentBuffer = FsRtlAllocatePool( PagedPool,
  1824. TotalBytesAllocated );
  1825. PagesPinned = (DcbSize + (PAGE_SIZE - 1 )) / PAGE_SIZE;
  1826. Bcbs = FsRtlAllocatePool( PagedPool, PagesPinned * sizeof(PBCB) );
  1827. RtlZeroMemory( Bcbs, PagesPinned * sizeof(PBCB) );
  1828. for (Page = 0; Page < PagesPinned; Page += 1) {
  1829. ULONG PinSize;
  1830. //
  1831. // Don't try to pin beyond the Dcb size.
  1832. //
  1833. if ((Page + 1) * PAGE_SIZE > DcbSize) {
  1834. PinSize = DcbSize - (Page * PAGE_SIZE);
  1835. } else {
  1836. PinSize = PAGE_SIZE;
  1837. }
  1838. RxPrepareWriteDirectoryFile( RxContext,
  1839. Dcb,
  1840. Page * PAGE_SIZE,
  1841. PinSize,
  1842. &Bcbs[Page],
  1843. &Dirent,
  1844. FALSE,
  1845. &DontCare );
  1846. if (Page == 0) {
  1847. Directory = (PUCHAR)Dirent;
  1848. }
  1849. }
  1850. TotalRuns = FsRtlNumberOfRunsInMcb( &Mcb );
  1851. for (Run = 0; Run < TotalRuns; Run++) {
  1852. VBO Vbo;
  1853. LBO Lbo;
  1854. Result = FsRtlGetNextMcbEntry( &Mcb,
  1855. Run,
  1856. &Vbo,
  1857. &Lbo,
  1858. &BytesUsed );
  1859. ASSERT(Result);
  1860. //
  1861. // Copy each run to their specific pool.
  1862. //
  1863. if (Lbo != 0) {
  1864. RtlCopyMemory( UsedDirents,
  1865. Directory + Vbo,
  1866. BytesUsed );
  1867. UsedDirents += BytesUsed;
  1868. } else {
  1869. RtlCopyMemory( UnusedDirents,
  1870. Directory + Vbo,
  1871. BytesUsed );
  1872. UnusedDirents += BytesUsed;
  1873. }
  1874. }
  1875. //
  1876. // Marking all the un-used dirents as "deleted". This will reclaim
  1877. // storage used by orphaned LFNs.
  1878. //
  1879. for (Char = UnusedDirentBuffer; Char < UnusedDirents; Char += sizeof(DIRENT)) {
  1880. *Char = RDBSS_DIRENT_DELETED;
  1881. }
  1882. //
  1883. // Now, for the permanent step. Copy the two pool buffer back to the
  1884. // real Dcb directory, and flush the Dcb directory
  1885. //
  1886. ASSERT( TotalBytesAllocated == (ULONG)(UsedDirents - UsedDirentBuffer) );
  1887. RtlCopyMemory( Directory, UsedDirentBuffer, TotalBytesAllocated );
  1888. RtlCopyMemory( Directory + TotalBytesAllocated,
  1889. UnusedDirentBuffer,
  1890. UnusedDirents - UnusedDirentBuffer );
  1891. //
  1892. // We need to unpin here so that the UnpinRepinned won't deadlock.
  1893. //
  1894. if (Bcbs) {
  1895. for (Page = 0; Page < PagesPinned; Page += 1) {
  1896. RxUnpinBcb( RxContext, Bcbs[Page] );
  1897. }
  1898. ExFreePool(Bcbs);
  1899. Bcbs = NULL;
  1900. }
  1901. //
  1902. // Flush the directory to disk.
  1903. //
  1904. RxUnpinRepinnedBcbs( RxContext );
  1905. //
  1906. // OK, now nothing can go wrong. We have two more things to do.
  1907. // First, we have to fix up all the dirent offsets in any open Fcbs.
  1908. // If we cannot now find the Fcb, the file is marked invalid. Also,
  1909. // we skip deleted files.
  1910. //
  1911. for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
  1912. Links != &Dcb->Specific.Dcb.ParentDcbQueue;
  1913. Links = Links->Flink) {
  1914. PBCB TmpBcb = NULL;
  1915. ULONG TmpOffset;
  1916. PDIRENT TmpDirent = NULL;
  1917. ULONG PreviousLfnSpread;
  1918. Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
  1919. if (IsFileDeleted( RxContext, Fcb )) {
  1920. continue;
  1921. }
  1922. RxLocateSimpleOemDirent( RxContext,
  1923. Dcb,
  1924. &Fcb->ShortName.Name.Oem,
  1925. &TmpDirent,
  1926. &TmpBcb,
  1927. &TmpOffset );
  1928. if (TmpBcb == NULL) {
  1929. RxMarkFcbCondition( RxContext, Fcb, FcbBad );
  1930. } else {
  1931. RxUnpinBcb( RxContext, TmpBcb );
  1932. PreviousLfnSpread = Fcb->DirentOffsetWithinDirectory -
  1933. Fcb->LfnOffsetWithinDirectory;
  1934. Fcb->DirentOffsetWithinDirectory = TmpOffset;
  1935. Fcb->LfnOffsetWithinDirectory = TmpOffset - PreviousLfnSpread;
  1936. }
  1937. }
  1938. //
  1939. // Now, finally, make the free dirent bitmap reflect the new
  1940. // state of the Dcb directory.
  1941. //
  1942. RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  1943. 0,
  1944. TotalBytesAllocated / sizeof(DIRENT) );
  1945. RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap,
  1946. TotalBytesAllocated / sizeof(DIRENT),
  1947. (DcbSize - TotalBytesAllocated) / sizeof(DIRENT) );
  1948. ReturnValue = TotalBytesAllocated / sizeof(DIRENT);
  1949. try_exit: NOTHING;
  1950. } finally {
  1951. //
  1952. // Free all our resources and stuff.
  1953. //
  1954. if (McbInitialized) {
  1955. FsRtlUninitializeMcb( &Mcb );
  1956. }
  1957. if (Lfn.Buffer) {
  1958. ExFreePool( Lfn.Buffer );
  1959. }
  1960. if (UnusedDirentBuffer) {
  1961. ExFreePool( UnusedDirentBuffer );
  1962. }
  1963. if (UsedDirentBuffer) {
  1964. ExFreePool( UsedDirentBuffer );
  1965. }
  1966. if (Bcbs) {
  1967. for (Page = 0; Page < PagesPinned; Page += 1) {
  1968. RxUnpinBcb( RxContext, Bcbs[Page] );
  1969. }
  1970. ExFreePool(Bcbs);
  1971. }
  1972. for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink;
  1973. Links != &Dcb->Specific.Dcb.ParentDcbQueue;
  1974. Links = Links->Flink) {
  1975. Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
  1976. ExReleaseResource( Fcb->Header.Resource );
  1977. }
  1978. RxContext->Flags = SavedRxContextFlag;
  1979. }
  1980. //
  1981. // Now return the offset of the first free dirent to the caller.
  1982. //
  1983. return ReturnValue;
  1984. }