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.

3755 lines
97 KiB

  1. /*
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. idindex.c
  5. Abstract:
  6. This module contains the id index manipulation routines.
  7. Author:
  8. Jameel Hyder (microsoft!jameelh)
  9. Revision History:
  10. 25 Apr 1992 Initial Version
  11. 24 Feb 1993 SueA Fix AfpRenameDfEntry and AfpMoveDfEntry to invalidate
  12. the entire pathcache if the object of the move/rename
  13. is a directory that has children. This is faster
  14. than having to either search the path cache for
  15. paths that have the moved/renamed dir path as a prefix,
  16. or having to walk down the subtree below that dir
  17. and invalidate the cache for each item there.
  18. 05 Oct 1993 JameelH Performance Changes. Merge cached afpinfo into the
  19. idindex structure. Make both the ANSI and the UNICODE
  20. names part of idindex. Added EnumCache for improving
  21. enumerate perf.
  22. 05 Jun 1995 JameelH Remove the ANSI name from DFE. Also keep the files
  23. in the directory in multiple hash buckets instead
  24. of a single one. The hash buckets are also
  25. seperated into files and directories for faster
  26. lookup. The notify filtering is now moved to completion
  27. time and made over-all optimizations related to iddb.
  28. Notes: Tab stop: 4
  29. Directories and files that the AFP server has enumerated have AFP ids
  30. associated with them. These ids are DWORD and start with 1 (0 is invalid).
  31. Id 1 is reserved for the 'parent of the volume root' directory. Id 2 is
  32. reserved for the volume root directory. Id 3 is reserved for the Network
  33. Trash directory. Volumes that have no Network Trash will not use Id 3.
  34. These ids are per-volume and a database of ids are kept in memory in the
  35. form of a sibling tree which mirrors the part of the disk that the AFP
  36. server knows about (those files and dirs which have at some point been
  37. enumerated by a mac client). An index is also maintained for this database
  38. which is in the form of a sorted hashed index. The overflow hash links are
  39. sorted by AFP id in descending order. This is based on the idea that the
  40. most recently created items will be accessed most frequently (at least
  41. for writable volumes).
  42. --*/
  43. #define IDINDEX_LOCALS
  44. #define _IDDB_GLOBALS_
  45. #define FILENUM FILE_IDINDEX
  46. #include <afp.h>
  47. #include <scavengr.h>
  48. #include <fdparm.h>
  49. #include <pathmap.h>
  50. #include <afpinfo.h>
  51. #include <access.h> // for AfpWorldId
  52. #ifdef ALLOC_PRAGMA
  53. #pragma alloc_text(INIT, AfpDfeInit)
  54. #pragma alloc_text(PAGE, AfpDfeDeInit)
  55. #pragma alloc_text(PAGE, AfpFindDfEntryById)
  56. #pragma alloc_text(PAGE, AfpFindEntryByUnicodeName)
  57. #pragma alloc_text(PAGE, afpFindEntryByNtName)
  58. #pragma alloc_text(PAGE, AfpAddDfEntry)
  59. #pragma alloc_text(PAGE, AfpRenameDfEntry)
  60. #pragma alloc_text(PAGE, AfpMoveDfEntry)
  61. #pragma alloc_text(PAGE, AfpDeleteDfEntry)
  62. #pragma alloc_text(PAGE, AfpExchangeIdEntries)
  63. #pragma alloc_text(PAGE, AfpPruneIdDb)
  64. #pragma alloc_text(PAGE, AfpEnumerate)
  65. #pragma alloc_text(PAGE, AfpCatSearch)
  66. #pragma alloc_text(PAGE, afpPackSearchParms)
  67. #pragma alloc_text(PAGE, AfpSetDFFileFlags)
  68. #pragma alloc_text(PAGE, AfpCacheParentModTime)
  69. #pragma alloc_text(PAGE, afpAllocDfe)
  70. #pragma alloc_text(PAGE, afpFreeDfe)
  71. #pragma alloc_text(PAGE, AfpFreeIdIndexTables)
  72. #pragma alloc_text(PAGE, AfpInitIdDb)
  73. #pragma alloc_text(PAGE, afpSeedIdDb)
  74. #pragma alloc_text(PAGE, afpDfeBlockAge)
  75. #pragma alloc_text(PAGE, afpRenameInvalidWin32Name)
  76. #ifdef AGE_DFES
  77. #pragma alloc_text( PAGE, AfpAgeDfEntries)
  78. #endif
  79. #if DBG
  80. #pragma alloc_text( PAGE, afpDumpDfeTree)
  81. #pragma alloc_text( PAGE, afpDisplayDfe)
  82. #endif
  83. #endif
  84. /*** AfpDfeInit
  85. *
  86. * Initialize the Swmr for Dfe Block package and start the aging scavenger for it.
  87. */
  88. NTSTATUS
  89. AfpDfeInit(
  90. VOID
  91. )
  92. {
  93. NTSTATUS Status;
  94. // Initialize the DfeBlock Swmr
  95. AfpSwmrInitSwmr(&afpDfeBlockLock);
  96. #if DBG
  97. AfpScavengerScheduleEvent(afpDumpDfeTree,
  98. NULL,
  99. 2,
  100. True);
  101. #endif
  102. // Age out file and dir DFEs differently and seperately
  103. Status = AfpScavengerScheduleEvent(afpDfeBlockAge,
  104. afpDirDfeFreeBlockHead,
  105. DIR_BLOCK_AGE_TIME,
  106. True);
  107. if (NT_SUCCESS(Status))
  108. {
  109. // Age out file and dir DFEs differently and seperately
  110. Status = AfpScavengerScheduleEvent(afpDfeBlockAge,
  111. afpFileDfeFreeBlockHead,
  112. FILE_BLOCK_AGE_TIME,
  113. True);
  114. }
  115. return Status;
  116. }
  117. /*** AfpDfeDeInit
  118. *
  119. * Free any Dfe Blocks that have not yet been aged out.
  120. */
  121. VOID
  122. AfpDfeDeInit(
  123. VOID
  124. )
  125. {
  126. PDFEBLOCK pDfb;
  127. int i;
  128. ASSERT (afpDfeAllocCount == 0);
  129. for (i = 0; i < MAX_BLOCK_TYPE; i++)
  130. {
  131. ASSERT (afpDirDfePartialBlockHead[i] == NULL);
  132. ASSERT (afpDirDfeUsedBlockHead[i] == NULL);
  133. for (pDfb = afpDirDfeFreeBlockHead[i];
  134. pDfb != NULL;
  135. NOTHING)
  136. {
  137. PDFEBLOCK pFree;
  138. ASSERT(pDfb->dfb_NumFree == afpDfeNumDirBlocks[i]);
  139. pFree = pDfb;
  140. pDfb = pDfb->dfb_Next;
  141. AfpFreeVirtualMemoryPage(pFree);
  142. #if DBG
  143. afpDfbAllocCount --;
  144. #endif
  145. }
  146. ASSERT (afpFileDfePartialBlockHead[i] == NULL);
  147. ASSERT (afpFileDfeUsedBlockHead[i] == NULL);
  148. for (pDfb = afpFileDfeFreeBlockHead[i];
  149. pDfb != NULL;)
  150. {
  151. PDFEBLOCK pFree;
  152. ASSERT(pDfb->dfb_NumFree == afpDfeNumFileBlocks[i]);
  153. pFree = pDfb;
  154. pDfb = pDfb->dfb_Next;
  155. AfpFreeVirtualMemoryPage(pFree);
  156. #if DBG
  157. afpDfbAllocCount --;
  158. #endif
  159. }
  160. }
  161. ASSERT (afpDfbAllocCount == 0);
  162. }
  163. /*** AfpFindDfEntryById
  164. *
  165. * Search for an entity based on its AFP Id. returns a pointer to the entry
  166. * if found, else null.
  167. *
  168. * Callable from within the Fsp only. The caller should take Swmr lock for
  169. * READ.
  170. *
  171. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Shared)
  172. */
  173. PDFENTRY
  174. AfpFindDfEntryById(
  175. IN PVOLDESC pVolDesc,
  176. IN DWORD Id,
  177. IN DWORD EntityMask
  178. )
  179. {
  180. PDFENTRY pDfEntry;
  181. struct _DirFileEntry **DfeDirBucketStart;
  182. struct _DirFileEntry **DfeFileBucketStart;
  183. BOOLEAN Found = False;
  184. PAGED_CODE( );
  185. #ifdef PROFILING
  186. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_NumDfeLookupById);
  187. #endif
  188. if (Id == AFP_ID_ROOT)
  189. {
  190. Found = True;
  191. pDfEntry = pVolDesc->vds_pDfeRoot;
  192. ASSERT (VALID_DFE(pDfEntry));
  193. #ifdef PROFILING
  194. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_DfeCacheHits);
  195. #endif
  196. }
  197. else
  198. {
  199. pDfEntry = pVolDesc->vds_pDfeCache[HASH_CACHE_ID(Id)];
  200. if ((pDfEntry != NULL) && (pDfEntry->dfe_AfpId == Id))
  201. {
  202. Found = True;
  203. ASSERT (VALID_DFE(pDfEntry));
  204. #ifdef PROFILING
  205. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_DfeCacheHits);
  206. #endif
  207. }
  208. else
  209. {
  210. BOOLEAN retry = False;
  211. #ifdef PROFILING
  212. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_DfeCacheMisses);
  213. #endif
  214. DfeDirBucketStart = pVolDesc->vds_pDfeDirBucketStart;
  215. DfeFileBucketStart = pVolDesc->vds_pDfeFileBucketStart;
  216. if ((EntityMask == DFE_ANY) || (EntityMask == DFE_DIR))
  217. {
  218. if (EntityMask == DFE_ANY)
  219. retry = True;
  220. pDfEntry = DfeDirBucketStart[HASH_DIR_ID(Id,pVolDesc)];
  221. }
  222. else
  223. {
  224. pDfEntry = DfeFileBucketStart[HASH_FILE_ID(Id,pVolDesc)];
  225. }
  226. do
  227. {
  228. for (NOTHING;
  229. (pDfEntry != NULL) && (pDfEntry->dfe_AfpId >= Id);
  230. pDfEntry = pDfEntry->dfe_NextOverflow)
  231. {
  232. #ifdef PROFILING
  233. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_DfeDepthTraversed);
  234. #endif
  235. ASSERT(VALID_DFE(pDfEntry));
  236. if (pDfEntry->dfe_AfpId < Id)
  237. {
  238. break; // Did not find
  239. }
  240. if (pDfEntry->dfe_AfpId == Id)
  241. {
  242. pVolDesc->vds_pDfeCache[HASH_CACHE_ID(Id)] = pDfEntry;
  243. Found = True;
  244. break;
  245. }
  246. }
  247. if (Found)
  248. {
  249. break;
  250. }
  251. if (retry)
  252. {
  253. ASSERT(EntityMask == DFE_ANY);
  254. pDfEntry = DfeFileBucketStart[HASH_FILE_ID(Id,pVolDesc)];
  255. }
  256. retry ^= True;
  257. } while (!retry);
  258. }
  259. }
  260. if (Found)
  261. {
  262. afpValidateDFEType(pDfEntry, EntityMask);
  263. if (pDfEntry != NULL)
  264. {
  265. afpUpdateDfeAccessTime(pVolDesc, pDfEntry);
  266. }
  267. }
  268. else
  269. {
  270. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  271. ("AfpFindDfEntryById: Not found for id %lx, entity %d\n",
  272. Id, EntityMask));
  273. pDfEntry = NULL;
  274. }
  275. return pDfEntry;
  276. }
  277. /*** AfpFindEntryByUnicodeName
  278. *
  279. * Search for an entity based on a Unicode name and its parent dfentry.
  280. * Returns a pointer to the entry if found, else null. If lookup is by
  281. * longname, we just need to search the parent's children's names as
  282. * stored in the database. If lookup is by shortname, we first assume
  283. * that longname == shortname. If we don't find it in the database, we
  284. * must query the filesystem for the longname, then search again.
  285. *
  286. * Callable from within the Fsp only. The caller should take Swmr lock for
  287. * READ.
  288. *
  289. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Shared)
  290. */
  291. PDFENTRY
  292. AfpFindEntryByUnicodeName(
  293. IN PVOLDESC pVolDesc,
  294. IN PUNICODE_STRING pName,
  295. IN DWORD PathType, // short or long name
  296. IN PDFENTRY pDfeParent, // pointer to parent DFENTRY
  297. IN DWORD EntityMask // find a file,dir or either
  298. )
  299. {
  300. PDFENTRY pDfEntry;
  301. PAGED_CODE( );
  302. #ifdef PROFILING
  303. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_NumDfeLookupByName);
  304. #endif
  305. do
  306. {
  307. afpFindDFEByUnicodeNameInSiblingList(pVolDesc,
  308. pDfeParent,
  309. pName,
  310. &pDfEntry,
  311. EntityMask);
  312. if ((pDfEntry == NULL) && (PathType == AFP_SHORTNAME))
  313. {
  314. AFPSTATUS Status;
  315. FILESYSHANDLE hDir;
  316. UNICODE_STRING HostPath;
  317. UNICODE_STRING ULongName;
  318. WCHAR LongNameBuf[AFP_LONGNAME_LEN+1];
  319. // AFP does not allow use of the volume root shortname (IA p.13-13)
  320. if (DFE_IS_PARENT_OF_ROOT(pDfeParent))
  321. {
  322. pDfEntry = NULL;
  323. break;
  324. }
  325. AfpSetEmptyUnicodeString(&HostPath, 0, NULL);
  326. if (!DFE_IS_ROOT(pDfeParent))
  327. {
  328. // Get the volume relative path of the parent dir
  329. if (!NT_SUCCESS(AfpHostPathFromDFEntry(pDfeParent,
  330. 0,
  331. &HostPath)))
  332. {
  333. pDfEntry = NULL;
  334. break;
  335. }
  336. }
  337. // Open the parent directory
  338. hDir.fsh_FileHandle = NULL;
  339. Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  340. AFP_STREAM_DATA,
  341. FILEIO_OPEN_DIR,
  342. DFE_IS_ROOT(pDfeParent) ?
  343. &UNullString : &HostPath,
  344. FILEIO_ACCESS_READ,
  345. FILEIO_DENY_NONE,
  346. False,
  347. &hDir);
  348. if (HostPath.Buffer != NULL)
  349. AfpFreeMemory(HostPath.Buffer);
  350. if (!NT_SUCCESS(Status))
  351. {
  352. pDfEntry = NULL;
  353. break;
  354. }
  355. // get the LongName associated with this file/dir
  356. AfpSetEmptyUnicodeString(&ULongName, sizeof(LongNameBuf), LongNameBuf);
  357. Status = AfpIoQueryLongName(&hDir, pName, &ULongName);
  358. AfpIoClose(&hDir);
  359. if (!NT_SUCCESS(Status) ||
  360. EQUAL_UNICODE_STRING(&ULongName, pName, True))
  361. {
  362. pDfEntry = NULL;
  363. break;
  364. }
  365. afpFindDFEByUnicodeNameInSiblingList(pVolDesc,
  366. pDfeParent,
  367. &ULongName,
  368. &pDfEntry,
  369. EntityMask);
  370. } // end else if SHORTNAME
  371. } while (False);
  372. return pDfEntry;
  373. }
  374. /*** afpGetNextId
  375. *
  376. * Get the next assignable id for a file/directory. This is a seperate
  377. * routine so that AfpAddDfEntry can be paged. Only update the dirty bit
  378. * and LastModified time if no new id is assigned.
  379. *
  380. * LOCKS: vds_VolLock (SPIN)
  381. */
  382. LOCAL DWORD FASTCALL
  383. afpGetNextId(
  384. IN PVOLDESC pVolDesc
  385. )
  386. {
  387. KIRQL OldIrql;
  388. DWORD afpId;
  389. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  390. if (pVolDesc->vds_LastId == AFP_MAX_DIRID)
  391. {
  392. // errorlog the case where the assigned Id has wrapped around.
  393. // call product suppport and have them tell you to copy
  394. // all the files from one volume onto another volume FROM A MAC
  395. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  396. AFPLOG_ERROR(AFPSRVMSG_MAX_DIRID,
  397. STATUS_UNSUCCESSFUL,
  398. NULL,
  399. 0,
  400. &pVolDesc->vds_Name);
  401. return 0;
  402. }
  403. afpId = ++ pVolDesc->vds_LastId;
  404. pVolDesc->vds_Flags |= VOLUME_IDDBHDR_DIRTY;
  405. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  406. if (IS_VOLUME_NTFS(pVolDesc))
  407. {
  408. AfpVolumeSetModifiedTime(pVolDesc);
  409. }
  410. return afpId;
  411. }
  412. /*** afpFindEntryByNtName
  413. *
  414. * Search for an entity based on a Nt name (which could include names > 31
  415. * chars or shortnames) and its parent dfentry.
  416. * Returns a pointer to the entry if found, else null.
  417. *
  418. * If we don't find it in the database, we query the filesystem for the
  419. * longname (in the AFP sense), then search again based on this name.
  420. *
  421. * Callable from within the Fsp only. The caller should take Swmr lock for
  422. * READ.
  423. *
  424. * It has been determined that:
  425. * a, The name is longer than 31 chars OR
  426. * b, The name lookup in the IdDb has failed.
  427. *
  428. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Exclusive)
  429. */
  430. PDFENTRY
  431. afpFindEntryByNtName(
  432. IN PVOLDESC pVolDesc,
  433. IN PUNICODE_STRING pName,
  434. IN PDFENTRY pParentDfe // pointer to parent DFENTRY
  435. )
  436. {
  437. AFPSTATUS Status;
  438. WCHAR wbuf[AFP_LONGNAME_LEN+1];
  439. WCHAR HostPathBuf[BIG_PATH_LEN];
  440. UNICODE_STRING uLongName;
  441. UNICODE_STRING HostPath;
  442. FILESYSHANDLE hDir;
  443. PDFENTRY pDfEntry = NULL;
  444. PAGED_CODE( );
  445. ASSERT(pParentDfe != NULL);
  446. ASSERT(pName->Length > 0);
  447. do
  448. {
  449. AfpSetEmptyUnicodeString(&HostPath, sizeof(HostPathBuf), HostPathBuf);
  450. if (!DFE_IS_ROOT(pParentDfe))
  451. {
  452. // Get the volume relative path of the parent dir
  453. if (!NT_SUCCESS(AfpHostPathFromDFEntry(pParentDfe,
  454. 0,
  455. &HostPath)))
  456. {
  457. pDfEntry = NULL;
  458. break;
  459. }
  460. }
  461. // Open the parent directory
  462. // NOTE: We CANNOT use the vds_hRootDir handle to enumerate for this
  463. // purpose. We MUST open another handle to the root dir because
  464. // the FileName parameter will be ignored on all subsequent enumerates
  465. // on a handle. Therefore we must open a new handle for each
  466. // enumerate that we want to do for any directory. When the handle
  467. // is closed, the 'findfirst' will be cancelled, otherwise we would
  468. // always be enumerating on the wrong filename!
  469. hDir.fsh_FileHandle = NULL;
  470. Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  471. AFP_STREAM_DATA,
  472. FILEIO_OPEN_DIR,
  473. DFE_IS_ROOT(pParentDfe) ?
  474. &UNullString : &HostPath,
  475. FILEIO_ACCESS_NONE,
  476. FILEIO_DENY_NONE,
  477. False,
  478. &hDir);
  479. if (!NT_SUCCESS(Status))
  480. {
  481. pDfEntry = NULL;
  482. break;
  483. }
  484. // get the 'AFP longname' associated with this file/dir. If the
  485. // pName is longer than 31 chars, we will know it by its shortname,
  486. // so query for it's shortname (i.e. the 'AFP longname' we know it
  487. // by). If the name is shorter than 31 chars, since we know we
  488. // didn't find it in our database, then the pName must be the ntfs
  489. // shortname. Again, we need to Find the 'AFP longname' that we
  490. // know it by.
  491. AfpSetEmptyUnicodeString(&uLongName, sizeof(wbuf), wbuf);
  492. Status = AfpIoQueryLongName(&hDir, pName, &uLongName);
  493. AfpIoClose(&hDir);
  494. if (!NT_SUCCESS(Status) ||
  495. EQUAL_UNICODE_STRING(&uLongName, pName, True))
  496. {
  497. pDfEntry = NULL;
  498. if ((Status == STATUS_NO_MORE_FILES) ||
  499. (Status == STATUS_NO_SUCH_FILE))
  500. {
  501. // This file must have been deleted. Since we cannot
  502. // identify it in our database by the NT name that was
  503. // passed in, we must reenumerate the parent directory.
  504. // Anything we don't see on disk that we still have in
  505. // our database must have been deleted from disk, so get
  506. // rid of it in the database as well.
  507. // We must open a DIFFERENT handle to the parent dir since
  508. // we had already done an enumerate using that handle and
  509. // searching for a different name.
  510. hDir.fsh_FileHandle = NULL;
  511. Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  512. AFP_STREAM_DATA,
  513. FILEIO_OPEN_DIR,
  514. DFE_IS_ROOT(pParentDfe) ?
  515. &UNullString : &HostPath,
  516. FILEIO_ACCESS_NONE,
  517. FILEIO_DENY_NONE,
  518. False,
  519. &hDir);
  520. if (NT_SUCCESS(Status))
  521. {
  522. AfpCacheDirectoryTree(pVolDesc,
  523. pParentDfe,
  524. REENUMERATE,
  525. &hDir,
  526. NULL);
  527. AfpIoClose(&hDir);
  528. }
  529. }
  530. break;
  531. }
  532. afpFindDFEByUnicodeNameInSiblingList(pVolDesc,
  533. pParentDfe,
  534. &uLongName,
  535. &pDfEntry,
  536. DFE_ANY);
  537. } while (False);
  538. if ((HostPath.Buffer != NULL) && (HostPath.Buffer != HostPathBuf))
  539. AfpFreeMemory(HostPath.Buffer);
  540. return pDfEntry;
  541. }
  542. /*** afpFindEntryByNtPath
  543. *
  544. * Given a NT path relative to the volume root (which may contain names
  545. * > 31 chars or shortnames), look up the entry in the idindex DB.
  546. * If the Change Action is FILE_ACTION_ADDED, we want to lookup the entry
  547. * for the item's parent dir. Point the pParent and pTail strings into
  548. * the appropriate places in pPath.
  549. *
  550. * Called by the ProcessChangeNotify code when caching information in the DFE.
  551. *
  552. * LOCKS: vds_VolLock (SPIN)
  553. */
  554. PDFENTRY
  555. afpFindEntryByNtPath(
  556. IN PVOLDESC pVolDesc,
  557. IN DWORD ChangeAction, // if ADDED then lookup parent DFE
  558. IN PUNICODE_STRING pPath,
  559. OUT PUNICODE_STRING pParent,
  560. OUT PUNICODE_STRING pTail
  561. )
  562. {
  563. PDFENTRY pParentDfe, pDfEntry;
  564. PWSTR CurPtr, EndPtr;
  565. USHORT Len;
  566. BOOLEAN NewComp;
  567. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  568. ("afpFindEntryByNtPath: Entered for %Z\n", pPath));
  569. pParentDfe = pVolDesc->vds_pDfeRoot;
  570. ASSERT(pParentDfe != NULL);
  571. ASSERT(pPath->Length >= sizeof(WCHAR));
  572. ASSERT(pPath->Buffer[0] != L'\\');
  573. // Start off with Parent and Tail as both empty and modify as we go.
  574. AfpSetEmptyUnicodeString(pTail, 0, NULL);
  575. #if DBG
  576. AfpSetEmptyUnicodeString(pParent, 0, NULL); // Need it for the DBGPRINT down below
  577. #endif
  578. CurPtr = pPath->Buffer;
  579. EndPtr = (PWSTR)((PBYTE)CurPtr + pPath->Length);
  580. NewComp = True;
  581. for (Len = 0; CurPtr < EndPtr; CurPtr++)
  582. {
  583. if (NewComp)
  584. {
  585. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  586. ("afpFindEntryByNtPath: Parent DFE %lx, Old Parent %Z\n",
  587. pParentDfe, pParent));
  588. // The previous char seen was a path separator
  589. NewComp = False;
  590. *pParent = *pTail;
  591. pParent->Length =
  592. pParent->MaximumLength = Len;
  593. pTail->Length =
  594. pTail->MaximumLength = (USHORT)((PBYTE)EndPtr - (PBYTE)CurPtr);
  595. pTail->Buffer = CurPtr;
  596. Len = 0;
  597. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  598. ("afpFindEntryByNtPath: Current Parent %Z, tail %Z\n",
  599. pParent, pTail));
  600. if (pParent->Length > 0)
  601. {
  602. // Map this name to a DFE. Do the most common case here
  603. // If the name is <= AFP_LONGNAME_NAME, then check the
  604. // current parent's children, else go the long route.
  605. pDfEntry = NULL;
  606. //if (pParent->Length/sizeof(WCHAR) <= AFP_LONGNAME_LEN)
  607. if ((RtlUnicodeStringToAnsiSize(pParent)-1) <= AFP_LONGNAME_LEN)
  608. {
  609. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  610. ("afpFindEntryByNtPath: Looking for %Z in parent DFE %lx\n",
  611. pParent, pParentDfe));
  612. afpFindDFEByUnicodeNameInSiblingList(pVolDesc,
  613. pParentDfe,
  614. pParent,
  615. &pDfEntry,
  616. DFE_DIR);
  617. }
  618. if (pDfEntry == NULL)
  619. {
  620. pDfEntry = afpFindEntryByNtName(pVolDesc,
  621. pParent,
  622. pParentDfe);
  623. }
  624. if ((pParentDfe = pDfEntry) == NULL)
  625. {
  626. break;
  627. }
  628. }
  629. }
  630. if (*CurPtr == L'\\')
  631. {
  632. // We have encountered a path terminator
  633. NewComp = True;
  634. }
  635. else Len += sizeof(WCHAR);
  636. }
  637. // At this point we have pParentDfe & pParent pointing to the parent directory
  638. // and pTail pointing to the last component. If it is an add operation, we are
  639. // set, else map the last component to its Dfe
  640. if ((ChangeAction != FILE_ACTION_ADDED) && (pParentDfe != NULL))
  641. {
  642. pDfEntry = NULL;
  643. //if (pTail->Length/sizeof(WCHAR) <= AFP_LONGNAME_LEN)
  644. if ((RtlUnicodeStringToAnsiSize(pTail)-1) <= AFP_LONGNAME_LEN)
  645. {
  646. afpFindDFEByUnicodeNameInSiblingList(pVolDesc,
  647. pParentDfe,
  648. pTail,
  649. &pDfEntry,
  650. DFE_ANY);
  651. }
  652. if (pDfEntry == NULL)
  653. {
  654. BOOLEAN KeepLooking = True;
  655. //
  656. // We couldn't find this item in the database by the name
  657. // given, which means that we either know it by a different
  658. // name or it has been deleted, renamed or moved since.
  659. // If this is a modify change notify, then search for a
  660. // corresponding DELETED or RENAMED_OLD_NAME change that might
  661. // be in the changelist by this same name (so can do a fast
  662. // case sensitive search).
  663. //
  664. // This will speed up the case (avoid disk enumerates) where
  665. // there are a bunch of changes that we are trying to process
  666. // for an item, but it has since been deleted. It will prevent
  667. // us from re-enumerating the disk looking for the longname
  668. // and then also trying to prune out dead wood with a call to
  669. // AfpCacheDirectoryTree(REENUMERATE).
  670. //
  671. // This will pimp the case where a PC has made a change using
  672. // a different name than we know it by (and not deleted or
  673. // renamed the thing), but this case takes a back seat to the
  674. // other case that could happen when a mac app does a File-Save
  675. // doing a lot of writes followed by renames (or ExchangeFiles)
  676. // and deletes.
  677. //
  678. if ( (ChangeAction == FILE_ACTION_MODIFIED) ||
  679. (ChangeAction == FILE_ACTION_MODIFIED_STREAM) )
  680. {
  681. KIRQL OldIrql;
  682. PLIST_ENTRY pLink = &pVolDesc->vds_ChangeNotifyLookAhead;
  683. PVOL_NOTIFY pVolNotify;
  684. UNICODE_STRING UName;
  685. PFILE_NOTIFY_INFORMATION pFNInfo;
  686. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  687. while (pLink->Flink != &pVolDesc->vds_ChangeNotifyLookAhead)
  688. {
  689. pLink = pLink->Flink;
  690. pVolNotify = CONTAINING_RECORD(pLink, VOL_NOTIFY, vn_DelRenLink);
  691. pFNInfo = (PFILE_NOTIFY_INFORMATION) (pVolNotify + 1);
  692. AfpInitUnicodeStringWithNonNullTerm(&UName,
  693. (USHORT)pFNInfo->FileNameLength,
  694. pFNInfo->FileName);
  695. if (EQUAL_UNICODE_STRING_CS(pPath, &UName))
  696. {
  697. KeepLooking = False;
  698. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_WARN,
  699. ("afpFindEntryByNtPath: Found later REMOVE for %Z, Ignoring change\n", pPath));
  700. break;
  701. }
  702. }
  703. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  704. }
  705. if (KeepLooking)
  706. {
  707. pDfEntry = afpFindEntryByNtName(pVolDesc,
  708. pTail,
  709. pParentDfe);
  710. }
  711. }
  712. pParentDfe = pDfEntry;
  713. }
  714. // pParent is pointing to the parent component, we need the entire volume
  715. // relative path. Make it so. Do not bother if pParentDfe is NULL. Make
  716. // sure that we handle the case where there is only one component
  717. if (pParentDfe != NULL)
  718. {
  719. *pParent = *pPath;
  720. pParent->Length = pPath->Length - pTail->Length;
  721. if (pPath->Length > pTail->Length)
  722. pParent->Length -= sizeof(L'\\');
  723. }
  724. return pParentDfe;
  725. }
  726. /*** AfpAddDfEntry
  727. *
  728. * Triggerred by the creation of a file/directory or discovery of a file/dir
  729. * from an enumerate or pathmapping operation. If no AFP Id is supplied, a new
  730. * id is assigned to this entity. If an AFP Id is supplied (we know the Id
  731. * is within our current range and does not collide with any other entry), then
  732. * we use that Id. An entry is created and linked in to the database and hash
  733. * table. If this is an NTFS volume, the Id database header is marked
  734. * dirty if we assigned a new AFP Id, and the volume modification time is
  735. * updated. The hash table overflow entries are sorted in descending AFP Id
  736. * order.
  737. *
  738. * Callable from within the Fsp only. The caller should take Swmr lock for
  739. * WRITE.
  740. *
  741. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Exclusive)
  742. */
  743. PDFENTRY
  744. AfpAddDfEntry(
  745. IN PVOLDESC pVolDesc,
  746. IN PDFENTRY pDfeParent,
  747. IN PUNICODE_STRING pUName,
  748. IN BOOLEAN fDirectory,
  749. IN DWORD AfpId OPTIONAL
  750. )
  751. {
  752. PDFENTRY pDfEntry;
  753. BOOLEAN fSuccess;
  754. PAGED_CODE();
  755. ASSERT(DFE_IS_DIRECTORY(pDfeParent));
  756. do
  757. {
  758. if ((pDfEntry = ALLOC_DFE(USIZE_TO_INDEX(pUName->Length), fDirectory)) == NULL)
  759. {
  760. break;
  761. }
  762. pDfEntry->dfe_Flags = 0;
  763. if (!ARGUMENT_PRESENT((ULONG_PTR)AfpId))
  764. AfpId = afpGetNextId(pVolDesc);
  765. if (AfpId == 0)
  766. {
  767. // errorlog the case where the assigned Id has wrapped around.
  768. // call product suppport and have them tell you to copy
  769. // all the files from one volume onto another volume FROM A MAC
  770. //
  771. // NOTE: How about a utility which will re-assign new ids on
  772. // a volume after stopping the server ? A whole lot more
  773. // palatable idea.
  774. FREE_DFE(pDfEntry);
  775. pDfEntry = NULL;
  776. break;
  777. }
  778. pDfEntry->dfe_AfpId = AfpId;
  779. // Initialize its parent
  780. pDfEntry->dfe_Parent = pDfeParent;
  781. // Copy the name
  782. AfpCopyUnicodeString(&pDfEntry->dfe_UnicodeName,
  783. pUName);
  784. // And hash it
  785. afpHashUnicodeName(&pDfEntry->dfe_UnicodeName, &pDfEntry->dfe_NameHash);
  786. pDfEntry->dfe_NextOverflow = NULL;
  787. pDfEntry->dfe_NextSibling = NULL;
  788. // Now link this into the hash bucket, sorted in AFP Id descending order
  789. // and update the cache
  790. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  791. ("AfpAddDfEntry: Linking DFE %lx( Id %ld) for %Z into %s bucket %ld\n",
  792. pDfEntry, pDfEntry->dfe_AfpId, pUName,
  793. fDirectory ? "Dir" : "File",
  794. fDirectory ? HASH_DIR_ID(AfpId,pVolDesc) : HASH_FILE_ID(AfpId,pVolDesc)));
  795. if (fDirectory)
  796. {
  797. DFE_SET_DIRECTORY(pDfEntry, pDfeParent->dfe_DirDepth);
  798. }
  799. else
  800. {
  801. DFE_SET_FILE(pDfEntry);
  802. }
  803. afpInsertDFEInHashBucket(pVolDesc, pDfEntry, fDirectory, &fSuccess);
  804. if (!fSuccess)
  805. {
  806. /* Out of id space - bail out */
  807. FREE_DFE(pDfEntry);
  808. pDfEntry = NULL;
  809. break;
  810. }
  811. if (fDirectory)
  812. {
  813. if ((pDfeParent->dfe_DirOffspring == 0) && !EXCLUSIVE_VOLUME(pVolDesc))
  814. {
  815. DWORD requiredLen;
  816. // check to see if we need to reallocate a bigger notify buffer.
  817. // The buffer must be large enough to hold a rename
  818. // notification (which will contain 2 FILE_NOTIFY_INFORMATION
  819. // structures) for the deepest element in the directory tree.
  820. requiredLen = (((pDfEntry->dfe_DirDepth + 1) *
  821. ((AFP_FILENAME_LEN + 1) * sizeof(WCHAR))) +
  822. FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName)) * 2 ;
  823. if (requiredLen > pVolDesc->vds_RequiredNotifyBufLen)
  824. {
  825. pVolDesc->vds_RequiredNotifyBufLen = requiredLen;
  826. }
  827. }
  828. pDfeParent->dfe_DirOffspring ++;
  829. pDfEntry->dfe_DirOffspring = 0;
  830. pDfEntry->dfe_FileOffspring = 0;
  831. pVolDesc->vds_NumDirDfEntries ++;
  832. #ifdef AGE_DFES
  833. // These fields are relevant to directories only
  834. pDfEntry->dfe_pDirEntry->de_LastAccessTime = BEGINNING_OF_TIME;
  835. pDfEntry->dfe_pDirEntry->de_ChildForkOpenCount = 0;
  836. #endif
  837. ASSERT((FIELD_OFFSET(DIRENTRY, de_ChildFile) -
  838. FIELD_OFFSET(DIRENTRY, de_ChildDir)) == sizeof(PVOID));
  839. // Insert it into its sibling chain
  840. afpInsertDirDFEInSiblingList(pDfeParent, pDfEntry);
  841. }
  842. else
  843. {
  844. pDfeParent->dfe_FileOffspring ++;
  845. pDfEntry->dfe_DataLen = 0;
  846. pDfEntry->dfe_RescLen = 0;
  847. pVolDesc->vds_NumFileDfEntries ++;
  848. // Insert it into its sibling chain
  849. afpInsertFileDFEInSiblingList(pDfeParent, pDfEntry);
  850. }
  851. } while (False);
  852. return pDfEntry;
  853. }
  854. /*** AfpRenameDfEntry
  855. *
  856. * Triggered by a rename of a file/directory. If the new name is longer than
  857. * the current name, the DFEntry is freed and then reallocated to fit the new
  858. * name. A renamed file/dir must retain its original ID.
  859. *
  860. * Callable from within the Fsp only. The caller should take Swmr lock for
  861. * WRITE.
  862. *
  863. * LOCKS: vds_VolLock (SPIN) for updating the IdDb header.
  864. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Exclusive)
  865. * LOCK_ORDER: VolDesc lock after IdDb Swmr.
  866. *
  867. */
  868. PDFENTRY
  869. AfpRenameDfEntry(
  870. IN PVOLDESC pVolDesc,
  871. IN PDFENTRY pDfEntry,
  872. IN PUNICODE_STRING pNewName
  873. )
  874. {
  875. BOOLEAN fDirectory;
  876. PDFENTRY pNewDfEntry = pDfEntry;
  877. DWORD OldIndex, NewIndex;
  878. PAGED_CODE( );
  879. ASSERT((pDfEntry != NULL) && (pNewName != NULL) && (pVolDesc != NULL));
  880. do
  881. {
  882. fDirectory = DFE_IS_DIRECTORY(pDfEntry);
  883. OldIndex = USIZE_TO_INDEX(pDfEntry->dfe_UnicodeName.MaximumLength);
  884. NewIndex = USIZE_TO_INDEX(pNewName->Length);
  885. if (OldIndex != NewIndex)
  886. {
  887. if ((pNewDfEntry = ALLOC_DFE(NewIndex, fDirectory)) == NULL)
  888. {
  889. pNewDfEntry = NULL;
  890. break;
  891. }
  892. // Careful here how the structures are copied
  893. RtlCopyMemory(pNewDfEntry,
  894. pDfEntry,
  895. FIELD_OFFSET(DFENTRY, dfe_CopyUpto));
  896. // Update the cache
  897. pVolDesc->vds_pDfeCache[HASH_CACHE_ID(pDfEntry->dfe_AfpId)] = pNewDfEntry;
  898. // fix up the overflow links from the hash table
  899. AfpUnlinkDouble(pDfEntry,
  900. dfe_NextOverflow,
  901. dfe_PrevOverflow);
  902. if (pDfEntry->dfe_NextOverflow != NULL)
  903. {
  904. AfpInsertDoubleBefore(pNewDfEntry,
  905. pDfEntry->dfe_NextOverflow,
  906. dfe_NextOverflow,
  907. dfe_PrevOverflow);
  908. }
  909. else
  910. {
  911. *(pDfEntry->dfe_PrevOverflow) = pNewDfEntry;
  912. pNewDfEntry->dfe_NextOverflow = NULL;
  913. }
  914. // now fix any of this thing's children's parent pointers.
  915. if (fDirectory)
  916. {
  917. PDFENTRY pTmp;
  918. LONG i;
  919. // First copy the DirEntry structure
  920. if (fDirectory)
  921. {
  922. *pNewDfEntry->dfe_pDirEntry = *pDfEntry->dfe_pDirEntry;
  923. }
  924. // Start with Dir children
  925. if ((pTmp = pDfEntry->dfe_pDirEntry->de_ChildDir) != NULL)
  926. {
  927. // First fix up the first child's PrevSibling pointer
  928. pTmp->dfe_PrevSibling = &pNewDfEntry->dfe_pDirEntry->de_ChildDir;
  929. for (NOTHING;
  930. pTmp != NULL;
  931. pTmp = pTmp->dfe_NextSibling)
  932. {
  933. ASSERT(pTmp->dfe_Parent == pDfEntry);
  934. pTmp->dfe_Parent = pNewDfEntry;
  935. }
  936. }
  937. // Repeat for File childs as well
  938. for (i = 0; i < MAX_CHILD_HASH_BUCKETS; i++)
  939. {
  940. if ((pTmp = pDfEntry->dfe_pDirEntry->de_ChildFile[i]) != NULL)
  941. {
  942. // First fix up the first child's PrevSibling pointer
  943. pTmp->dfe_PrevSibling = &pNewDfEntry->dfe_pDirEntry->de_ChildFile[i];
  944. for (NOTHING;
  945. pTmp != NULL;
  946. pTmp = pTmp->dfe_NextSibling)
  947. {
  948. ASSERT(pTmp->dfe_Parent == pDfEntry);
  949. pTmp->dfe_Parent = pNewDfEntry;
  950. }
  951. }
  952. }
  953. }
  954. }
  955. // Now fix the sibling relationships. Note that this needs to be done
  956. // regardless of whether a new dfe was created since these depend on
  957. // name hash which has potentially changed
  958. AfpUnlinkDouble(pDfEntry,
  959. dfe_NextSibling,
  960. dfe_PrevSibling);
  961. // Copy the new unicode name and create a new hash
  962. AfpCopyUnicodeString(&pNewDfEntry->dfe_UnicodeName,
  963. pNewName);
  964. afpHashUnicodeName(&pNewDfEntry->dfe_UnicodeName, &pNewDfEntry->dfe_NameHash);
  965. // Insert it into its sibling chain
  966. afpInsertDFEInSiblingList(pNewDfEntry->dfe_Parent, pNewDfEntry, fDirectory);
  967. if (pDfEntry != pNewDfEntry)
  968. FREE_DFE(pDfEntry);
  969. AfpVolumeSetModifiedTime(pVolDesc);
  970. } while (False);
  971. return pNewDfEntry;
  972. }
  973. /*** AfpMoveDfEntry
  974. *
  975. * Triggered by a move/rename-move of a file/dir. A moved entity must retain
  976. * its AfpId.
  977. *
  978. * Callable from within the Fsp only. The caller should take Swmr lock for
  979. * WRITE.
  980. *
  981. * LOCKS: vds_VolLock (SPIN) for updating the IdDb header.
  982. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Exclusive)
  983. * LOCK_ORDER: VolDesc lock after IdDb Swmr.
  984. *
  985. */
  986. PDFENTRY
  987. AfpMoveDfEntry(
  988. IN PVOLDESC pVolDesc,
  989. IN PDFENTRY pDfEntry,
  990. IN PDFENTRY pNewParentDFE,
  991. IN PUNICODE_STRING pNewName OPTIONAL
  992. )
  993. {
  994. SHORT depthDelta; // This must be signed
  995. BOOLEAN fDirectory;
  996. PAGED_CODE( );
  997. ASSERT((pDfEntry != NULL) && (pNewParentDFE != NULL) && (pVolDesc != NULL));
  998. // do we need to rename the DFEntry ?
  999. if (ARGUMENT_PRESENT(pNewName) &&
  1000. !EQUAL_UNICODE_STRING(pNewName, &pDfEntry->dfe_UnicodeName, True))
  1001. {
  1002. if ((pDfEntry = AfpRenameDfEntry(pVolDesc,
  1003. pDfEntry,
  1004. pNewName)) == NULL)
  1005. {
  1006. return NULL;
  1007. }
  1008. }
  1009. if (pDfEntry->dfe_Parent != pNewParentDFE)
  1010. {
  1011. // unlink the current entry from its parent/sibling associations (but not
  1012. // the overflow hash bucket list since the AfpId has not changed. The
  1013. // children of this entity being moved (if its a dir and it has any) will
  1014. // remain intact, and move along with the dir)
  1015. AfpUnlinkDouble(pDfEntry, dfe_NextSibling, dfe_PrevSibling);
  1016. fDirectory = DFE_IS_DIRECTORY(pDfEntry);
  1017. // Decrement the old parent's offspring count & increment the new parent
  1018. if (fDirectory)
  1019. {
  1020. ASSERT(pDfEntry->dfe_Parent->dfe_DirOffspring > 0);
  1021. pDfEntry->dfe_Parent->dfe_DirOffspring --;
  1022. pNewParentDFE->dfe_DirOffspring ++;
  1023. // insert it into the new parent's child list
  1024. afpInsertDirDFEInSiblingList(pNewParentDFE, pDfEntry);
  1025. }
  1026. else
  1027. {
  1028. ASSERT(pDfEntry->dfe_Parent->dfe_FileOffspring > 0);
  1029. pDfEntry->dfe_Parent->dfe_FileOffspring --;
  1030. pNewParentDFE->dfe_FileOffspring ++;
  1031. #ifdef AGE_DFES
  1032. if (IS_VOLUME_AGING_DFES(pVolDesc))
  1033. {
  1034. if (pDfEntry->dfe_Flags & DFE_FLAGS_R_ALREADYOPEN)
  1035. {
  1036. pDfEntry->dfe_Parent->dfe_pDirEntry->de_ChildForkOpenCount --;
  1037. pNewParentDFE->dfe_pDirEntry->de_ChildForkOpenCount ++;
  1038. }
  1039. if (pDfEntry->dfe_Flags & DFE_FLAGS_D_ALREADYOPEN)
  1040. {
  1041. pDfEntry->dfe_Parent->dfe_pDirEntry->de_ChildForkOpenCount --;
  1042. pNewParentDFE->dfe_pDirEntry->de_ChildForkOpenCount ++;
  1043. }
  1044. }
  1045. #endif
  1046. // insert it into the new parent's child list
  1047. afpInsertFileDFEInSiblingList(pNewParentDFE, pDfEntry);
  1048. }
  1049. pDfEntry->dfe_Parent = pNewParentDFE;
  1050. // If we moved a directory, we must adjust the directory depths of the
  1051. // directory, and all directories below it
  1052. if (fDirectory &&
  1053. ((depthDelta = (pNewParentDFE->dfe_DirDepth + 1 - pDfEntry->dfe_DirDepth)) != 0))
  1054. {
  1055. PDFENTRY pTmp = pDfEntry;
  1056. while (True)
  1057. {
  1058. if ((pTmp->dfe_pDirEntry->de_ChildDir != NULL) &&
  1059. (pTmp->dfe_DirDepth != (pTmp->dfe_Parent->dfe_DirDepth + 1)))
  1060. {
  1061. ASSERT(DFE_IS_DIRECTORY(pTmp));
  1062. pTmp->dfe_DirDepth += depthDelta;
  1063. pTmp = pTmp->dfe_pDirEntry->de_ChildDir;
  1064. }
  1065. else
  1066. {
  1067. ASSERT(DFE_IS_DIRECTORY(pTmp));
  1068. if ((pTmp->dfe_DirDepth != pTmp->dfe_Parent->dfe_DirDepth + 1))
  1069. pTmp->dfe_DirDepth += depthDelta;
  1070. if (pTmp == pDfEntry)
  1071. break;
  1072. else if (pTmp->dfe_NextSibling != NULL)
  1073. pTmp = pTmp->dfe_NextSibling;
  1074. else pTmp = pTmp->dfe_Parent;
  1075. }
  1076. }
  1077. }
  1078. }
  1079. AfpVolumeSetModifiedTime(pVolDesc);
  1080. return pDfEntry;
  1081. }
  1082. /*** AfpDeleteDfEntry
  1083. *
  1084. * Trigerred by the deletion of a file/directory. The entry as well as the
  1085. * index is unlinked and freed. If we are deleting a directory that is not
  1086. * empty, the entire directory tree underneath is deleted as well. Note when
  1087. * implementing FPDelete, always attempt the delete from the actual file system
  1088. * first, then delete from the IdDB if that succeeds.
  1089. *
  1090. * Callable from within the Fsp only. The caller should take Swmr lock for
  1091. * WRITE.
  1092. *
  1093. * LOCKS: vds_VolLock (SPIN) for updating the IdDb header.
  1094. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Exclusive)
  1095. * LOCK_ORDER: VolDesc lock after IdDb Swmr.
  1096. */
  1097. VOID FASTCALL
  1098. AfpDeleteDfEntry(
  1099. IN PVOLDESC pVolDesc,
  1100. IN PDFENTRY pDfEntry
  1101. )
  1102. {
  1103. PDFENTRY pDfeParent = pDfEntry->dfe_Parent;
  1104. LONG i;
  1105. BOOLEAN Prune = False;
  1106. PAGED_CODE( );
  1107. ASSERT(pDfeParent != NULL);
  1108. if (DFE_IS_DIRECTORY(pDfEntry))
  1109. {
  1110. for (i = 0; i < MAX_CHILD_HASH_BUCKETS; i++)
  1111. {
  1112. if (pDfEntry->dfe_pDirEntry->de_ChildFile[i] != NULL)
  1113. {
  1114. Prune = True;
  1115. break;
  1116. }
  1117. }
  1118. if ((pDfEntry->dfe_pDirEntry->de_ChildDir != NULL) || Prune)
  1119. {
  1120. // This will happen if a PC user deletes a tree behind our back
  1121. AfpPruneIdDb(pVolDesc, pDfEntry);
  1122. }
  1123. ASSERT(pDfeParent->dfe_DirOffspring > 0);
  1124. pDfeParent->dfe_DirOffspring --;
  1125. }
  1126. else
  1127. {
  1128. ASSERT(pDfeParent->dfe_FileOffspring > 0);
  1129. pDfeParent->dfe_FileOffspring --;
  1130. // The Finder is bad about deleting APPL mappings (it deletes
  1131. // the file before deleting the APPL mapping so always gets
  1132. // ObjectNotFound error for RemoveAPPL, and leaves turd mappings).
  1133. if (pDfEntry->dfe_FinderInfo.fd_TypeD == *(PDWORD)"APPL")
  1134. {
  1135. AfpRemoveAppl(pVolDesc,
  1136. pDfEntry->dfe_FinderInfo.fd_CreatorD,
  1137. pDfEntry->dfe_AfpId);
  1138. }
  1139. }
  1140. // Unlink it now from the hash table
  1141. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1142. ("AfpDeleteDfEntry: Unlinking from the hash table\n") );
  1143. AfpUnlinkDouble(pDfEntry,
  1144. dfe_NextOverflow,
  1145. dfe_PrevOverflow);
  1146. // Make sure we get rid of the cache if valid
  1147. if (pVolDesc->vds_pDfeCache[HASH_CACHE_ID(pDfEntry->dfe_AfpId)] == pDfEntry)
  1148. pVolDesc->vds_pDfeCache[HASH_CACHE_ID(pDfEntry->dfe_AfpId)] = NULL;
  1149. // Seperate it now from its siblings
  1150. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1151. ("AfpDeleteDfEntry: Unlinking from the sibling list\n") );
  1152. AfpUnlinkDouble(pDfEntry,
  1153. dfe_NextSibling,
  1154. dfe_PrevSibling);
  1155. (DFE_IS_DIRECTORY(pDfEntry)) ?
  1156. pVolDesc->vds_NumDirDfEntries -- :
  1157. pVolDesc->vds_NumFileDfEntries --;
  1158. FREE_DFE(pDfEntry);
  1159. AfpVolumeSetModifiedTime(pVolDesc);
  1160. }
  1161. /*** AfpPruneIdDb
  1162. *
  1163. * Lops off a branch of the IdDb. Called by network trash code when
  1164. * cleaning out the trash directory, or by directory enumerate code that
  1165. * has discovered a directory has been 'delnoded' by a PC user. The
  1166. * IdDb sibling tree is traversed, and each node under the pDfeTarget node
  1167. * is deleted from the database and freed. pDfeTarget itself is NOT
  1168. * deleted. If necessary, the caller should delete the target itself.
  1169. *
  1170. * Callable from within the Fsp only. The caller should take Swmr lock for
  1171. * WRITE.
  1172. *
  1173. * LOCKS: vds_VolLock (SPIN) for updating the IdDb header.
  1174. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Exclusive)
  1175. * LOCK_ORDER: VolDesc lock after IdDb Swmr.
  1176. */
  1177. VOID FASTCALL
  1178. AfpPruneIdDb(
  1179. IN PVOLDESC pVolDesc,
  1180. IN PDFENTRY pDfeTarget
  1181. )
  1182. {
  1183. PDFENTRY pCurDfe = pDfeTarget, pDelDfe;
  1184. LONG i = 0;
  1185. PAGED_CODE( );
  1186. ASSERT((pVolDesc != NULL) && (pDfeTarget != NULL) &&
  1187. (pDfeTarget->dfe_Flags & DFE_FLAGS_DIR));
  1188. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1189. ("AfpPruneIdDb entered...\n") );
  1190. while (True)
  1191. {
  1192. ASSERT(DFE_IS_DIRECTORY(pCurDfe));
  1193. // Delete all the file children of this node first
  1194. for (i = 0; i < MAX_CHILD_HASH_BUCKETS; i++)
  1195. {
  1196. while ((pDelDfe = pCurDfe->dfe_pDirEntry->de_ChildFile[i]) != NULL)
  1197. {
  1198. AfpDeleteDfEntry(pVolDesc, pDelDfe);
  1199. }
  1200. }
  1201. if (pCurDfe->dfe_pDirEntry->de_ChildDir != NULL)
  1202. {
  1203. pCurDfe = pCurDfe->dfe_pDirEntry->de_ChildDir;
  1204. }
  1205. else if (pCurDfe == pDfeTarget)
  1206. {
  1207. return;
  1208. }
  1209. else if (pCurDfe->dfe_NextSibling != NULL)
  1210. {
  1211. pDelDfe = pCurDfe;
  1212. pCurDfe = pCurDfe->dfe_NextSibling;
  1213. AfpDeleteDfEntry(pVolDesc, pDelDfe);
  1214. }
  1215. else
  1216. {
  1217. pDelDfe = pCurDfe;
  1218. pCurDfe = pCurDfe->dfe_Parent;
  1219. AfpDeleteDfEntry(pVolDesc, pDelDfe);
  1220. }
  1221. }
  1222. }
  1223. /*** AfpExchangeIdEntries
  1224. *
  1225. * Called by AfpExchangeFiles api.
  1226. *
  1227. * Callable from within the Fsp only. The caller should take Swmr lock for
  1228. * WRITE.
  1229. *
  1230. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Exclusive)
  1231. */
  1232. VOID
  1233. AfpExchangeIdEntries(
  1234. IN PVOLDESC pVolDesc,
  1235. IN DWORD AfpId1,
  1236. IN DWORD AfpId2
  1237. )
  1238. {
  1239. PDFENTRY pDFE1, pDFE2;
  1240. DFENTRY DFEtemp;
  1241. PAGED_CODE( );
  1242. pDFE1 = AfpFindDfEntryById(pVolDesc, AfpId1, DFE_FILE);
  1243. ASSERT(pDFE1 != NULL);
  1244. pDFE2 = AfpFindDfEntryById(pVolDesc, AfpId2, DFE_FILE);
  1245. ASSERT(pDFE2 != NULL);
  1246. // a customer hit this problem on NT4 where one of the Dfe's was NULL!
  1247. if (pDFE1 == NULL || pDFE2 == NULL)
  1248. {
  1249. ASSERT(0);
  1250. return;
  1251. }
  1252. DFEtemp = *pDFE2;
  1253. pDFE2->dfe_Flags = pDFE1->dfe_Flags;
  1254. pDFE2->dfe_BackupTime = pDFE1->dfe_BackupTime;
  1255. pDFE2->dfe_LastModTime = pDFE1->dfe_LastModTime;
  1256. pDFE2->dfe_DataLen = pDFE1->dfe_DataLen;
  1257. pDFE2->dfe_RescLen = pDFE1->dfe_RescLen;
  1258. pDFE2->dfe_NtAttr = pDFE1->dfe_NtAttr;
  1259. pDFE2->dfe_AfpAttr = pDFE1->dfe_AfpAttr;
  1260. pDFE1->dfe_Flags = DFEtemp.dfe_Flags;
  1261. pDFE1->dfe_BackupTime = DFEtemp.dfe_BackupTime;
  1262. pDFE1->dfe_LastModTime = DFEtemp.dfe_LastModTime;
  1263. pDFE1->dfe_DataLen = DFEtemp.dfe_DataLen;
  1264. pDFE1->dfe_RescLen = DFEtemp.dfe_RescLen;
  1265. pDFE1->dfe_NtAttr = DFEtemp.dfe_NtAttr;
  1266. pDFE1->dfe_AfpAttr = DFEtemp.dfe_AfpAttr;
  1267. }
  1268. /*** AfpEnumerate
  1269. *
  1270. * Enumerates files and dirs in a directory using the IdDb.
  1271. * An array of ENUMDIR structures is returned which represent
  1272. * the enumerated files and dirs.
  1273. *
  1274. * Short Names
  1275. * ProDos Info
  1276. * Offspring count
  1277. * Permissions/Owner Id/Group Id
  1278. *
  1279. * LOCKS: vds_idDbAccessLock (SWMR, Shared)
  1280. *
  1281. */
  1282. AFPSTATUS
  1283. AfpEnumerate(
  1284. IN PCONNDESC pConnDesc,
  1285. IN DWORD ParentDirId,
  1286. IN PANSI_STRING pPath,
  1287. IN DWORD BitmapF,
  1288. IN DWORD BitmapD,
  1289. IN BYTE PathType,
  1290. IN DWORD DFFlags,
  1291. OUT PENUMDIR * ppEnumDir
  1292. )
  1293. {
  1294. PENUMDIR pEnumDir;
  1295. PDFENTRY pDfe, pTmp;
  1296. PEIT pEit;
  1297. AFPSTATUS Status;
  1298. PATHMAPENTITY PME;
  1299. BOOLEAN NeedHandle = False;
  1300. FILEDIRPARM FDParm;
  1301. PVOLDESC pVolDesc = pConnDesc->cds_pVolDesc;
  1302. LONG EnumCount;
  1303. BOOLEAN ReleaseSwmr = False, NeedWriteLock = False;
  1304. PAGED_CODE( );
  1305. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1306. ("AfpEnumerate Entered\n"));
  1307. do
  1308. {
  1309. // Check if this enumeration matches the current enumeration
  1310. if ((pEnumDir = pConnDesc->cds_pEnumDir) != NULL)
  1311. {
  1312. if ((pEnumDir->ed_ParentDirId == ParentDirId) &&
  1313. (pEnumDir->ed_PathType == PathType) &&
  1314. (pEnumDir->ed_TimeStamp >= pVolDesc->vds_ModifiedTime) &&
  1315. (pEnumDir->ed_Bitmap == (BitmapF + (BitmapD << 16))) &&
  1316. (((pPath->Length == 0) && (pEnumDir->ed_PathName.Length == 0)) ||
  1317. RtlCompareMemory(pEnumDir->ed_PathName.Buffer,
  1318. pPath->Buffer,
  1319. pPath->Length)))
  1320. {
  1321. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1322. ("AfpEnumerate found cache hit\n"));
  1323. INTERLOCKED_INCREMENT_LONG(&AfpServerStatistics.stat_EnumCacheHits);
  1324. *ppEnumDir = pEnumDir;
  1325. Status = AFP_ERR_NONE;
  1326. break;
  1327. }
  1328. // Does not match, cleanup the previous entry
  1329. AfpFreeMemory(pEnumDir);
  1330. pConnDesc->cds_pEnumDir = NULL;
  1331. }
  1332. INTERLOCKED_INCREMENT_LONG(&AfpServerStatistics.stat_EnumCacheMisses);
  1333. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1334. ("AfpEnumerate creating new cache\n"));
  1335. // We have no current enumeration. Create one now
  1336. *ppEnumDir = NULL;
  1337. AfpInitializeFDParms(&FDParm);
  1338. AfpInitializePME(&PME, 0, NULL);
  1339. if (IS_VOLUME_NTFS(pVolDesc))
  1340. {
  1341. NeedHandle = True;
  1342. }
  1343. Status = AfpMapAfpPathForLookup(pConnDesc,
  1344. ParentDirId,
  1345. pPath,
  1346. PathType,
  1347. DFE_DIR,
  1348. DIR_BITMAP_DIRID |
  1349. DIR_BITMAP_GROUPID |
  1350. DIR_BITMAP_OWNERID |
  1351. DIR_BITMAP_ACCESSRIGHTS |
  1352. FD_INTERNAL_BITMAP_OPENACCESS_READCTRL |
  1353. DIR_BITMAP_OFFSPRINGS,
  1354. NeedHandle ? &PME : NULL,
  1355. &FDParm);
  1356. if (Status != AFP_ERR_NONE)
  1357. {
  1358. if (Status == AFP_ERR_OBJECT_NOT_FOUND)
  1359. Status = AFP_ERR_DIR_NOT_FOUND;
  1360. break;
  1361. }
  1362. if (NeedHandle)
  1363. {
  1364. AfpIoClose(&PME.pme_Handle);
  1365. }
  1366. // For admin, set all access bits
  1367. if (pConnDesc->cds_pSda->sda_ClientType == SDA_CLIENT_ADMIN)
  1368. {
  1369. FDParm._fdp_UserRights = DIR_ACCESS_ALL | DIR_ACCESS_OWNER;
  1370. }
  1371. if ((BitmapF != 0) && (FDParm._fdp_UserRights & DIR_ACCESS_READ))
  1372. DFFlags |= DFE_FILE;
  1373. if ((BitmapD != 0) && (FDParm._fdp_UserRights & DIR_ACCESS_SEARCH))
  1374. DFFlags |= DFE_DIR;
  1375. // Catch access denied error here
  1376. if (DFFlags == 0)
  1377. {
  1378. Status = AFP_ERR_ACCESS_DENIED;
  1379. break;
  1380. }
  1381. // All is hunky-dory so far, go ahead with the enumeration now
  1382. #ifdef GET_CORRECT_OFFSPRING_COUNTS
  1383. take_swmr_for_enum:
  1384. #endif
  1385. NeedWriteLock ?
  1386. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock) :
  1387. AfpSwmrAcquireShared(&pVolDesc->vds_IdDbAccessLock);
  1388. ReleaseSwmr = True;
  1389. // Lookup the dfentry of the AfpIdEnumDir
  1390. if ((pDfe = AfpFindDfEntryById(pVolDesc,
  1391. FDParm._fdp_AfpId,
  1392. DFE_DIR)) == NULL)
  1393. {
  1394. Status = AFP_ERR_OBJECT_NOT_FOUND;
  1395. break;
  1396. }
  1397. // Allocate a ENUMDIR structure and initialize it
  1398. EnumCount = 0;
  1399. if (DFFlags & DFE_DIR)
  1400. EnumCount += (DWORD)(pDfe->dfe_DirOffspring);
  1401. if (DFFlags & DFE_FILE)
  1402. EnumCount += (DWORD)(pDfe->dfe_FileOffspring);
  1403. if (EnumCount == 0)
  1404. {
  1405. Status = AFP_ERR_OBJECT_NOT_FOUND;
  1406. break;
  1407. }
  1408. if ((pEnumDir = (PENUMDIR)AfpAllocNonPagedMemory(sizeof(ENUMDIR) +
  1409. pPath->MaximumLength +
  1410. EnumCount*sizeof(EIT))) == NULL)
  1411. {
  1412. Status = AFP_ERR_OBJECT_NOT_FOUND;
  1413. break;
  1414. }
  1415. pEnumDir->ed_ParentDirId = ParentDirId;
  1416. pEnumDir->ed_ChildCount = EnumCount;
  1417. pEnumDir->ed_PathType = PathType;
  1418. pEnumDir->ed_Bitmap = (BitmapF + (BitmapD << 16));
  1419. pEnumDir->ed_BadCount = 0;
  1420. pEnumDir->ed_pEit = pEit = (PEIT)((PBYTE)pEnumDir + sizeof(ENUMDIR));
  1421. AfpSetEmptyAnsiString(&pEnumDir->ed_PathName,
  1422. pPath->MaximumLength,
  1423. (PBYTE)pEnumDir +
  1424. sizeof(ENUMDIR) +
  1425. EnumCount*sizeof(EIT));
  1426. RtlCopyMemory(pEnumDir->ed_PathName.Buffer,
  1427. pPath->Buffer,
  1428. pPath->Length);
  1429. *ppEnumDir = pConnDesc->cds_pEnumDir = pEnumDir;
  1430. // Now copy the enum parameters (Afp Id and file/dir flag) of
  1431. // each of the children, files first
  1432. if (DFFlags & DFE_FILE)
  1433. {
  1434. LONG i;
  1435. for (i = 0; i < MAX_CHILD_HASH_BUCKETS; i++)
  1436. {
  1437. for (pTmp = pDfe->dfe_pDirEntry->de_ChildFile[i];
  1438. pTmp != NULL;
  1439. pTmp = pTmp->dfe_NextSibling, pEit ++)
  1440. {
  1441. ASSERT(!DFE_IS_DIRECTORY(pTmp));
  1442. pEit->eit_Id = pTmp->dfe_AfpId;
  1443. pEit->eit_Flags = DFE_FILE;
  1444. }
  1445. }
  1446. }
  1447. if (DFFlags & DFE_DIR)
  1448. {
  1449. for (pTmp = pDfe->dfe_pDirEntry->de_ChildDir;
  1450. pTmp != NULL;
  1451. pTmp = pTmp->dfe_NextSibling, pEit ++)
  1452. {
  1453. ASSERT(DFE_IS_DIRECTORY(pTmp));
  1454. pEit->eit_Id = pTmp->dfe_AfpId;
  1455. pEit->eit_Flags = DFE_DIR;
  1456. #ifdef GET_CORRECT_OFFSPRING_COUNTS
  1457. // We are returning a directory offspring, make sure
  1458. // that it has its children cached in so we get the correct
  1459. // file and dir offspring counts for it, otherwise Finder
  1460. // 'view by name' doesn't work correctly because it sees
  1461. // zero as the offspring count and clicking on the triangle
  1462. // shows nothing since it tries to be smart and doesn't
  1463. // explicitly enumerate that dir if offspring count is zero.
  1464. //
  1465. // This can be a big performance hit if a directory has lots
  1466. // of subdirectories which in turn have tons of files.
  1467. //
  1468. // JH - Could we alternately return incorrect information about
  1469. // files as long as there are directry children. What else
  1470. // will break ?
  1471. // if (!DFE_CHILDREN_ARE_PRESENT(pTmp) && (pTmp->dfe_DirOffspring == 0))
  1472. if (!DFE_CHILDREN_ARE_PRESENT(pTmp))
  1473. {
  1474. if (!AfpSwmrLockedExclusive(&pVolDesc->vds_IdDbAccessLock) &&
  1475. !AfpSwmrUpgradeToExclusive(&pVolDesc->vds_IdDbAccessLock))
  1476. {
  1477. NeedWriteLock = True;
  1478. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  1479. ReleaseSwmr = False;
  1480. // We must free the memory here in case the next
  1481. // time we enumerate the dir it has more children
  1482. // than it had the first time -- since we must let go
  1483. // of the swmr here things could change.
  1484. AfpFreeMemory(pEnumDir);
  1485. *ppEnumDir = pConnDesc->cds_pEnumDir = NULL;
  1486. goto take_swmr_for_enum;
  1487. }
  1488. AfpCacheDirectoryTree(pVolDesc,
  1489. pTmp,
  1490. GETFILES,
  1491. NULL,
  1492. NULL);
  1493. } // if children not cached
  1494. #endif
  1495. }
  1496. }
  1497. AfpGetCurrentTimeInMacFormat(&pEnumDir->ed_TimeStamp);
  1498. Status = AFP_ERR_NONE;
  1499. } while (False);
  1500. if (ReleaseSwmr)
  1501. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  1502. return Status;
  1503. }
  1504. /*** AfpCatSearch
  1505. *
  1506. * This routine does a left-hand search on the DFE tree to search for
  1507. * file/dirs that match the search criteria indicated in pFDParm1 and
  1508. * pFDParm2.
  1509. *
  1510. * LOCKS: vds_idDbAccessLock (SWMR, Shared or Exclusive)
  1511. */
  1512. AFPSTATUS
  1513. AfpCatSearch(
  1514. IN PCONNDESC pConnDesc,
  1515. IN PCATALOGPOSITION pCatPosition,
  1516. IN DWORD Bitmap,
  1517. IN DWORD FileBitmap,
  1518. IN DWORD DirBitmap,
  1519. IN PFILEDIRPARM pFDParm1,
  1520. IN PFILEDIRPARM pFDParm2,
  1521. IN PUNICODE_STRING pMatchString OPTIONAL,
  1522. IN OUT PDWORD pCount,
  1523. IN SHORT Buflen,
  1524. OUT PSHORT pSizeLeft,
  1525. OUT PBYTE pResults,
  1526. OUT PCATALOGPOSITION pNewCatPosition
  1527. )
  1528. {
  1529. PVOLDESC pVolDesc = pConnDesc->cds_pVolDesc;
  1530. PDFENTRY pCurParent, pCurFile;
  1531. BOOLEAN MatchFiles = True, MatchDirs = True, NewSearch = False;
  1532. BOOLEAN HaveSeeFiles, HaveSeeFolders, CheckAccess = False;
  1533. AFPSTATUS Status = AFP_ERR_NONE;
  1534. LONG i;
  1535. DWORD ActCount = 0;
  1536. SHORT SizeLeft = Buflen;
  1537. PSWMR pSwmr = &(pConnDesc->cds_pVolDesc->vds_IdDbAccessLock);
  1538. USHORT Flags;
  1539. UNICODE_STRING CurPath;
  1540. typedef struct _SearchEntityPkt
  1541. {
  1542. BYTE __Length;
  1543. BYTE __FileDirFlag;
  1544. // The real parameters follow
  1545. } SEP, *PSEP;
  1546. PSEP pSep;
  1547. PAGED_CODE( );
  1548. pSep = (PSEP)pResults;
  1549. RtlZeroMemory(pNewCatPosition, sizeof(CATALOGPOSITION));
  1550. CatSearchStart:
  1551. Flags = pCatPosition->cp_Flags;
  1552. pCurFile = NULL;
  1553. i = MAX_CHILD_HASH_BUCKETS;
  1554. if (Flags & CATFLAGS_WRITELOCK_REQUIRED)
  1555. {
  1556. ASSERT(Flags == (CATFLAGS_SEARCHING_FILES | CATFLAGS_WRITELOCK_REQUIRED));
  1557. AfpSwmrAcquireExclusive(pSwmr);
  1558. Flags &= ~CATFLAGS_WRITELOCK_REQUIRED;
  1559. }
  1560. else
  1561. AfpSwmrAcquireShared(pSwmr);
  1562. if (Flags == 0)
  1563. {
  1564. //
  1565. // Start search from beginning of catalog (i.e. the root directory)
  1566. //
  1567. i = 0;
  1568. pCurParent = pVolDesc->vds_pDfeRoot;
  1569. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1570. if (IS_VOLUME_NTFS(pVolDesc))
  1571. CheckAccess = True;
  1572. Flags = CATFLAGS_SEARCHING_FILES;
  1573. NewSearch = True;
  1574. }
  1575. else
  1576. {
  1577. //
  1578. // This is a continuation of a previous search, pickup where we
  1579. // left off
  1580. //
  1581. AFPTIME CurrentTime;
  1582. AfpGetCurrentTimeInMacFormat(&CurrentTime);
  1583. // If we cannot find the current parent dir specified by this
  1584. // catalog position, or too much time has elapsed since the
  1585. // user last sent in this catalog position, then restart the search
  1586. // from the root dir. The reason we have a time limitation is that
  1587. // if someone made a CatSearch request N minutes ago, and the
  1588. // current position is deep in the tree, the directory permissions
  1589. // higher up in the tree could have changed by now so that the user
  1590. // shouldn't even have access to this part of the tree anymore.
  1591. // Since we do move up in the tree without rechecking permissions,
  1592. // this could happen. (We assume that if we got down to the current
  1593. // position in the tree that we had to have had access higher up
  1594. // in order to get here, so moving up is ok. But if somebody comes
  1595. // back a day later and continues the catsearch where it left off,
  1596. // we shouldn't let them.) It is too expensive to be rechecking
  1597. // parents' parent permissions everytime we move back up the tree.
  1598. if (((CurrentTime - pCatPosition->cp_TimeStamp) >= MAX_CATSEARCH_TIME) ||
  1599. ((pCurParent = AfpFindDfEntryById(pVolDesc,
  1600. pCatPosition->cp_CurParentId,
  1601. DFE_DIR)) == NULL))
  1602. {
  1603. // Start over from root directory
  1604. Status = AFP_ERR_CATALOG_CHANGED;
  1605. DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_WARN,
  1606. ("AfpCatSearch: Time diff >= MAX_CATSEARCH_TIME or couldn't find CurParent Id!\n"));
  1607. pCurParent = pVolDesc->vds_pDfeRoot;
  1608. Flags = CATFLAGS_SEARCHING_FILES;
  1609. pSep = (PSEP)pResults;
  1610. Status = AFP_ERR_NONE;
  1611. MatchFiles = True;
  1612. MatchDirs = True;
  1613. SizeLeft = Buflen;
  1614. ActCount = 0;
  1615. if (IS_VOLUME_NTFS(pVolDesc))
  1616. CheckAccess = True;
  1617. NewSearch = True;
  1618. }
  1619. else if (pCatPosition->cp_TimeStamp < pVolDesc->vds_ModifiedTime)
  1620. {
  1621. Status = AFP_ERR_CATALOG_CHANGED;
  1622. ASSERT(IS_VOLUME_NTFS(pVolDesc));
  1623. DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_WARN,
  1624. ("AfpCatSearch: Catalog timestamp older than IdDb Modtime\n"));
  1625. }
  1626. ASSERT(DFE_IS_DIRECTORY(pCurParent));
  1627. // If we need to resume searching the files for this parent, find the
  1628. // one we should start with, if it is not the first file child.
  1629. if (Flags & CATFLAGS_SEARCHING_FILES)
  1630. {
  1631. //
  1632. // Default is to start with parent's first child which
  1633. // may or may not be null depending on if the parent has had
  1634. // its file children cached in or not. If we are restarting a
  1635. // search because we had to let go of the IdDb SWMR in order to
  1636. // reaquire for Exclusive access, this parent's children could
  1637. // very well have been cached in by someone else in the mean time.
  1638. // If so then we will pick it up here.
  1639. //
  1640. i = 0;
  1641. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1642. if (pCatPosition->cp_NextFileId != 0)
  1643. {
  1644. // Find the DFE corresponding to the next fileID to look at
  1645. if (((pCurFile = AfpFindDfEntryById(pVolDesc,
  1646. pCatPosition->cp_NextFileId,
  1647. DFE_FILE)) == NULL) ||
  1648. (pCurFile->dfe_Parent != pCurParent))
  1649. {
  1650. // If we can't find the file that was specified, start over
  1651. // with this parent's first file child and indicate there may
  1652. // be duplicates returned or files missed
  1653. Status = AFP_ERR_CATALOG_CHANGED;
  1654. DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_WARN,
  1655. ("AfpCatSearch: Could not find file Child ID!\n"));
  1656. i = 0;
  1657. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1658. }
  1659. else
  1660. {
  1661. i = (pCurFile->dfe_NameHash % MAX_CHILD_HASH_BUCKETS);
  1662. }
  1663. }
  1664. }
  1665. }
  1666. if (pFDParm1->_fdp_Flags == DFE_FLAGS_FILE_WITH_ID)
  1667. MatchDirs = False;
  1668. else if (pFDParm1->_fdp_Flags == DFE_FLAGS_DIR)
  1669. MatchFiles = False;
  1670. if (NewSearch && MatchDirs)
  1671. {
  1672. SHORT Length;
  1673. ASSERT (DFE_IS_ROOT(pCurParent));
  1674. // See if the volume root itself is a match
  1675. if ((Length = AfpIsCatSearchMatch(pCurParent,
  1676. Bitmap,
  1677. DirBitmap,
  1678. pFDParm1,
  1679. pFDParm2,
  1680. pMatchString)) != 0)
  1681. {
  1682. ASSERT(Length <= SizeLeft);
  1683. PUTSHORT2BYTE(&pSep->__Length, Length - sizeof(SEP));
  1684. pSep->__FileDirFlag = FILEDIR_FLAG_DIR;
  1685. afpPackSearchParms(pCurParent,
  1686. DirBitmap,
  1687. (PBYTE)pSep + sizeof(SEP));
  1688. pSep = (PSEP)((PBYTE)pSep + Length);
  1689. SizeLeft -= Length;
  1690. ASSERT(SizeLeft >= 0);
  1691. ActCount ++;
  1692. }
  1693. }
  1694. NewSearch = False;
  1695. while (True)
  1696. {
  1697. HaveSeeFiles = HaveSeeFolders = True;
  1698. //
  1699. // First time thru, if we are resuming a search and need to start
  1700. // with the pCurParent's sibling, then do so.
  1701. //
  1702. if (Flags & CATFLAGS_SEARCHING_SIBLING)
  1703. {
  1704. Flags &= ~CATFLAGS_SEARCHING_SIBLING;
  1705. goto check_sibling;
  1706. }
  1707. //
  1708. // If we have not searched this directory yet and this is NTFS, check
  1709. // that user has seefiles/seefolders access in this directory
  1710. //
  1711. if (CheckAccess)
  1712. {
  1713. BYTE UserRights;
  1714. NTSTATUS PermStatus;
  1715. ASSERT(IS_VOLUME_NTFS(pVolDesc));
  1716. AfpSetEmptyUnicodeString(&CurPath, 0, NULL);
  1717. // Get the root relative path of this directory
  1718. if (NT_SUCCESS(AfpHostPathFromDFEntry(pCurParent,
  1719. 0,
  1720. &CurPath)))
  1721. {
  1722. // Check for SeeFiles/SeeFolders which is the most common case
  1723. if (!NT_SUCCESS((PermStatus = AfpCheckParentPermissions(pConnDesc,
  1724. pCurParent->dfe_AfpId,
  1725. &CurPath,
  1726. DIR_ACCESS_READ | DIR_ACCESS_SEARCH,
  1727. NULL,
  1728. &UserRights))))
  1729. {
  1730. if (PermStatus == AFP_ERR_ACCESS_DENIED)
  1731. {
  1732. if ((UserRights & DIR_ACCESS_READ) == 0)
  1733. HaveSeeFiles = False;
  1734. if ((UserRights & DIR_ACCESS_SEARCH) == 0)
  1735. HaveSeeFolders = False;
  1736. }
  1737. else
  1738. HaveSeeFiles = HaveSeeFolders = False;
  1739. }
  1740. if (CurPath.Buffer != NULL)
  1741. AfpFreeMemory(CurPath.Buffer);
  1742. }
  1743. else
  1744. {
  1745. HaveSeeFiles = HaveSeeFolders = False;
  1746. DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_ERR,
  1747. ("AfpCatSearch: Could not get host path from DFE!!\n"));
  1748. }
  1749. CheckAccess = False;
  1750. }
  1751. // Search the files first if have seefiles access on the current
  1752. // parent and the user has asked for file matches. If we are
  1753. // resuming a search by looking at a directory child first, don't look
  1754. // at the files.
  1755. if (HaveSeeFiles && MatchFiles && (Flags & CATFLAGS_SEARCHING_FILES))
  1756. {
  1757. PDFENTRY pDFE;
  1758. SHORT Length;
  1759. AFPSTATUS subStatus = AFP_ERR_NONE, subsubStatus = AFP_ERR_NONE;
  1760. if (!DFE_CHILDREN_ARE_PRESENT(pCurParent))
  1761. {
  1762. if (!AfpSwmrLockedExclusive(pSwmr) &&
  1763. !AfpSwmrUpgradeToExclusive(pSwmr))
  1764. {
  1765. if (ActCount > 0)
  1766. {
  1767. // We have at least one thing to return to the user,
  1768. // so return it now and set the flag for next time
  1769. // to take the write lock.
  1770. pNewCatPosition->cp_NextFileId = 0;
  1771. Flags |= CATFLAGS_WRITELOCK_REQUIRED;
  1772. break; // out of while loop
  1773. }
  1774. else
  1775. {
  1776. // Let go of lock and reaquire it for exclusive
  1777. // access. Start over where we left off here if
  1778. // possible. Put a new timestamp in the catalog
  1779. // position so if it changes between the time we let
  1780. // go of the lock and reaquire it for exclusive access,
  1781. // we will return AFP_ERR_CATALOG_CHANGED since
  1782. // something could change while we don't own the lock.
  1783. AfpSwmrRelease(pSwmr);
  1784. pCatPosition->cp_Flags = CATFLAGS_WRITELOCK_REQUIRED |
  1785. CATFLAGS_SEARCHING_FILES;
  1786. pCatPosition->cp_CurParentId = pCurParent->dfe_AfpId;
  1787. pCatPosition->cp_NextFileId = 0;
  1788. AfpGetCurrentTimeInMacFormat(&pCatPosition->cp_TimeStamp);
  1789. DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO,
  1790. ("AfpCatSearch: Lock released; reaquiring Exclusive\n"));
  1791. goto CatSearchStart;
  1792. }
  1793. }
  1794. AfpCacheDirectoryTree(pVolDesc,
  1795. pCurParent,
  1796. GETFILES,
  1797. NULL,
  1798. NULL);
  1799. i = 0;
  1800. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1801. // If we have the exclusive lock, downgrade it to shared so
  1802. // we don't lock out others who want to read.
  1803. if (AfpSwmrLockedExclusive(pSwmr))
  1804. AfpSwmrDowngradeToShared(pSwmr);
  1805. }
  1806. //
  1807. // Search files for matches. If we are picking up in the middle
  1808. // of searching the files, then start with the right one as pointed
  1809. // at by pCurFile.
  1810. //
  1811. while (TRUE)
  1812. {
  1813. while (pCurFile == NULL)
  1814. {
  1815. i ++;
  1816. if (i < MAX_CHILD_HASH_BUCKETS)
  1817. {
  1818. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[i];
  1819. }
  1820. else
  1821. {
  1822. subsubStatus = STATUS_NO_MORE_FILES;
  1823. break; // out of while (pCurFile == NULL)
  1824. }
  1825. }
  1826. if (subsubStatus != AFP_ERR_NONE)
  1827. {
  1828. break;
  1829. }
  1830. ASSERT(pCurFile->dfe_Parent == pCurParent);
  1831. if ((Length = AfpIsCatSearchMatch(pCurFile,
  1832. Bitmap,
  1833. FileBitmap,
  1834. pFDParm1,
  1835. pFDParm2,
  1836. pMatchString)) != 0)
  1837. {
  1838. // Add this to the output buffer if there is room
  1839. if ((Length <= SizeLeft) && (ActCount < *pCount))
  1840. {
  1841. PUTSHORT2BYTE(&pSep->__Length, Length - sizeof(SEP));
  1842. pSep->__FileDirFlag = FILEDIR_FLAG_FILE;
  1843. afpPackSearchParms(pCurFile,
  1844. FileBitmap,
  1845. (PBYTE)pSep + sizeof(SEP));
  1846. pSep = (PSEP)((PBYTE)pSep + Length);
  1847. SizeLeft -= Length;
  1848. ASSERT(SizeLeft >= 0);
  1849. ActCount ++;
  1850. }
  1851. else
  1852. {
  1853. // We don't have enough room to return this entry, or
  1854. // we already have found the requested count. So this
  1855. // will be where we pick up from on the next search
  1856. pNewCatPosition->cp_NextFileId = pCurFile->dfe_AfpId;
  1857. subStatus = STATUS_BUFFER_OVERFLOW;
  1858. break;
  1859. }
  1860. }
  1861. pCurFile = pCurFile->dfe_NextSibling;
  1862. }
  1863. if (subStatus != AFP_ERR_NONE)
  1864. {
  1865. break; // out of while loop
  1866. }
  1867. Flags = 0;
  1868. }
  1869. // If have seefolders on curparent and curparent has a dir child,
  1870. // Move down the tree to the parent's first directory branch
  1871. if (HaveSeeFolders && (pCurParent->dfe_pDirEntry->de_ChildDir != NULL))
  1872. {
  1873. SHORT Length;
  1874. // If user has asked for directory matches, try the parent's
  1875. // first directory child as a match
  1876. if (MatchDirs &&
  1877. ((Length = AfpIsCatSearchMatch(pCurParent->dfe_pDirEntry->de_ChildDir,
  1878. Bitmap,
  1879. DirBitmap,
  1880. pFDParm1,
  1881. pFDParm2,
  1882. pMatchString)) != 0))
  1883. {
  1884. // Add this to the output buffer if there is room
  1885. if ((Length <= SizeLeft) && (ActCount < *pCount))
  1886. {
  1887. PUTSHORT2BYTE(&pSep->__Length, Length - sizeof(SEP));
  1888. pSep->__FileDirFlag = FILEDIR_FLAG_DIR;
  1889. afpPackSearchParms(pCurParent->dfe_pDirEntry->de_ChildDir,
  1890. DirBitmap,
  1891. (PBYTE)pSep + sizeof(SEP));
  1892. pSep = (PSEP)((PBYTE)pSep + Length);
  1893. SizeLeft -= Length;
  1894. ASSERT(SizeLeft >= 0);
  1895. ActCount ++;
  1896. }
  1897. else
  1898. {
  1899. // We don't have enough room to return this entry, so
  1900. // it will be where we pick up from on the next search
  1901. Flags = CATFLAGS_SEARCHING_DIRCHILD;
  1902. break;
  1903. }
  1904. }
  1905. // Make the current parent's first dir child the new pCurParent
  1906. // and continue the search from there.
  1907. pCurParent = pCurParent->dfe_pDirEntry->de_ChildDir;
  1908. if (IS_VOLUME_NTFS(pVolDesc))
  1909. CheckAccess = True;
  1910. Flags = CATFLAGS_SEARCHING_FILES;
  1911. i = 0;
  1912. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1913. continue;
  1914. }
  1915. // We either don't have the access rights to go into any directories
  1916. // under this parent, or the current parent did not have any directory
  1917. // children. See if it has any siblings. We know we have access to
  1918. // see this level of siblings since we are at this level in the first
  1919. // place.
  1920. check_sibling:
  1921. if (pCurParent->dfe_NextSibling != NULL)
  1922. {
  1923. SHORT Length;
  1924. // If user has asked for directory matches, try the parent's
  1925. // next sibling as a match
  1926. if (MatchDirs &&
  1927. ((Length = AfpIsCatSearchMatch(pCurParent->dfe_NextSibling,
  1928. Bitmap,
  1929. DirBitmap,
  1930. pFDParm1,
  1931. pFDParm2,
  1932. pMatchString)) != 0))
  1933. {
  1934. // Add this to the output buffer if there is room
  1935. if ((Length <= SizeLeft) && (ActCount < *pCount))
  1936. {
  1937. PUTSHORT2BYTE(&pSep->__Length, Length - sizeof(SEP));
  1938. pSep->__FileDirFlag = FILEDIR_FLAG_DIR;
  1939. afpPackSearchParms(pCurParent->dfe_NextSibling,
  1940. DirBitmap,
  1941. (PBYTE)pSep + sizeof(SEP));
  1942. pSep = (PSEP)((PBYTE)pSep + Length);
  1943. SizeLeft -= Length;
  1944. ASSERT(SizeLeft >= 0);
  1945. ActCount ++;
  1946. }
  1947. else
  1948. {
  1949. // We don't have enough room to return this entry, so
  1950. // it will be where we pick up from on the next search
  1951. Flags = CATFLAGS_SEARCHING_SIBLING;
  1952. break;
  1953. }
  1954. }
  1955. // Make the current parent's next sibling the new pCurParent and
  1956. // continue the search from there
  1957. pCurParent = pCurParent->dfe_NextSibling;
  1958. if (IS_VOLUME_NTFS(pVolDesc))
  1959. CheckAccess = True;
  1960. Flags = CATFLAGS_SEARCHING_FILES;
  1961. i = 0;
  1962. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1963. continue;
  1964. }
  1965. // When we hit the root directory again we have searched everything.
  1966. if (DFE_IS_ROOT(pCurParent))
  1967. {
  1968. Status = AFP_ERR_EOF;
  1969. break;
  1970. }
  1971. // Move back up the tree and see if the parent has a sibling to
  1972. // traverse. If not, then it will come back here and move up
  1973. // the tree again till it finds a node with a sibling or hits
  1974. // the root.
  1975. pCurParent = pCurParent->dfe_Parent;
  1976. goto check_sibling;
  1977. }
  1978. if ((Status == AFP_ERR_NONE) || (Status == AFP_ERR_CATALOG_CHANGED) ||
  1979. (Status == AFP_ERR_EOF))
  1980. {
  1981. // return the current catalog position and number of items returned
  1982. if (Status != AFP_ERR_EOF)
  1983. {
  1984. ASSERT(Flags != 0);
  1985. ASSERT(ActCount > 0);
  1986. pNewCatPosition->cp_Flags = Flags;
  1987. pNewCatPosition->cp_CurParentId = pCurParent->dfe_AfpId;
  1988. AfpGetCurrentTimeInMacFormat(&pNewCatPosition->cp_TimeStamp);
  1989. }
  1990. *pCount = ActCount;
  1991. ASSERT(SizeLeft >= 0);
  1992. *pSizeLeft = SizeLeft;
  1993. }
  1994. AfpSwmrRelease(pSwmr);
  1995. return Status;
  1996. }
  1997. /*** afpPackSearchParms
  1998. *
  1999. *
  2000. * LOCKS_ASSUMED: vds_IdDbAccessLock (Shared or Exclusive)
  2001. */
  2002. VOID
  2003. afpPackSearchParms(
  2004. IN PDFENTRY pDfe,
  2005. IN DWORD Bitmap,
  2006. IN PBYTE pBuf
  2007. )
  2008. {
  2009. DWORD Offset = 0;
  2010. ANSI_STRING AName;
  2011. BYTE NameBuf[AFP_LONGNAME_LEN+1];
  2012. PAGED_CODE( );
  2013. RtlZeroMemory (NameBuf, AFP_LONGNAME_LEN+1);
  2014. if (Bitmap & FD_BITMAP_PARENT_DIRID)
  2015. {
  2016. PUTDWORD2DWORD(pBuf, pDfe->dfe_Parent->dfe_AfpId);
  2017. Offset += sizeof(DWORD);
  2018. }
  2019. if (Bitmap & FD_BITMAP_LONGNAME)
  2020. {
  2021. PUTDWORD2SHORT(pBuf + Offset, Offset + sizeof(USHORT));
  2022. Offset += sizeof(USHORT);
  2023. #ifndef DBCS
  2024. // 1996.09.26 V-HIDEKK
  2025. PUTSHORT2BYTE(pBuf + Offset, pDfe->dfe_UnicodeName.Length/sizeof(WCHAR));
  2026. #endif
  2027. AfpInitAnsiStringWithNonNullTerm(&AName, sizeof(NameBuf), NameBuf);
  2028. AfpConvertMungedUnicodeToAnsi(&pDfe->dfe_UnicodeName,
  2029. &AName);
  2030. #ifdef DBCS
  2031. // FiX #11992 SFM: As a result of search, I get incorrect file information.
  2032. // 1996.09.26 V-HIDEKK
  2033. PUTSHORT2BYTE(pBuf + Offset, AName.Length);
  2034. #endif
  2035. RtlCopyMemory(pBuf + Offset + sizeof(BYTE),
  2036. NameBuf,
  2037. AName.Length);
  2038. #ifdef DBCS
  2039. // FiX #11992 SFM: As a result of search, I get incorrect file information.
  2040. // 1996.09.26 V-HIDEKK
  2041. Offset += sizeof(BYTE) + AName.Length;
  2042. #else
  2043. Offset += sizeof(BYTE) + pDfe->dfe_UnicodeName.Length/sizeof(WCHAR);
  2044. #endif
  2045. }
  2046. if (Offset & 1)
  2047. *(pBuf + Offset) = 0;
  2048. }
  2049. /*** AfpSetDFFileFlags
  2050. *
  2051. * Set or clear the DAlreadyOpen or RAlreadyOpen flags for a DFEntry of type
  2052. * File, or mark the file as having a FileId assigned.
  2053. *
  2054. * LOCKS: vds_idDbAccessLock (SWMR, Exclusive)
  2055. */
  2056. AFPSTATUS
  2057. AfpSetDFFileFlags(
  2058. IN PVOLDESC pVolDesc,
  2059. IN DWORD AfpId,
  2060. IN DWORD Flags OPTIONAL,
  2061. IN BOOLEAN SetFileId,
  2062. IN BOOLEAN ClrFileId
  2063. )
  2064. {
  2065. PDFENTRY pDfeFile;
  2066. AFPSTATUS Status = AFP_ERR_NONE;
  2067. PAGED_CODE( );
  2068. ASSERT(!(SetFileId | ClrFileId) || (SetFileId ^ ClrFileId));
  2069. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
  2070. pDfeFile = AfpFindDfEntryById(pVolDesc, AfpId, DFE_FILE);
  2071. if (pDfeFile != NULL)
  2072. {
  2073. #ifdef AGE_DFES
  2074. if (IS_VOLUME_AGING_DFES(pVolDesc))
  2075. {
  2076. if (Flags)
  2077. {
  2078. pDfeFile->dfe_Parent->dfe_pDirEntry->de_ChildForkOpenCount ++;
  2079. }
  2080. }
  2081. #endif
  2082. pDfeFile->dfe_Flags |= (Flags & DFE_FLAGS_OPEN_BITS);
  2083. if (SetFileId)
  2084. {
  2085. if (DFE_IS_FILE_WITH_ID(pDfeFile))
  2086. Status = AFP_ERR_ID_EXISTS;
  2087. DFE_SET_FILE_ID(pDfeFile);
  2088. }
  2089. if (ClrFileId)
  2090. {
  2091. if (!DFE_IS_FILE_WITH_ID(pDfeFile))
  2092. Status = AFP_ERR_ID_NOT_FOUND;
  2093. DFE_CLR_FILE_ID(pDfeFile);
  2094. }
  2095. }
  2096. else Status = AFP_ERR_OBJECT_NOT_FOUND;
  2097. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  2098. return Status;
  2099. }
  2100. /*** AfpCacheParentModTime
  2101. *
  2102. * When the contents of a directory change, the parent LastMod time must be
  2103. * updated. Since we don't want to wait for a notification of this,
  2104. * certain apis must go query for the new parent mod time and cache it.
  2105. * These include: CreateDir, CreateFile, CopyFile (Dest), Delete,
  2106. * Move (Src & Dest), Rename and ExchangeFiles.
  2107. *
  2108. * LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Exclusive)
  2109. */
  2110. VOID
  2111. AfpCacheParentModTime(
  2112. IN PVOLDESC pVolDesc,
  2113. IN PFILESYSHANDLE pHandle OPTIONAL, // if pPath not supplied
  2114. IN PUNICODE_STRING pPath OPTIONAL, // if pHandle not supplied
  2115. IN PDFENTRY pDfeParent OPTIONAL, // if ParentId not supplied
  2116. IN DWORD ParentId OPTIONAL // if pDfeParent not supplied
  2117. )
  2118. {
  2119. FILESYSHANDLE fshParent;
  2120. PFILESYSHANDLE phParent;
  2121. NTSTATUS Status;
  2122. PAGED_CODE( );
  2123. ASSERT(AfpSwmrLockedExclusive(&pVolDesc->vds_IdDbAccessLock));
  2124. if (!ARGUMENT_PRESENT(pDfeParent))
  2125. {
  2126. ASSERT(ARGUMENT_PRESENT((ULONG_PTR)ParentId));
  2127. pDfeParent = AfpFindDfEntryById(pVolDesc, ParentId, DFE_DIR);
  2128. if (pDfeParent == NULL)
  2129. {
  2130. return;
  2131. }
  2132. }
  2133. if (!ARGUMENT_PRESENT(pHandle))
  2134. {
  2135. ASSERT(ARGUMENT_PRESENT(pPath));
  2136. Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  2137. AFP_STREAM_DATA,
  2138. FILEIO_OPEN_DIR,
  2139. pPath,
  2140. FILEIO_ACCESS_NONE,
  2141. FILEIO_DENY_NONE,
  2142. False,
  2143. &fshParent);
  2144. if (!NT_SUCCESS(Status))
  2145. {
  2146. return;
  2147. }
  2148. phParent = &fshParent;
  2149. }
  2150. else
  2151. {
  2152. ASSERT(pHandle->fsh_FileHandle != NULL);
  2153. phParent = pHandle;
  2154. }
  2155. AfpIoQueryTimesnAttr(phParent,
  2156. NULL,
  2157. &pDfeParent->dfe_LastModTime,
  2158. NULL);
  2159. if (!ARGUMENT_PRESENT(pHandle))
  2160. {
  2161. AfpIoClose(&fshParent);
  2162. }
  2163. }
  2164. /*** afpAllocDfe
  2165. *
  2166. * Allocate a DFE from the DFE Blocks. The DFEs are allocated in 4K chunks and internally
  2167. * managed. The idea is primarily to reduce the number of faults we may take during
  2168. * enumeration/pathmap code in faulting in multiple pages to get multiple DFEs.
  2169. *
  2170. * The DFEs are allocated out of paged memory.
  2171. *
  2172. * It is important to keep blocks which are all used up at the end, so that if we hit a
  2173. * block which is empty, we can stop.
  2174. *
  2175. * LOCKS: afpDfeBlockLock (SWMR, Exclusive)
  2176. */
  2177. LOCAL PDFENTRY FASTCALL
  2178. afpAllocDfe(
  2179. IN LONG Index,
  2180. IN BOOLEAN fDir
  2181. )
  2182. {
  2183. PDFEBLOCK pDfb;
  2184. PDFENTRY pDfEntry = NULL;
  2185. #ifdef PROFILING
  2186. TIME TimeS, TimeE, TimeD;
  2187. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_DFEAllocCount);
  2188. AfpGetPerfCounter(&TimeS);
  2189. #endif
  2190. PAGED_CODE( );
  2191. ASSERT ((Index >= 0) && (Index < MAX_BLOCK_TYPE));
  2192. AfpSwmrAcquireExclusive(&afpDfeBlockLock);
  2193. // If the block head has no free entries then there are none !!
  2194. // Pick the right block based on whether it is file or dir
  2195. pDfb = fDir ? afpDirDfePartialBlockHead[Index] : afpFileDfePartialBlockHead[Index];
  2196. if (pDfb == NULL)
  2197. {
  2198. //
  2199. // There are no partial blocks. Check if there any free ones and if there move them to partial
  2200. // since we about to allocate from them
  2201. //
  2202. if (fDir)
  2203. {
  2204. pDfb = afpDirDfeFreeBlockHead[Index];
  2205. if (pDfb != NULL)
  2206. {
  2207. AfpUnlinkDouble(pDfb, dfb_Next, dfb_Prev);
  2208. AfpLinkDoubleAtHead(afpDirDfePartialBlockHead[Index],
  2209. pDfb,
  2210. dfb_Next,
  2211. dfb_Prev);
  2212. }
  2213. }
  2214. else
  2215. {
  2216. pDfb = afpFileDfeFreeBlockHead[Index];
  2217. if (pDfb != NULL)
  2218. {
  2219. AfpUnlinkDouble(pDfb, dfb_Next, dfb_Prev);
  2220. AfpLinkDoubleAtHead(afpFileDfePartialBlockHead[Index],
  2221. pDfb,
  2222. dfb_Next,
  2223. dfb_Prev);
  2224. }
  2225. }
  2226. }
  2227. if (pDfb != NULL)
  2228. {
  2229. ASSERT(VALID_DFB(pDfb));
  2230. ASSERT((fDir && (pDfb->dfb_NumFree <= afpDfeNumDirBlocks[Index])) ||
  2231. (!fDir && (pDfb->dfb_NumFree <= afpDfeNumFileBlocks[Index])));
  2232. ASSERT (pDfb->dfb_NumFree != 0);
  2233. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  2234. ("afpAllocDfe: Found space in Block %lx\n", pDfb));
  2235. }
  2236. if (pDfb == NULL)
  2237. {
  2238. ASSERT(QUAD_SIZED(sizeof(DFEBLOCK)));
  2239. ASSERT(QUAD_SIZED(sizeof(DIRENTRY)));
  2240. ASSERT(QUAD_SIZED(sizeof(DFENTRY)));
  2241. if ((pDfb = (PDFEBLOCK)AfpAllocateVirtualMemoryPage()) != NULL)
  2242. {
  2243. LONG i;
  2244. USHORT DfeSize, UnicodeSize, MaxDfes, DirEntrySize;
  2245. #if DBG
  2246. afpDfbAllocCount ++;
  2247. #endif
  2248. UnicodeSize = afpDfeUnicodeBufSize[Index];
  2249. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
  2250. ("afpAllocDfe: No free %s blocks. Allocated a new block %lx for index %ld\n",
  2251. fDir ? "Dir" : "File", pDfb, Index));
  2252. //
  2253. // Link it in the partial list as we are about to allocate one block out of it anyway.
  2254. //
  2255. if (fDir)
  2256. {
  2257. AfpLinkDoubleAtHead(afpDirDfePartialBlockHead[Index],
  2258. pDfb,
  2259. dfb_Next,
  2260. dfb_Prev);
  2261. DfeSize = afpDfeDirBlockSize[Index];
  2262. pDfb->dfb_NumFree = MaxDfes = afpDfeNumDirBlocks[Index];
  2263. DirEntrySize = sizeof(DIRENTRY);
  2264. }
  2265. else
  2266. {
  2267. AfpLinkDoubleAtHead(afpFileDfePartialBlockHead[Index],
  2268. pDfb,
  2269. dfb_Next,
  2270. dfb_Prev);
  2271. DfeSize = afpDfeFileBlockSize[Index];
  2272. pDfb->dfb_NumFree = MaxDfes = afpDfeNumFileBlocks[Index];
  2273. DirEntrySize = 0;
  2274. }
  2275. ASSERT(QUAD_SIZED(DfeSize));
  2276. pDfb->dfb_fDir = fDir;
  2277. pDfb->dfb_Age = 0;
  2278. // Initialize the list of free dfentries
  2279. for (i = 0, pDfEntry = pDfb->dfb_FreeHead = (PDFENTRY)((PBYTE)pDfb + sizeof(DFEBLOCK));
  2280. i < MaxDfes;
  2281. i++, pDfEntry = pDfEntry->dfe_NextFree)
  2282. {
  2283. pDfEntry->dfe_NextFree = (i == (MaxDfes - 1)) ?
  2284. NULL :
  2285. (PDFENTRY)((PBYTE)pDfEntry + DfeSize);
  2286. pDfEntry->dfe_pDirEntry = fDir ?
  2287. pDfEntry->dfe_pDirEntry = (PDIRENTRY)((PCHAR)pDfEntry+sizeof(DFENTRY)) : NULL;
  2288. pDfEntry->dfe_UnicodeName.Buffer = (PWCHAR)((PCHAR)pDfEntry+
  2289. DirEntrySize+
  2290. sizeof(DFENTRY));
  2291. pDfEntry->dfe_UnicodeName.MaximumLength = UnicodeSize;
  2292. }
  2293. }
  2294. else
  2295. {
  2296. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2297. ("afpAllocDfe: AfpAllocateVirtualMemoryPage failed\n"));
  2298. AFPLOG_ERROR(AFPSRVMSG_VIRTMEM_ALLOC_FAILED,
  2299. STATUS_INSUFFICIENT_RESOURCES,
  2300. NULL,
  2301. 0,
  2302. NULL);
  2303. }
  2304. }
  2305. if (pDfb != NULL)
  2306. {
  2307. PDFEBLOCK pTmp;
  2308. ASSERT(VALID_DFB(pDfb));
  2309. pDfEntry = pDfb->dfb_FreeHead;
  2310. ASSERT(VALID_DFE(pDfEntry));
  2311. ASSERT(pDfb->dfb_fDir ^ (pDfEntry->dfe_pDirEntry == NULL));
  2312. #if DBG
  2313. afpDfeAllocCount ++;
  2314. #endif
  2315. pDfb->dfb_FreeHead = pDfEntry->dfe_NextFree;
  2316. pDfb->dfb_NumFree --;
  2317. //
  2318. // If the block is now empty (completely used), unlink it from here and move it
  2319. // to the Used list.
  2320. //
  2321. if (pDfb->dfb_NumFree == 0)
  2322. {
  2323. AfpUnlinkDouble(pDfb, dfb_Next, dfb_Prev);
  2324. if (fDir)
  2325. {
  2326. AfpLinkDoubleAtHead(afpDirDfeUsedBlockHead[Index],
  2327. pDfb,
  2328. dfb_Next,
  2329. dfb_Prev);
  2330. }
  2331. else
  2332. {
  2333. AfpLinkDoubleAtHead(afpFileDfeUsedBlockHead[Index],
  2334. pDfb,
  2335. dfb_Next,
  2336. dfb_Prev);
  2337. }
  2338. }
  2339. pDfEntry->dfe_UnicodeName.Length = 0;
  2340. }
  2341. AfpSwmrRelease(&afpDfeBlockLock);
  2342. #ifdef PROFILING
  2343. AfpGetPerfCounter(&TimeE);
  2344. TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
  2345. INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_DFEAllocTime,
  2346. TimeD,
  2347. &AfpStatisticsLock);
  2348. #endif
  2349. if ((pDfEntry != NULL) &&
  2350. (pDfEntry->dfe_pDirEntry != NULL))
  2351. {
  2352. // For a directory ZERO out the directory entry
  2353. RtlZeroMemory(&pDfEntry->dfe_pDirEntry->de_ChildDir, sizeof(DIRENTRY));
  2354. }
  2355. return pDfEntry;
  2356. }
  2357. /*** afpFreeDfe
  2358. *
  2359. * Return a DFE to the allocation block.
  2360. *
  2361. * LOCKS: afpDfeBlockLock (SWMR, Exclusive)
  2362. */
  2363. LOCAL VOID FASTCALL
  2364. afpFreeDfe(
  2365. IN PDFENTRY pDfEntry
  2366. )
  2367. {
  2368. PDFEBLOCK pDfb;
  2369. USHORT NumBlks, index;
  2370. #ifdef PROFILING
  2371. TIME TimeS, TimeE, TimeD;
  2372. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_DFEFreeCount);
  2373. AfpGetPerfCounter(&TimeS);
  2374. #endif
  2375. PAGED_CODE( );
  2376. // NOTE: The following code *depends* on the fact that we allocate DFBs as
  2377. // 64K blocks and also that these are allocated *at* 64K boundaries
  2378. // This lets us *cheaply* get to the owning DFB from the DFE.
  2379. pDfb = (PDFEBLOCK)((ULONG_PTR)pDfEntry & ~(PAGE_SIZE-1));
  2380. ASSERT(VALID_DFB(pDfb));
  2381. ASSERT(pDfb->dfb_fDir ^ (pDfEntry->dfe_pDirEntry == NULL));
  2382. AfpSwmrAcquireExclusive(&afpDfeBlockLock);
  2383. #if DBG
  2384. afpDfeAllocCount --;
  2385. #endif
  2386. index = USIZE_TO_INDEX(pDfEntry->dfe_UnicodeName.MaximumLength);
  2387. NumBlks = (pDfb->dfb_fDir) ? afpDfeNumDirBlocks[index] : afpDfeNumFileBlocks[index];
  2388. ASSERT((pDfb->dfb_fDir && (pDfb->dfb_NumFree < NumBlks)) ||
  2389. (!pDfb->dfb_fDir && (pDfb->dfb_NumFree < NumBlks)));
  2390. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  2391. ("AfpFreeDfe: Returning pDfEntry %lx to Block %lx, size %d\n",
  2392. pDfEntry, pDfb, pDfEntry->dfe_UnicodeName.MaximumLength));
  2393. pDfb->dfb_NumFree ++;
  2394. pDfEntry->dfe_NextFree = pDfb->dfb_FreeHead;
  2395. pDfb->dfb_FreeHead = pDfEntry;
  2396. if (pDfb->dfb_NumFree == 1)
  2397. {
  2398. LONG Index;
  2399. //
  2400. // The block is now partially free (it used to be completely used). move it to the partial list.
  2401. //
  2402. Index = USIZE_TO_INDEX(pDfEntry->dfe_UnicodeName.MaximumLength);
  2403. AfpUnlinkDouble(pDfb, dfb_Next, dfb_Prev);
  2404. if (pDfb->dfb_fDir)
  2405. {
  2406. AfpLinkDoubleAtHead(afpDirDfePartialBlockHead[Index],
  2407. pDfb,
  2408. dfb_Next,
  2409. dfb_Prev);
  2410. }
  2411. else
  2412. {
  2413. AfpLinkDoubleAtHead(afpFileDfePartialBlockHead[Index],
  2414. pDfb,
  2415. dfb_Next,
  2416. dfb_Prev);
  2417. }
  2418. }
  2419. else if (pDfb->dfb_NumFree == NumBlks)
  2420. {
  2421. LONG Index;
  2422. //
  2423. // The block is now completely free (used to be partially used). move it to the free list
  2424. //
  2425. Index = USIZE_TO_INDEX(pDfEntry->dfe_UnicodeName.MaximumLength);
  2426. pDfb->dfb_Age = 0;
  2427. AfpUnlinkDouble(pDfb, dfb_Next, dfb_Prev);
  2428. if (AfpServerState == AFP_STATE_STOP_PENDING)
  2429. {
  2430. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
  2431. ("afpFreeDfe: Freeing Block %lx\n", pDfb));
  2432. AfpFreeVirtualMemoryPage(pDfb);
  2433. #if DBG
  2434. afpDfbAllocCount --;
  2435. #endif
  2436. }
  2437. else
  2438. {
  2439. if (pDfb->dfb_fDir)
  2440. {
  2441. AfpLinkDoubleAtHead(afpDirDfeFreeBlockHead[Index],
  2442. pDfb,
  2443. dfb_Next,
  2444. dfb_Prev);
  2445. }
  2446. else
  2447. {
  2448. AfpLinkDoubleAtHead(afpFileDfeFreeBlockHead[Index],
  2449. pDfb,
  2450. dfb_Next,
  2451. dfb_Prev);
  2452. }
  2453. }
  2454. }
  2455. AfpSwmrRelease(&afpDfeBlockLock);
  2456. #ifdef PROFILING
  2457. AfpGetPerfCounter(&TimeE);
  2458. TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
  2459. INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_DFEFreeTime,
  2460. TimeD,
  2461. &AfpStatisticsLock);
  2462. #endif
  2463. }
  2464. /*** afpDfeBlockAge
  2465. *
  2466. * Age out Dfe Blocks
  2467. *
  2468. * LOCKS: afpDfeBlockLock (SWMR, Exclusive)
  2469. */
  2470. AFPSTATUS FASTCALL
  2471. afpDfeBlockAge(
  2472. IN PPDFEBLOCK ppBlockHead
  2473. )
  2474. {
  2475. int index, MaxDfes;
  2476. PDFEBLOCK pDfb;
  2477. PAGED_CODE( );
  2478. AfpSwmrAcquireExclusive(&afpDfeBlockLock);
  2479. for (index = 0; index < MAX_BLOCK_TYPE; index++)
  2480. {
  2481. pDfb = ppBlockHead[index];
  2482. if (pDfb != NULL)
  2483. {
  2484. MaxDfes = pDfb->dfb_fDir ? afpDfeNumDirBlocks[index] : afpDfeNumFileBlocks[index];
  2485. }
  2486. while (pDfb != NULL)
  2487. {
  2488. PDFEBLOCK pFree;
  2489. ASSERT(VALID_DFB(pDfb));
  2490. pFree = pDfb;
  2491. pDfb = pDfb->dfb_Next;
  2492. ASSERT (pFree->dfb_NumFree == MaxDfes);
  2493. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
  2494. ("afpDfeBlockAge: Aging Block %lx, Size %d\n", pFree,
  2495. pFree->dfb_fDir ? afpDfeDirBlockSize[index] : afpDfeFileBlockSize[index]));
  2496. if (++(pFree->dfb_Age) >= MAX_BLOCK_AGE)
  2497. {
  2498. #ifdef PROFILING
  2499. INTERLOCKED_INCREMENT_LONG( &AfpServerProfile->perf_DFEAgeCount);
  2500. #endif
  2501. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
  2502. ("afpDfeBlockAge: Freeing Block %lx, Size %d\n", pFree,
  2503. pDfb->dfb_fDir ? afpDfeDirBlockSize[index] : afpDfeFileBlockSize[index]));
  2504. AfpUnlinkDouble(pFree, dfb_Next, dfb_Prev);
  2505. AfpFreeVirtualMemoryPage(pFree);
  2506. #if DBG
  2507. afpDfbAllocCount --;
  2508. #endif
  2509. }
  2510. }
  2511. }
  2512. AfpSwmrRelease(&afpDfeBlockLock);
  2513. return AFP_ERR_REQUEUE;
  2514. }
  2515. /*** AfpInitIdDb
  2516. *
  2517. * This routine initializes the memory image (and all related volume descriptor
  2518. * fields) of the ID index database for a new volume. The entire tree is
  2519. * scanned so all the file/dir cached info can be read in and our view of
  2520. * the volume tree will be complete. If an index database already exists
  2521. * on the disk for the volume root directory, that stream is read in. If this
  2522. * is a newly created volume, the Afp_IdIndex stream is created on the root of
  2523. * the volume. If this is a CDFS volume, only the memory image is initialized.
  2524. *
  2525. * The IdDb is not locked since the volume is still 'in transition' and not
  2526. * accessed by anybody.
  2527. */
  2528. NTSTATUS FASTCALL
  2529. AfpInitIdDb(
  2530. IN PVOLDESC pVolDesc,
  2531. OUT BOOLEAN *pfNewVolume,
  2532. OUT BOOLEAN *pfVerifyIndex
  2533. )
  2534. {
  2535. NTSTATUS Status;
  2536. ULONG CreateInfo;
  2537. FILESYSHANDLE fshIdDb;
  2538. IDDBHDR IdDbHdr;
  2539. BOOLEAN fLogEvent=FALSE;
  2540. PAGED_CODE( );
  2541. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  2542. ("AfpInitIdDb: Initializing Id Database...\n"));
  2543. *pfNewVolume = FALSE;
  2544. do
  2545. {
  2546. afpInitializeIdDb(pVolDesc);
  2547. // if this is not a CDFS volume, attempt to create the ID DB header
  2548. // stream. If it already exists, open it and read it in.
  2549. if (IS_VOLUME_NTFS(pVolDesc))
  2550. {
  2551. // Force the scavenger to write out the IdDb and header when the
  2552. // volume is successfully added
  2553. pVolDesc->vds_Flags |= VOLUME_IDDBHDR_DIRTY;
  2554. Status = AfpIoCreate(&pVolDesc->vds_hRootDir,
  2555. AFP_STREAM_IDDB,
  2556. &UNullString,
  2557. FILEIO_ACCESS_READWRITE,
  2558. FILEIO_DENY_WRITE,
  2559. FILEIO_OPEN_FILE_SEQ,
  2560. FILEIO_CREATE_INTERNAL,
  2561. FILE_ATTRIBUTE_NORMAL,
  2562. False,
  2563. NULL,
  2564. &fshIdDb,
  2565. &CreateInfo,
  2566. NULL,
  2567. NULL,
  2568. NULL);
  2569. if (!NT_SUCCESS(Status))
  2570. {
  2571. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  2572. ("AfpInitIdDb: AfpIoCreate failed with %lx\n", Status));
  2573. ASSERT(0);
  2574. fLogEvent = TRUE;
  2575. break;
  2576. }
  2577. if (CreateInfo == FILE_OPENED)
  2578. {
  2579. // read in the existing header. If we fail, just start from scratch
  2580. Status = afpReadIdDb(pVolDesc, &fshIdDb, pfVerifyIndex);
  2581. if (!NT_SUCCESS(Status) || (pVolDesc->vds_pDfeRoot == NULL))
  2582. CreateInfo = FILE_CREATED;
  2583. }
  2584. if (CreateInfo == FILE_CREATED)
  2585. {
  2586. // add the root and parent of root to the idindex
  2587. // and initialize a new header
  2588. Status = afpSeedIdDb(pVolDesc);
  2589. *pfNewVolume = TRUE;
  2590. }
  2591. else if (CreateInfo != FILE_OPENED)
  2592. {
  2593. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  2594. ("AfpInitIdDb: unexpected create action 0x%lx\n", CreateInfo));
  2595. ASSERTMSG("Unexpected Create Action\n", 0); // this should never happen
  2596. fLogEvent = TRUE;
  2597. Status = STATUS_UNSUCCESSFUL;
  2598. }
  2599. AfpIoClose(&fshIdDb);
  2600. //
  2601. // write back the IdDb header to the file, but with bad signature.
  2602. // If server shuts down, the correct signature will be
  2603. // written. If macfile is closed down using "net stop macfile"
  2604. // signature is corrupted with a different type
  2605. // If cool boot/bugcheck, a third type
  2606. // During volume startup, we will know from the signature,
  2607. // whether to rebuild completely, read iddb but verify or
  2608. // not rebuild at all
  2609. //
  2610. if (NT_SUCCESS(Status))
  2611. {
  2612. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  2613. ("AfpInitIdDb: ***** Corrupting IDDB header ***** \n"));
  2614. AfpVolDescToIdDbHdr(pVolDesc, &IdDbHdr);
  2615. IdDbHdr.idh_Signature = AFP_SERVER_SIGNATURE_INITIDDB;
  2616. AfpVolumeUpdateIdDbAndDesktop(pVolDesc,FALSE,FALSE,&IdDbHdr);
  2617. }
  2618. }
  2619. else
  2620. {
  2621. // its CDFS, just initialize the memory image of the IdDB
  2622. Status = afpSeedIdDb(pVolDesc);
  2623. *pfNewVolume = TRUE;
  2624. }
  2625. } while (False);
  2626. if (fLogEvent)
  2627. {
  2628. AFPLOG_ERROR(AFPSRVMSG_INIT_IDDB,
  2629. Status,
  2630. NULL,
  2631. 0,
  2632. &pVolDesc->vds_Name);
  2633. Status = STATUS_UNSUCCESSFUL;
  2634. }
  2635. return Status;
  2636. }
  2637. /*** afpSeedIdDb
  2638. *
  2639. * This routine adds the 'parent of root' and the root directory entries
  2640. * to a newly created ID index database (the memory image of the iddb).
  2641. *
  2642. **/
  2643. LOCAL NTSTATUS FASTCALL
  2644. afpSeedIdDb(
  2645. IN PVOLDESC pVolDesc
  2646. )
  2647. {
  2648. PDFENTRY pDfEntry;
  2649. AFPTIME CurrentTime;
  2650. AFPINFO afpinfo;
  2651. FILESYSHANDLE fshAfpInfo, fshComment, fshData;
  2652. DWORD i, crinfo, Attr;
  2653. FINDERINFO FinderInfo;
  2654. NTSTATUS Status = STATUS_SUCCESS;
  2655. PAGED_CODE( );
  2656. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  2657. ("afpSeedIdDb: Creating new Id Database...\n"));
  2658. do
  2659. {
  2660. pDfEntry = AfpFindDfEntryById(pVolDesc,
  2661. AFP_ID_PARENT_OF_ROOT,
  2662. DFE_DIR);
  2663. ASSERT (pDfEntry != NULL);
  2664. // add the root directory to the id index
  2665. if ((pDfEntry = AfpAddDfEntry(pVolDesc,
  2666. pDfEntry,
  2667. &pVolDesc->vds_Name,
  2668. True,
  2669. AFP_ID_ROOT)) == NULL )
  2670. {
  2671. Status = STATUS_NO_MEMORY;
  2672. break;
  2673. }
  2674. pVolDesc->vds_pDfeRoot = pDfEntry; // Initialize pointer to root.
  2675. // Attempt to open the comment stream. If it succeeds, set a flag in
  2676. // the DFE indicating that this thing does indeed have a comment.
  2677. if (NT_SUCCESS(AfpIoOpen(&pVolDesc->vds_hRootDir,
  2678. AFP_STREAM_COMM,
  2679. FILEIO_OPEN_FILE,
  2680. &UNullString,
  2681. FILEIO_ACCESS_NONE,
  2682. FILEIO_DENY_NONE,
  2683. False,
  2684. &fshComment)))
  2685. {
  2686. DFE_SET_COMMENT(pDfEntry);
  2687. AfpIoClose(&fshComment);
  2688. }
  2689. // Get the directory information for volume root dir. Do not get the
  2690. // mod-time. See below.
  2691. Status = AfpIoQueryTimesnAttr(&pVolDesc->vds_hRootDir,
  2692. &pDfEntry->dfe_CreateTime,
  2693. NULL,
  2694. &Attr);
  2695. // Setup up root directories Last ModTime such that it will
  2696. // get enumerated.
  2697. AfpConvertTimeFromMacFormat(BEGINNING_OF_TIME,
  2698. &pDfEntry->dfe_LastModTime);
  2699. ASSERT(NT_SUCCESS(Status));
  2700. pDfEntry->dfe_NtAttr = (USHORT)Attr & FILE_ATTRIBUTE_VALID_FLAGS;
  2701. if (IS_VOLUME_NTFS(pVolDesc))
  2702. {
  2703. if (NT_SUCCESS(Status = AfpCreateAfpInfo(&pVolDesc->vds_hRootDir,
  2704. &fshAfpInfo,
  2705. &crinfo)))
  2706. {
  2707. if ((crinfo == FILE_CREATED) ||
  2708. (!NT_SUCCESS(AfpReadAfpInfo(&fshAfpInfo, &afpinfo))))
  2709. {
  2710. Status = AfpSlapOnAfpInfoStream(NULL,
  2711. NULL,
  2712. &pVolDesc->vds_hRootDir,
  2713. &fshAfpInfo,
  2714. AFP_ID_ROOT,
  2715. True,
  2716. NULL,
  2717. &afpinfo);
  2718. }
  2719. else
  2720. {
  2721. // Just make sure the afp ID is ok, preserve the rest
  2722. if (afpinfo.afpi_Id != AFP_ID_ROOT)
  2723. {
  2724. afpinfo.afpi_Id = AFP_ID_ROOT;
  2725. AfpWriteAfpInfo(&fshAfpInfo, &afpinfo);
  2726. }
  2727. }
  2728. AfpIoClose(&fshAfpInfo);
  2729. pDfEntry->dfe_AfpAttr = afpinfo.afpi_Attributes;
  2730. pDfEntry->dfe_FinderInfo = afpinfo.afpi_FinderInfo;
  2731. if (pVolDesc->vds_Flags & AFP_VOLUME_HAS_CUSTOM_ICON)
  2732. {
  2733. // Don't bother writing back to disk since we do not
  2734. // try to keep this in sync in the permanent afpinfo
  2735. // stream with the actual existence of the icon<0d> file.
  2736. pDfEntry->dfe_FinderInfo.fd_Attr1 |= FINDER_FLAG_HAS_CUSTOM_ICON;
  2737. }
  2738. pDfEntry->dfe_BackupTime = afpinfo.afpi_BackupTime;
  2739. DFE_OWNER_ACCESS(pDfEntry) = afpinfo.afpi_AccessOwner;
  2740. DFE_GROUP_ACCESS(pDfEntry) = afpinfo.afpi_AccessGroup;
  2741. DFE_WORLD_ACCESS(pDfEntry) = afpinfo.afpi_AccessWorld;
  2742. }
  2743. }
  2744. else // CDFS
  2745. {
  2746. RtlZeroMemory(&pDfEntry->dfe_FinderInfo, sizeof(FINDERINFO));
  2747. if (IS_VOLUME_CD_HFS(pVolDesc))
  2748. {
  2749. Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  2750. AFP_STREAM_DATA,
  2751. FILEIO_OPEN_DIR,
  2752. &UNullString,
  2753. FILEIO_ACCESS_NONE,
  2754. FILEIO_DENY_NONE,
  2755. False,
  2756. &fshData);
  2757. if (!NT_SUCCESS(Status))
  2758. {
  2759. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  2760. ("afpSeedIdDb: AfpIoOpeno failed with %lx for CD_HFS\n", Status));
  2761. break;
  2762. }
  2763. AfpIoClose(&fshData);
  2764. }
  2765. pDfEntry->dfe_BackupTime = BEGINNING_OF_TIME;
  2766. DFE_OWNER_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
  2767. DFE_GROUP_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
  2768. DFE_WORLD_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
  2769. pDfEntry->dfe_AfpAttr = 0;
  2770. }
  2771. } while (False);
  2772. return Status;
  2773. }
  2774. /*** AfpFreeIdIndexTables
  2775. *
  2776. * Free the allocated memory for the volume id index tables. The volume is
  2777. * about to be deleted. Ensure that either the volume is readonly or it is
  2778. * clean i.e. the scavenger threads have written it back.
  2779. *
  2780. */
  2781. VOID FASTCALL
  2782. AfpFreeIdIndexTables(
  2783. IN PVOLDESC pVolDesc
  2784. )
  2785. {
  2786. DWORD i;
  2787. struct _DirFileEntry ** DfeDirBucketStart;
  2788. struct _DirFileEntry ** DfeFileBucketStart;
  2789. PAGED_CODE( );
  2790. ASSERT (IS_VOLUME_RO(pVolDesc) ||
  2791. (pVolDesc->vds_pOpenForkDesc == NULL));
  2792. // Traverse each of the hashed indices and free the entries.
  2793. // Need only traverse the overflow links. Ignore other links.
  2794. // JH - Do not bother if we are here during shutdown
  2795. if (AfpServerState != AFP_STATE_SHUTTINGDOWN)
  2796. {
  2797. PDFENTRY pDfEntry, pFree;
  2798. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
  2799. DfeFileBucketStart = pVolDesc->vds_pDfeFileBucketStart;
  2800. if (DfeFileBucketStart)
  2801. {
  2802. for (i = 0; i < pVolDesc->vds_FileHashTableSize; i++)
  2803. {
  2804. for (pDfEntry = DfeFileBucketStart[i];
  2805. pDfEntry != NULL;
  2806. NOTHING)
  2807. {
  2808. ASSERT(VALID_DFE(pDfEntry));
  2809. pFree = pDfEntry;
  2810. pDfEntry = pDfEntry->dfe_NextOverflow;
  2811. FREE_DFE(pFree);
  2812. }
  2813. DfeFileBucketStart[i] = NULL;
  2814. }
  2815. }
  2816. DfeDirBucketStart = pVolDesc->vds_pDfeDirBucketStart;
  2817. if (DfeDirBucketStart)
  2818. {
  2819. for (i = 0; i < pVolDesc->vds_DirHashTableSize; i++)
  2820. {
  2821. for (pDfEntry = DfeDirBucketStart[i];
  2822. pDfEntry != NULL;
  2823. NOTHING)
  2824. {
  2825. ASSERT(VALID_DFE(pDfEntry));
  2826. pFree = pDfEntry;
  2827. pDfEntry = pDfEntry->dfe_NextOverflow;
  2828. FREE_DFE(pFree);
  2829. }
  2830. DfeDirBucketStart[i] = NULL;
  2831. }
  2832. }
  2833. RtlZeroMemory(pVolDesc->vds_pDfeCache,
  2834. IDINDEX_CACHE_ENTRIES * sizeof(PDFENTRY));
  2835. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  2836. }
  2837. }
  2838. /*** afpRenameInvalidWin32Name
  2839. *
  2840. */
  2841. VOID
  2842. afpRenameInvalidWin32Name(
  2843. IN PFILESYSHANDLE phRootDir,
  2844. IN BOOLEAN IsDir,
  2845. IN PUNICODE_STRING pName
  2846. )
  2847. {
  2848. FILESYSHANDLE Fsh;
  2849. NTSTATUS rc;
  2850. WCHAR wc;
  2851. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2852. ("afpRenameInvalidWin32Name: renaming on the fly %Z\n", pName));
  2853. // Rename it now
  2854. if (NT_SUCCESS(AfpIoOpen(phRootDir,
  2855. AFP_STREAM_DATA,
  2856. IsDir ? FILEIO_OPEN_DIR : FILEIO_OPEN_FILE,
  2857. pName,
  2858. FILEIO_ACCESS_DELETE,
  2859. FILEIO_DENY_NONE,
  2860. False,
  2861. &Fsh)))
  2862. {
  2863. DWORD NtAttr;
  2864. // Before we attempt a rename, check if the RO bit is set. If it is
  2865. // reset it temporarily.
  2866. rc = AfpIoQueryTimesnAttr(&Fsh, NULL, NULL, &NtAttr);
  2867. ASSERT(NT_SUCCESS(rc));
  2868. if (NtAttr & FILE_ATTRIBUTE_READONLY)
  2869. {
  2870. rc = AfpIoSetTimesnAttr(&Fsh,
  2871. NULL,
  2872. NULL,
  2873. 0,
  2874. FILE_ATTRIBUTE_READONLY,
  2875. NULL,
  2876. NULL);
  2877. ASSERT(NT_SUCCESS(rc));
  2878. }
  2879. // Convert the name back to UNICODE so that munging happens !!!
  2880. wc = pName->Buffer[(pName->Length - 1)/sizeof(WCHAR)];
  2881. if (wc == UNICODE_SPACE)
  2882. pName->Buffer[(pName->Length - 1)/sizeof(WCHAR)] = AfpMungedUnicodeSpace;
  2883. if (wc == UNICODE_PERIOD)
  2884. pName->Buffer[(pName->Length - 1)/sizeof(WCHAR)] = AfpMungedUnicodePeriod;
  2885. rc = AfpIoMoveAndOrRename(&Fsh,
  2886. NULL,
  2887. pName,
  2888. NULL,
  2889. NULL,
  2890. NULL,
  2891. NULL,
  2892. NULL);
  2893. ASSERT(NT_SUCCESS(rc));
  2894. // Set the RO Attr back if it was set to begin with
  2895. if (NtAttr & FILE_ATTRIBUTE_READONLY)
  2896. {
  2897. rc = AfpIoSetTimesnAttr(&Fsh,
  2898. NULL,
  2899. NULL,
  2900. FILE_ATTRIBUTE_READONLY,
  2901. 0,
  2902. NULL,
  2903. NULL);
  2904. ASSERT(NT_SUCCESS(rc));
  2905. }
  2906. AfpIoClose(&Fsh);
  2907. }
  2908. }
  2909. LONG afpVirtualMemoryCount = 0;
  2910. LONG afpVirtualMemorySize = 0;
  2911. /*** AfpAllocVirtualMemory
  2912. *
  2913. * This is a wrapper over NtAllocateVirtualMemory.
  2914. */
  2915. PBYTE FASTCALL
  2916. AfpAllocateVirtualMemoryPage(
  2917. IN VOID
  2918. )
  2919. {
  2920. PBYTE pMem = NULL;
  2921. NTSTATUS Status;
  2922. PBLOCK64K pCurrBlock;
  2923. PBLOCK64K pTmpBlk;
  2924. SIZE_T Size64K;
  2925. DWORD i, dwMaxPages;
  2926. Size64K = BLOCK_64K_ALLOC;
  2927. dwMaxPages = (BLOCK_64K_ALLOC/PAGE_SIZE);
  2928. pCurrBlock = afp64kBlockHead;
  2929. //
  2930. // if we have never allocated a 64K block as yet, or we don't have one that
  2931. // has any free page(s) in it, allocate a new block!
  2932. //
  2933. if ((pCurrBlock == NULL) || (pCurrBlock->b64_PagesFree == 0))
  2934. {
  2935. pCurrBlock = (PBLOCK64K)AfpAllocNonPagedMemory(sizeof(BLOCK64K));
  2936. if (pCurrBlock == NULL)
  2937. {
  2938. return(NULL);
  2939. }
  2940. ExInterlockedIncrementLong(&afpVirtualMemoryCount, &AfpStatisticsLock);
  2941. ExInterlockedAddUlong(&afpVirtualMemorySize, (ULONG)Size64K, &(AfpStatisticsLock.SpinLock));
  2942. Status = NtAllocateVirtualMemory(NtCurrentProcess(),
  2943. &pMem,
  2944. 0L,
  2945. &Size64K,
  2946. MEM_COMMIT,
  2947. PAGE_READWRITE);
  2948. if (NT_SUCCESS(Status))
  2949. {
  2950. ASSERT(pMem != NULL);
  2951. #if DBG
  2952. afpDfe64kBlockCount++;
  2953. #endif
  2954. pCurrBlock->b64_Next = afp64kBlockHead;
  2955. pCurrBlock->b64_BaseAddress = pMem;
  2956. pCurrBlock->b64_PagesFree = dwMaxPages;
  2957. for (i=0; i<dwMaxPages; i++)
  2958. {
  2959. pCurrBlock->b64_PageInUse[i] = FALSE;
  2960. }
  2961. afp64kBlockHead = pCurrBlock;
  2962. }
  2963. else
  2964. {
  2965. AfpFreeMemory(pCurrBlock);
  2966. return(NULL);
  2967. }
  2968. }
  2969. //
  2970. // if we came this far, we are guaranteed that pCurrBlock is pointing to a
  2971. // block that has at least one page free
  2972. //
  2973. ASSERT ((pCurrBlock != NULL) &&
  2974. (pCurrBlock->b64_PagesFree > 0) &&
  2975. (pCurrBlock->b64_PagesFree <= dwMaxPages));
  2976. // find out which page is free
  2977. for (i=0; i<dwMaxPages; i++)
  2978. {
  2979. if (pCurrBlock->b64_PageInUse[i] == FALSE)
  2980. {
  2981. break;
  2982. }
  2983. }
  2984. ASSERT(i < dwMaxPages);
  2985. pCurrBlock->b64_PagesFree--;
  2986. pCurrBlock->b64_PageInUse[i] = TRUE;
  2987. pMem = ((PBYTE)pCurrBlock->b64_BaseAddress) + (i * PAGE_SIZE);
  2988. //
  2989. // if this 64kblock has no more free pages in it, move it to a spot after
  2990. // all the blocks that have some pages free in it. For that, we first
  2991. // find the guy who has no pages free in him and move this block after him
  2992. //
  2993. if (pCurrBlock->b64_PagesFree == 0)
  2994. {
  2995. pTmpBlk = pCurrBlock->b64_Next;
  2996. if (pTmpBlk != NULL)
  2997. {
  2998. while (1)
  2999. {
  3000. // found a guy who has no free page in it?
  3001. if (pTmpBlk->b64_PagesFree == 0)
  3002. {
  3003. break;
  3004. }
  3005. // is this the last guy on the list?
  3006. if (pTmpBlk->b64_Next == NULL)
  3007. {
  3008. break;
  3009. }
  3010. pTmpBlk = pTmpBlk->b64_Next;
  3011. }
  3012. }
  3013. // if we found a block
  3014. if (pTmpBlk)
  3015. {
  3016. ASSERT(afp64kBlockHead == pCurrBlock);
  3017. afp64kBlockHead = pCurrBlock->b64_Next;
  3018. pCurrBlock->b64_Next = pTmpBlk->b64_Next;
  3019. pTmpBlk->b64_Next = pCurrBlock;
  3020. }
  3021. }
  3022. return pMem;
  3023. }
  3024. VOID FASTCALL
  3025. AfpFreeVirtualMemoryPage(
  3026. IN PVOID pBuffer
  3027. )
  3028. {
  3029. NTSTATUS Status;
  3030. PBYTE BaseAddr;
  3031. PBLOCK64K pCurrBlock;
  3032. PBLOCK64K pPrevBlk;
  3033. SIZE_T Size64K;
  3034. DWORD i, dwMaxPages, dwPageNum, Offset;
  3035. dwMaxPages = (BLOCK_64K_ALLOC/PAGE_SIZE);
  3036. Size64K = BLOCK_64K_ALLOC;
  3037. pCurrBlock = afp64kBlockHead;
  3038. pPrevBlk = afp64kBlockHead;
  3039. BaseAddr = (PBYTE)((ULONG_PTR)pBuffer & ~(BLOCK_64K_ALLOC - 1));
  3040. Offset = (DWORD)(((PBYTE)pBuffer - BaseAddr));
  3041. dwPageNum = Offset/PAGE_SIZE;
  3042. ASSERT(Offset < BLOCK_64K_ALLOC);
  3043. while (pCurrBlock != NULL)
  3044. {
  3045. if (pCurrBlock->b64_BaseAddress == BaseAddr)
  3046. {
  3047. break;
  3048. }
  3049. pPrevBlk = pCurrBlock;
  3050. pCurrBlock = pCurrBlock->b64_Next;
  3051. }
  3052. ASSERT(pCurrBlock->b64_BaseAddress == BaseAddr);
  3053. pCurrBlock->b64_PageInUse[dwPageNum] = FALSE;
  3054. pCurrBlock->b64_PagesFree++;
  3055. //
  3056. // if all the pages in this block are unused, then it's time to free this block
  3057. // after removing from the list
  3058. //
  3059. if (pCurrBlock->b64_PagesFree == dwMaxPages)
  3060. {
  3061. // is our guy the first (and potentially the only one) on the list?
  3062. if (afp64kBlockHead == pCurrBlock)
  3063. {
  3064. afp64kBlockHead = pCurrBlock->b64_Next;
  3065. }
  3066. // nope, there are others and we're somewhere in the middle (or end)
  3067. else
  3068. {
  3069. pPrevBlk->b64_Next = pCurrBlock->b64_Next;
  3070. }
  3071. AfpFreeMemory(pCurrBlock);
  3072. ExInterlockedDecrementLong(&afpVirtualMemoryCount, &AfpStatisticsLock);
  3073. ExInterlockedAddUlong(&afpVirtualMemorySize,
  3074. -1*((ULONG)Size64K),
  3075. &(AfpStatisticsLock.SpinLock));
  3076. Status = NtFreeVirtualMemory(NtCurrentProcess(),
  3077. (PVOID *)&BaseAddr,
  3078. &Size64K,
  3079. MEM_RELEASE);
  3080. #if DBG
  3081. ASSERT(afpDfe64kBlockCount > 0);
  3082. afpDfe64kBlockCount--;
  3083. #endif
  3084. }
  3085. //
  3086. // if a page became available in this block for the first time, move this
  3087. // block to the front of the list (unless it already is there)
  3088. //
  3089. else if (pCurrBlock->b64_PagesFree == 1)
  3090. {
  3091. if (afp64kBlockHead != pCurrBlock)
  3092. {
  3093. pPrevBlk->b64_Next = pCurrBlock->b64_Next;
  3094. pCurrBlock->b64_Next = afp64kBlockHead;
  3095. afp64kBlockHead = pCurrBlock;
  3096. }
  3097. }
  3098. }
  3099. #ifdef AGE_DFES
  3100. /*** AfpAgeDfEntries
  3101. *
  3102. * Age out DfEntries out of the Id database. The Files in directories which have not been
  3103. * accessed for VOLUME_IDDB_AGE_DELAY are aged out. The directories are marked so that
  3104. * they will be enumerated when they are hit next.
  3105. *
  3106. * LOCKS: vds_idDbAccessLock(SWMR, Exclusive)
  3107. */
  3108. VOID FASTCALL
  3109. AfpAgeDfEntries(
  3110. IN PVOLDESC pVolDesc
  3111. )
  3112. {
  3113. PDFENTRY pDfEntry, *Stack = NULL;
  3114. LONG i, StackPtr = 0;
  3115. AFPTIME Now;
  3116. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  3117. AfpGetCurrentTimeInMacFormat(&Now);
  3118. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
  3119. // Potentially all of the files can be aged out. Allocate 'stack' space
  3120. // for all of the directory DFEs
  3121. if ((Stack = (PDFENTRY *)
  3122. AfpAllocNonPagedMemory(pVolDesc->vds_NumDirDfEntries*sizeof(PDFENTRY))) != NULL)
  3123. {
  3124. // 'Prime' the stack of Dfe's
  3125. Stack[StackPtr++] = pVolDesc->vds_pDfeRoot;
  3126. while (StackPtr > 0)
  3127. {
  3128. PDFENTRY pDir;
  3129. pDfEntry = Stack[--StackPtr];
  3130. ASSERT(DFE_IS_DIRECTORY(pDfEntry));
  3131. if ((pDfEntry->dfe_AfpId >= AFP_FIRST_DIRID) &&
  3132. (pDfEntry->dfe_pDirEntry->de_ChildForkOpenCount == 0) &&
  3133. ((Now - pDfEntry->dfe_pDirEntry->de_LastAccessTime) > VOLUME_IDDB_AGE_DELAY))
  3134. {
  3135. PDFENTRY pFile, pNext;
  3136. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3137. ("AfpAgeDfEntries: Aging out directory %Z\n", &pDfEntry->dfe_UnicodeName));
  3138. // This directory's files need to be nuked
  3139. pDfEntry->dfe_FileOffspring = 0;
  3140. pDfEntry->dfe_Flags &= ~DFE_FLAGS_FILES_CACHED;
  3141. for (i = 0; i < MAX_CHILD_HASH_BUCKETS; i++)
  3142. {
  3143. for (pFile = pDfEntry->dfe_pDirEntry->de_ChildFile[i];
  3144. pFile != NULL;
  3145. pFile = pNext)
  3146. {
  3147. pNext = pFile->dfe_NextSibling;
  3148. // Unlink it from the hash buckets
  3149. AfpUnlinkDouble(pFile,
  3150. dfe_NextOverflow,
  3151. dfe_PrevOverflow);
  3152. // Nuke it from the cache if it is there
  3153. if (pVolDesc->vds_pDfeCache[HASH_CACHE_ID(pFile->dfe_AfpId)] == pFile)
  3154. {
  3155. pVolDesc->vds_pDfeCache[HASH_CACHE_ID(pFile->dfe_AfpId)] = NULL;
  3156. }
  3157. // Finally free it
  3158. FREE_DFE(pFile);
  3159. }
  3160. pDfEntry->dfe_pDirEntry->de_ChildFile[i] = NULL;
  3161. }
  3162. }
  3163. #if 0
  3164. // NOTE: Should we leave the tree under 'Network Trash Folder' alone ?
  3165. if (pDfEntry->dfe_AfpId == AFP_ID_NETWORK_TRASH)
  3166. continue;
  3167. #endif
  3168. // Pick up all the child directories of this directory and 'push' them on stack
  3169. for (pDir = pDfEntry->dfe_pDirEntry->de_ChildDir;
  3170. pDir != NULL;
  3171. pDir = pDir->dfe_NextSibling)
  3172. {
  3173. Stack[StackPtr++] = pDir;
  3174. }
  3175. }
  3176. AfpFreeMemory(Stack);
  3177. }
  3178. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  3179. }
  3180. #endif
  3181. #if DBG
  3182. NTSTATUS FASTCALL
  3183. afpDumpDfeTree(
  3184. IN PVOID Context
  3185. )
  3186. {
  3187. PVOLDESC pVolDesc;
  3188. PDFENTRY pDfEntry, pChild;
  3189. LONG i, StackPtr;
  3190. if (afpDumpDfeTreeFlag)
  3191. {
  3192. afpDumpDfeTreeFlag = 0;
  3193. for (pVolDesc = AfpVolumeList; pVolDesc != NULL; pVolDesc = pVolDesc->vds_Next)
  3194. {
  3195. StackPtr = 0;
  3196. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3197. ("Volume : %Z\n", &pVolDesc->vds_Name));
  3198. afpDfeStack[StackPtr++] = pVolDesc->vds_pDfeRoot;
  3199. while (StackPtr > 0)
  3200. {
  3201. pDfEntry = afpDfeStack[--StackPtr];
  3202. afpDisplayDfe(pDfEntry);
  3203. for (i = 0; i < MAX_CHILD_HASH_BUCKETS; i++)
  3204. {
  3205. for (pChild = pDfEntry->dfe_pDirEntry->de_ChildFile[i];
  3206. pChild != NULL;
  3207. pChild = pChild->dfe_NextSibling)
  3208. {
  3209. afpDisplayDfe(pChild);
  3210. }
  3211. }
  3212. for (pChild = pDfEntry->dfe_pDirEntry->de_ChildDir;
  3213. pChild != NULL;
  3214. pChild = pChild->dfe_NextSibling)
  3215. {
  3216. afpDfeStack[StackPtr++] = pChild;
  3217. }
  3218. }
  3219. }
  3220. }
  3221. return AFP_ERR_REQUEUE;
  3222. }
  3223. VOID FASTCALL
  3224. afpDisplayDfe(
  3225. IN PDFENTRY pDfEntry
  3226. )
  3227. {
  3228. USHORT i;
  3229. // Figure out the indenting. One space for every depth unit of parent
  3230. // If this is a directory, a '+' and then the dir name
  3231. // If this is a file, then just the file name
  3232. for (i = 0; i < (pDfEntry->dfe_Parent->dfe_DirDepth + 1); i++)
  3233. {
  3234. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3235. ("%c ", 0xB3));
  3236. }
  3237. if (pDfEntry->dfe_NextSibling == NULL)
  3238. {
  3239. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3240. ("%c%c%c%c", 0xC0, 0xC4, 0xC4, 0xC4));
  3241. }
  3242. else
  3243. {
  3244. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3245. ("%c%c%c%c", 0xC3, 0xC4, 0xC4, 0xC4));
  3246. }
  3247. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3248. ("%Z ", &pDfEntry->dfe_UnicodeName));
  3249. if (pDfEntry->dfe_Flags & DFE_FLAGS_DIR)
  3250. {
  3251. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3252. ("(%c, %lx, Id = %lx)\n", 0x9F, pDfEntry, pDfEntry->dfe_AfpId));
  3253. }
  3254. else
  3255. {
  3256. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3257. ("(%c, %lx, Id = %lx)\n", 0x46, pDfEntry, pDfEntry->dfe_AfpId));
  3258. }
  3259. }
  3260. #endif