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

3758 lines
101 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 == pEnumDir->ed_PathName.Length))
  1317. {
  1318. if (((pPath->Length == 0) && (pEnumDir->ed_PathName.Length == 0)) ||
  1319. RtlCompareMemory(pEnumDir->ed_PathName.Buffer,
  1320. pPath->Buffer,
  1321. pPath->Length))
  1322. {
  1323. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1324. ("AfpEnumerate found cache hit\n"));
  1325. INTERLOCKED_INCREMENT_LONG(&AfpServerStatistics.stat_EnumCacheHits);
  1326. *ppEnumDir = pEnumDir;
  1327. Status = AFP_ERR_NONE;
  1328. break;
  1329. }
  1330. }
  1331. // Does not match, cleanup the previous entry
  1332. AfpFreeMemory(pEnumDir);
  1333. pConnDesc->cds_pEnumDir = NULL;
  1334. }
  1335. INTERLOCKED_INCREMENT_LONG(&AfpServerStatistics.stat_EnumCacheMisses);
  1336. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1337. ("AfpEnumerate creating new cache\n"));
  1338. // We have no current enumeration. Create one now
  1339. *ppEnumDir = NULL;
  1340. AfpInitializeFDParms(&FDParm);
  1341. AfpInitializePME(&PME, 0, NULL);
  1342. if (IS_VOLUME_NTFS(pVolDesc))
  1343. {
  1344. NeedHandle = True;
  1345. }
  1346. Status = AfpMapAfpPathForLookup(pConnDesc,
  1347. ParentDirId,
  1348. pPath,
  1349. PathType,
  1350. DFE_DIR,
  1351. DIR_BITMAP_DIRID |
  1352. DIR_BITMAP_GROUPID |
  1353. DIR_BITMAP_OWNERID |
  1354. DIR_BITMAP_ACCESSRIGHTS |
  1355. FD_INTERNAL_BITMAP_OPENACCESS_READCTRL |
  1356. DIR_BITMAP_OFFSPRINGS,
  1357. NeedHandle ? &PME : NULL,
  1358. &FDParm);
  1359. if (Status != AFP_ERR_NONE)
  1360. {
  1361. if (Status == AFP_ERR_OBJECT_NOT_FOUND)
  1362. Status = AFP_ERR_DIR_NOT_FOUND;
  1363. break;
  1364. }
  1365. if (NeedHandle)
  1366. {
  1367. AfpIoClose(&PME.pme_Handle);
  1368. }
  1369. // For admin, set all access bits
  1370. if (pConnDesc->cds_pSda->sda_ClientType == SDA_CLIENT_ADMIN)
  1371. {
  1372. FDParm._fdp_UserRights = DIR_ACCESS_ALL | DIR_ACCESS_OWNER;
  1373. }
  1374. if ((BitmapF != 0) && (FDParm._fdp_UserRights & DIR_ACCESS_READ))
  1375. DFFlags |= DFE_FILE;
  1376. if ((BitmapD != 0) && (FDParm._fdp_UserRights & DIR_ACCESS_SEARCH))
  1377. DFFlags |= DFE_DIR;
  1378. // Catch access denied error here
  1379. if (DFFlags == 0)
  1380. {
  1381. Status = AFP_ERR_ACCESS_DENIED;
  1382. break;
  1383. }
  1384. // All is hunky-dory so far, go ahead with the enumeration now
  1385. #ifdef GET_CORRECT_OFFSPRING_COUNTS
  1386. take_swmr_for_enum:
  1387. #endif
  1388. NeedWriteLock ?
  1389. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock) :
  1390. AfpSwmrAcquireShared(&pVolDesc->vds_IdDbAccessLock);
  1391. ReleaseSwmr = True;
  1392. // Lookup the dfentry of the AfpIdEnumDir
  1393. if ((pDfe = AfpFindDfEntryById(pVolDesc,
  1394. FDParm._fdp_AfpId,
  1395. DFE_DIR)) == NULL)
  1396. {
  1397. Status = AFP_ERR_OBJECT_NOT_FOUND;
  1398. break;
  1399. }
  1400. // Allocate a ENUMDIR structure and initialize it
  1401. EnumCount = 0;
  1402. if (DFFlags & DFE_DIR)
  1403. EnumCount += (DWORD)(pDfe->dfe_DirOffspring);
  1404. if (DFFlags & DFE_FILE)
  1405. EnumCount += (DWORD)(pDfe->dfe_FileOffspring);
  1406. if (EnumCount == 0)
  1407. {
  1408. Status = AFP_ERR_OBJECT_NOT_FOUND;
  1409. break;
  1410. }
  1411. if ((pEnumDir = (PENUMDIR)AfpAllocNonPagedMemory(sizeof(ENUMDIR) +
  1412. pPath->MaximumLength +
  1413. EnumCount*sizeof(EIT))) == NULL)
  1414. {
  1415. Status = AFP_ERR_OBJECT_NOT_FOUND;
  1416. break;
  1417. }
  1418. pEnumDir->ed_ParentDirId = ParentDirId;
  1419. pEnumDir->ed_ChildCount = EnumCount;
  1420. pEnumDir->ed_PathType = PathType;
  1421. pEnumDir->ed_Bitmap = (BitmapF + (BitmapD << 16));
  1422. pEnumDir->ed_BadCount = 0;
  1423. pEnumDir->ed_pEit = pEit = (PEIT)((PBYTE)pEnumDir + sizeof(ENUMDIR));
  1424. AfpSetEmptyAnsiString(&pEnumDir->ed_PathName,
  1425. pPath->MaximumLength,
  1426. (PBYTE)pEnumDir +
  1427. sizeof(ENUMDIR) +
  1428. EnumCount*sizeof(EIT));
  1429. RtlCopyMemory(pEnumDir->ed_PathName.Buffer,
  1430. pPath->Buffer,
  1431. pPath->Length);
  1432. *ppEnumDir = pConnDesc->cds_pEnumDir = pEnumDir;
  1433. // Now copy the enum parameters (Afp Id and file/dir flag) of
  1434. // each of the children, files first
  1435. if (DFFlags & DFE_FILE)
  1436. {
  1437. LONG i;
  1438. for (i = 0; i < MAX_CHILD_HASH_BUCKETS; i++)
  1439. {
  1440. for (pTmp = pDfe->dfe_pDirEntry->de_ChildFile[i];
  1441. pTmp != NULL;
  1442. pTmp = pTmp->dfe_NextSibling, pEit ++)
  1443. {
  1444. ASSERT(!DFE_IS_DIRECTORY(pTmp));
  1445. pEit->eit_Id = pTmp->dfe_AfpId;
  1446. pEit->eit_Flags = DFE_FILE;
  1447. }
  1448. }
  1449. }
  1450. if (DFFlags & DFE_DIR)
  1451. {
  1452. for (pTmp = pDfe->dfe_pDirEntry->de_ChildDir;
  1453. pTmp != NULL;
  1454. pTmp = pTmp->dfe_NextSibling, pEit ++)
  1455. {
  1456. ASSERT(DFE_IS_DIRECTORY(pTmp));
  1457. pEit->eit_Id = pTmp->dfe_AfpId;
  1458. pEit->eit_Flags = DFE_DIR;
  1459. #ifdef GET_CORRECT_OFFSPRING_COUNTS
  1460. // We are returning a directory offspring, make sure
  1461. // that it has its children cached in so we get the correct
  1462. // file and dir offspring counts for it, otherwise Finder
  1463. // 'view by name' doesn't work correctly because it sees
  1464. // zero as the offspring count and clicking on the triangle
  1465. // shows nothing since it tries to be smart and doesn't
  1466. // explicitly enumerate that dir if offspring count is zero.
  1467. //
  1468. // This can be a big performance hit if a directory has lots
  1469. // of subdirectories which in turn have tons of files.
  1470. //
  1471. // JH - Could we alternately return incorrect information about
  1472. // files as long as there are directry children. What else
  1473. // will break ?
  1474. // if (!DFE_CHILDREN_ARE_PRESENT(pTmp) && (pTmp->dfe_DirOffspring == 0))
  1475. if (!DFE_CHILDREN_ARE_PRESENT(pTmp))
  1476. {
  1477. if (!AfpSwmrLockedExclusive(&pVolDesc->vds_IdDbAccessLock) &&
  1478. !AfpSwmrUpgradeToExclusive(&pVolDesc->vds_IdDbAccessLock))
  1479. {
  1480. NeedWriteLock = True;
  1481. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  1482. ReleaseSwmr = False;
  1483. // We must free the memory here in case the next
  1484. // time we enumerate the dir it has more children
  1485. // than it had the first time -- since we must let go
  1486. // of the swmr here things could change.
  1487. AfpFreeMemory(pEnumDir);
  1488. *ppEnumDir = pConnDesc->cds_pEnumDir = NULL;
  1489. goto take_swmr_for_enum;
  1490. }
  1491. AfpCacheDirectoryTree(pVolDesc,
  1492. pTmp,
  1493. GETFILES,
  1494. NULL,
  1495. NULL);
  1496. } // if children not cached
  1497. #endif
  1498. }
  1499. }
  1500. AfpGetCurrentTimeInMacFormat(&pEnumDir->ed_TimeStamp);
  1501. Status = AFP_ERR_NONE;
  1502. } while (False);
  1503. if (ReleaseSwmr)
  1504. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  1505. return Status;
  1506. }
  1507. /*** AfpCatSearch
  1508. *
  1509. * This routine does a left-hand search on the DFE tree to search for
  1510. * file/dirs that match the search criteria indicated in pFDParm1 and
  1511. * pFDParm2.
  1512. *
  1513. * LOCKS: vds_idDbAccessLock (SWMR, Shared or Exclusive)
  1514. */
  1515. AFPSTATUS
  1516. AfpCatSearch(
  1517. IN PCONNDESC pConnDesc,
  1518. IN PCATALOGPOSITION pCatPosition,
  1519. IN DWORD Bitmap,
  1520. IN DWORD FileBitmap,
  1521. IN DWORD DirBitmap,
  1522. IN PFILEDIRPARM pFDParm1,
  1523. IN PFILEDIRPARM pFDParm2,
  1524. IN PUNICODE_STRING pMatchString OPTIONAL,
  1525. IN OUT PDWORD pCount,
  1526. IN SHORT Buflen,
  1527. OUT PSHORT pSizeLeft,
  1528. OUT PBYTE pResults,
  1529. OUT PCATALOGPOSITION pNewCatPosition
  1530. )
  1531. {
  1532. PVOLDESC pVolDesc = pConnDesc->cds_pVolDesc;
  1533. PDFENTRY pCurParent, pCurFile;
  1534. BOOLEAN MatchFiles = True, MatchDirs = True, NewSearch = False;
  1535. BOOLEAN HaveSeeFiles, HaveSeeFolders, CheckAccess = False;
  1536. AFPSTATUS Status = AFP_ERR_NONE;
  1537. LONG i;
  1538. DWORD ActCount = 0;
  1539. SHORT SizeLeft = Buflen;
  1540. PSWMR pSwmr = &(pConnDesc->cds_pVolDesc->vds_IdDbAccessLock);
  1541. USHORT Flags;
  1542. UNICODE_STRING CurPath;
  1543. typedef struct _SearchEntityPkt
  1544. {
  1545. BYTE __Length;
  1546. BYTE __FileDirFlag;
  1547. // The real parameters follow
  1548. } SEP, *PSEP;
  1549. PSEP pSep;
  1550. PAGED_CODE( );
  1551. pSep = (PSEP)pResults;
  1552. RtlZeroMemory(pNewCatPosition, sizeof(CATALOGPOSITION));
  1553. CatSearchStart:
  1554. Flags = pCatPosition->cp_Flags;
  1555. pCurFile = NULL;
  1556. i = MAX_CHILD_HASH_BUCKETS;
  1557. if (Flags & CATFLAGS_WRITELOCK_REQUIRED)
  1558. {
  1559. ASSERT(Flags == (CATFLAGS_SEARCHING_FILES | CATFLAGS_WRITELOCK_REQUIRED));
  1560. AfpSwmrAcquireExclusive(pSwmr);
  1561. Flags &= ~CATFLAGS_WRITELOCK_REQUIRED;
  1562. }
  1563. else
  1564. AfpSwmrAcquireShared(pSwmr);
  1565. if (Flags == 0)
  1566. {
  1567. //
  1568. // Start search from beginning of catalog (i.e. the root directory)
  1569. //
  1570. i = 0;
  1571. pCurParent = pVolDesc->vds_pDfeRoot;
  1572. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1573. if (IS_VOLUME_NTFS(pVolDesc))
  1574. CheckAccess = True;
  1575. Flags = CATFLAGS_SEARCHING_FILES;
  1576. NewSearch = True;
  1577. }
  1578. else
  1579. {
  1580. //
  1581. // This is a continuation of a previous search, pickup where we
  1582. // left off
  1583. //
  1584. AFPTIME CurrentTime;
  1585. AfpGetCurrentTimeInMacFormat(&CurrentTime);
  1586. // If we cannot find the current parent dir specified by this
  1587. // catalog position, or too much time has elapsed since the
  1588. // user last sent in this catalog position, then restart the search
  1589. // from the root dir. The reason we have a time limitation is that
  1590. // if someone made a CatSearch request N minutes ago, and the
  1591. // current position is deep in the tree, the directory permissions
  1592. // higher up in the tree could have changed by now so that the user
  1593. // shouldn't even have access to this part of the tree anymore.
  1594. // Since we do move up in the tree without rechecking permissions,
  1595. // this could happen. (We assume that if we got down to the current
  1596. // position in the tree that we had to have had access higher up
  1597. // in order to get here, so moving up is ok. But if somebody comes
  1598. // back a day later and continues the catsearch where it left off,
  1599. // we shouldn't let them.) It is too expensive to be rechecking
  1600. // parents' parent permissions everytime we move back up the tree.
  1601. if (((CurrentTime - pCatPosition->cp_TimeStamp) >= MAX_CATSEARCH_TIME) ||
  1602. ((pCurParent = AfpFindDfEntryById(pVolDesc,
  1603. pCatPosition->cp_CurParentId,
  1604. DFE_DIR)) == NULL))
  1605. {
  1606. // Start over from root directory
  1607. Status = AFP_ERR_CATALOG_CHANGED;
  1608. DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_WARN,
  1609. ("AfpCatSearch: Time diff >= MAX_CATSEARCH_TIME or couldn't find CurParent Id!\n"));
  1610. pCurParent = pVolDesc->vds_pDfeRoot;
  1611. Flags = CATFLAGS_SEARCHING_FILES;
  1612. pSep = (PSEP)pResults;
  1613. Status = AFP_ERR_NONE;
  1614. MatchFiles = True;
  1615. MatchDirs = True;
  1616. SizeLeft = Buflen;
  1617. ActCount = 0;
  1618. if (IS_VOLUME_NTFS(pVolDesc))
  1619. CheckAccess = True;
  1620. NewSearch = True;
  1621. }
  1622. else if (pCatPosition->cp_TimeStamp < pVolDesc->vds_ModifiedTime)
  1623. {
  1624. Status = AFP_ERR_CATALOG_CHANGED;
  1625. ASSERT(IS_VOLUME_NTFS(pVolDesc));
  1626. DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_WARN,
  1627. ("AfpCatSearch: Catalog timestamp older than IdDb Modtime\n"));
  1628. }
  1629. ASSERT(DFE_IS_DIRECTORY(pCurParent));
  1630. // If we need to resume searching the files for this parent, find the
  1631. // one we should start with, if it is not the first file child.
  1632. if (Flags & CATFLAGS_SEARCHING_FILES)
  1633. {
  1634. //
  1635. // Default is to start with parent's first child which
  1636. // may or may not be null depending on if the parent has had
  1637. // its file children cached in or not. If we are restarting a
  1638. // search because we had to let go of the IdDb SWMR in order to
  1639. // reaquire for Exclusive access, this parent's children could
  1640. // very well have been cached in by someone else in the mean time.
  1641. // If so then we will pick it up here.
  1642. //
  1643. i = 0;
  1644. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1645. if (pCatPosition->cp_NextFileId != 0)
  1646. {
  1647. // Find the DFE corresponding to the next fileID to look at
  1648. if (((pCurFile = AfpFindDfEntryById(pVolDesc,
  1649. pCatPosition->cp_NextFileId,
  1650. DFE_FILE)) == NULL) ||
  1651. (pCurFile->dfe_Parent != pCurParent))
  1652. {
  1653. // If we can't find the file that was specified, start over
  1654. // with this parent's first file child and indicate there may
  1655. // be duplicates returned or files missed
  1656. Status = AFP_ERR_CATALOG_CHANGED;
  1657. DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_WARN,
  1658. ("AfpCatSearch: Could not find file Child ID!\n"));
  1659. i = 0;
  1660. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1661. }
  1662. else
  1663. {
  1664. i = (pCurFile->dfe_NameHash % MAX_CHILD_HASH_BUCKETS);
  1665. }
  1666. }
  1667. }
  1668. }
  1669. if (pFDParm1->_fdp_Flags == DFE_FLAGS_FILE_WITH_ID)
  1670. MatchDirs = False;
  1671. else if (pFDParm1->_fdp_Flags == DFE_FLAGS_DIR)
  1672. MatchFiles = False;
  1673. if (NewSearch && MatchDirs)
  1674. {
  1675. SHORT Length;
  1676. ASSERT (DFE_IS_ROOT(pCurParent));
  1677. // See if the volume root itself is a match
  1678. if ((Length = AfpIsCatSearchMatch(pCurParent,
  1679. Bitmap,
  1680. DirBitmap,
  1681. pFDParm1,
  1682. pFDParm2,
  1683. pMatchString)) != 0)
  1684. {
  1685. ASSERT(Length <= SizeLeft);
  1686. PUTSHORT2BYTE(&pSep->__Length, Length - sizeof(SEP));
  1687. pSep->__FileDirFlag = FILEDIR_FLAG_DIR;
  1688. afpPackSearchParms(pCurParent,
  1689. DirBitmap,
  1690. (PBYTE)pSep + sizeof(SEP));
  1691. pSep = (PSEP)((PBYTE)pSep + Length);
  1692. SizeLeft -= Length;
  1693. ASSERT(SizeLeft >= 0);
  1694. ActCount ++;
  1695. }
  1696. }
  1697. NewSearch = False;
  1698. while (True)
  1699. {
  1700. HaveSeeFiles = HaveSeeFolders = True;
  1701. //
  1702. // First time thru, if we are resuming a search and need to start
  1703. // with the pCurParent's sibling, then do so.
  1704. //
  1705. if (Flags & CATFLAGS_SEARCHING_SIBLING)
  1706. {
  1707. Flags &= ~CATFLAGS_SEARCHING_SIBLING;
  1708. goto check_sibling;
  1709. }
  1710. //
  1711. // If we have not searched this directory yet and this is NTFS, check
  1712. // that user has seefiles/seefolders access in this directory
  1713. //
  1714. if (CheckAccess)
  1715. {
  1716. BYTE UserRights;
  1717. NTSTATUS PermStatus;
  1718. ASSERT(IS_VOLUME_NTFS(pVolDesc));
  1719. AfpSetEmptyUnicodeString(&CurPath, 0, NULL);
  1720. // Get the root relative path of this directory
  1721. if (NT_SUCCESS(AfpHostPathFromDFEntry(pCurParent,
  1722. 0,
  1723. &CurPath)))
  1724. {
  1725. // Check for SeeFiles/SeeFolders which is the most common case
  1726. if (!NT_SUCCESS((PermStatus = AfpCheckParentPermissions(pConnDesc,
  1727. pCurParent->dfe_AfpId,
  1728. &CurPath,
  1729. DIR_ACCESS_READ | DIR_ACCESS_SEARCH,
  1730. NULL,
  1731. &UserRights))))
  1732. {
  1733. if (PermStatus == AFP_ERR_ACCESS_DENIED)
  1734. {
  1735. if ((UserRights & DIR_ACCESS_READ) == 0)
  1736. HaveSeeFiles = False;
  1737. if ((UserRights & DIR_ACCESS_SEARCH) == 0)
  1738. HaveSeeFolders = False;
  1739. }
  1740. else
  1741. HaveSeeFiles = HaveSeeFolders = False;
  1742. }
  1743. if (CurPath.Buffer != NULL)
  1744. AfpFreeMemory(CurPath.Buffer);
  1745. }
  1746. else
  1747. {
  1748. HaveSeeFiles = HaveSeeFolders = False;
  1749. DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_ERR,
  1750. ("AfpCatSearch: Could not get host path from DFE!!\n"));
  1751. }
  1752. CheckAccess = False;
  1753. }
  1754. // Search the files first if have seefiles access on the current
  1755. // parent and the user has asked for file matches. If we are
  1756. // resuming a search by looking at a directory child first, don't look
  1757. // at the files.
  1758. if (HaveSeeFiles && MatchFiles && (Flags & CATFLAGS_SEARCHING_FILES))
  1759. {
  1760. PDFENTRY pDFE;
  1761. SHORT Length;
  1762. AFPSTATUS subStatus = AFP_ERR_NONE, subsubStatus = AFP_ERR_NONE;
  1763. if (!DFE_CHILDREN_ARE_PRESENT(pCurParent))
  1764. {
  1765. if (!AfpSwmrLockedExclusive(pSwmr) &&
  1766. !AfpSwmrUpgradeToExclusive(pSwmr))
  1767. {
  1768. if (ActCount > 0)
  1769. {
  1770. // We have at least one thing to return to the user,
  1771. // so return it now and set the flag for next time
  1772. // to take the write lock.
  1773. pNewCatPosition->cp_NextFileId = 0;
  1774. Flags |= CATFLAGS_WRITELOCK_REQUIRED;
  1775. break; // out of while loop
  1776. }
  1777. else
  1778. {
  1779. // Let go of lock and reaquire it for exclusive
  1780. // access. Start over where we left off here if
  1781. // possible. Put a new timestamp in the catalog
  1782. // position so if it changes between the time we let
  1783. // go of the lock and reaquire it for exclusive access,
  1784. // we will return AFP_ERR_CATALOG_CHANGED since
  1785. // something could change while we don't own the lock.
  1786. AfpSwmrRelease(pSwmr);
  1787. pCatPosition->cp_Flags = CATFLAGS_WRITELOCK_REQUIRED |
  1788. CATFLAGS_SEARCHING_FILES;
  1789. pCatPosition->cp_CurParentId = pCurParent->dfe_AfpId;
  1790. pCatPosition->cp_NextFileId = 0;
  1791. AfpGetCurrentTimeInMacFormat(&pCatPosition->cp_TimeStamp);
  1792. DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO,
  1793. ("AfpCatSearch: Lock released; reaquiring Exclusive\n"));
  1794. goto CatSearchStart;
  1795. }
  1796. }
  1797. AfpCacheDirectoryTree(pVolDesc,
  1798. pCurParent,
  1799. GETFILES,
  1800. NULL,
  1801. NULL);
  1802. i = 0;
  1803. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1804. // If we have the exclusive lock, downgrade it to shared so
  1805. // we don't lock out others who want to read.
  1806. if (AfpSwmrLockedExclusive(pSwmr))
  1807. AfpSwmrDowngradeToShared(pSwmr);
  1808. }
  1809. //
  1810. // Search files for matches. If we are picking up in the middle
  1811. // of searching the files, then start with the right one as pointed
  1812. // at by pCurFile.
  1813. //
  1814. while (TRUE)
  1815. {
  1816. while (pCurFile == NULL)
  1817. {
  1818. i ++;
  1819. if (i < MAX_CHILD_HASH_BUCKETS)
  1820. {
  1821. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[i];
  1822. }
  1823. else
  1824. {
  1825. subsubStatus = STATUS_NO_MORE_FILES;
  1826. break; // out of while (pCurFile == NULL)
  1827. }
  1828. }
  1829. if (subsubStatus != AFP_ERR_NONE)
  1830. {
  1831. break;
  1832. }
  1833. ASSERT(pCurFile->dfe_Parent == pCurParent);
  1834. if ((Length = AfpIsCatSearchMatch(pCurFile,
  1835. Bitmap,
  1836. FileBitmap,
  1837. pFDParm1,
  1838. pFDParm2,
  1839. pMatchString)) != 0)
  1840. {
  1841. // Add this to the output buffer if there is room
  1842. if ((Length <= SizeLeft) && (ActCount < *pCount))
  1843. {
  1844. PUTSHORT2BYTE(&pSep->__Length, Length - sizeof(SEP));
  1845. pSep->__FileDirFlag = FILEDIR_FLAG_FILE;
  1846. afpPackSearchParms(pCurFile,
  1847. FileBitmap,
  1848. (PBYTE)pSep + sizeof(SEP));
  1849. pSep = (PSEP)((PBYTE)pSep + Length);
  1850. SizeLeft -= Length;
  1851. ASSERT(SizeLeft >= 0);
  1852. ActCount ++;
  1853. }
  1854. else
  1855. {
  1856. // We don't have enough room to return this entry, or
  1857. // we already have found the requested count. So this
  1858. // will be where we pick up from on the next search
  1859. pNewCatPosition->cp_NextFileId = pCurFile->dfe_AfpId;
  1860. subStatus = STATUS_BUFFER_OVERFLOW;
  1861. break;
  1862. }
  1863. }
  1864. pCurFile = pCurFile->dfe_NextSibling;
  1865. }
  1866. if (subStatus != AFP_ERR_NONE)
  1867. {
  1868. break; // out of while loop
  1869. }
  1870. Flags = 0;
  1871. }
  1872. // If have seefolders on curparent and curparent has a dir child,
  1873. // Move down the tree to the parent's first directory branch
  1874. if (HaveSeeFolders && (pCurParent->dfe_pDirEntry->de_ChildDir != NULL))
  1875. {
  1876. SHORT Length;
  1877. // If user has asked for directory matches, try the parent's
  1878. // first directory child as a match
  1879. if (MatchDirs &&
  1880. ((Length = AfpIsCatSearchMatch(pCurParent->dfe_pDirEntry->de_ChildDir,
  1881. Bitmap,
  1882. DirBitmap,
  1883. pFDParm1,
  1884. pFDParm2,
  1885. pMatchString)) != 0))
  1886. {
  1887. // Add this to the output buffer if there is room
  1888. if ((Length <= SizeLeft) && (ActCount < *pCount))
  1889. {
  1890. PUTSHORT2BYTE(&pSep->__Length, Length - sizeof(SEP));
  1891. pSep->__FileDirFlag = FILEDIR_FLAG_DIR;
  1892. afpPackSearchParms(pCurParent->dfe_pDirEntry->de_ChildDir,
  1893. DirBitmap,
  1894. (PBYTE)pSep + sizeof(SEP));
  1895. pSep = (PSEP)((PBYTE)pSep + Length);
  1896. SizeLeft -= Length;
  1897. ASSERT(SizeLeft >= 0);
  1898. ActCount ++;
  1899. }
  1900. else
  1901. {
  1902. // We don't have enough room to return this entry, so
  1903. // it will be where we pick up from on the next search
  1904. Flags = CATFLAGS_SEARCHING_DIRCHILD;
  1905. break;
  1906. }
  1907. }
  1908. // Make the current parent's first dir child the new pCurParent
  1909. // and continue the search from there.
  1910. pCurParent = pCurParent->dfe_pDirEntry->de_ChildDir;
  1911. if (IS_VOLUME_NTFS(pVolDesc))
  1912. CheckAccess = True;
  1913. Flags = CATFLAGS_SEARCHING_FILES;
  1914. i = 0;
  1915. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1916. continue;
  1917. }
  1918. // We either don't have the access rights to go into any directories
  1919. // under this parent, or the current parent did not have any directory
  1920. // children. See if it has any siblings. We know we have access to
  1921. // see this level of siblings since we are at this level in the first
  1922. // place.
  1923. check_sibling:
  1924. if (pCurParent->dfe_NextSibling != NULL)
  1925. {
  1926. SHORT Length;
  1927. // If user has asked for directory matches, try the parent's
  1928. // next sibling as a match
  1929. if (MatchDirs &&
  1930. ((Length = AfpIsCatSearchMatch(pCurParent->dfe_NextSibling,
  1931. Bitmap,
  1932. DirBitmap,
  1933. pFDParm1,
  1934. pFDParm2,
  1935. pMatchString)) != 0))
  1936. {
  1937. // Add this to the output buffer if there is room
  1938. if ((Length <= SizeLeft) && (ActCount < *pCount))
  1939. {
  1940. PUTSHORT2BYTE(&pSep->__Length, Length - sizeof(SEP));
  1941. pSep->__FileDirFlag = FILEDIR_FLAG_DIR;
  1942. afpPackSearchParms(pCurParent->dfe_NextSibling,
  1943. DirBitmap,
  1944. (PBYTE)pSep + sizeof(SEP));
  1945. pSep = (PSEP)((PBYTE)pSep + Length);
  1946. SizeLeft -= Length;
  1947. ASSERT(SizeLeft >= 0);
  1948. ActCount ++;
  1949. }
  1950. else
  1951. {
  1952. // We don't have enough room to return this entry, so
  1953. // it will be where we pick up from on the next search
  1954. Flags = CATFLAGS_SEARCHING_SIBLING;
  1955. break;
  1956. }
  1957. }
  1958. // Make the current parent's next sibling the new pCurParent and
  1959. // continue the search from there
  1960. pCurParent = pCurParent->dfe_NextSibling;
  1961. if (IS_VOLUME_NTFS(pVolDesc))
  1962. CheckAccess = True;
  1963. Flags = CATFLAGS_SEARCHING_FILES;
  1964. i = 0;
  1965. pCurFile = pCurParent->dfe_pDirEntry->de_ChildFile[0];
  1966. continue;
  1967. }
  1968. // When we hit the root directory again we have searched everything.
  1969. if (DFE_IS_ROOT(pCurParent))
  1970. {
  1971. Status = AFP_ERR_EOF;
  1972. break;
  1973. }
  1974. // Move back up the tree and see if the parent has a sibling to
  1975. // traverse. If not, then it will come back here and move up
  1976. // the tree again till it finds a node with a sibling or hits
  1977. // the root.
  1978. pCurParent = pCurParent->dfe_Parent;
  1979. goto check_sibling;
  1980. }
  1981. if ((Status == AFP_ERR_NONE) || (Status == AFP_ERR_CATALOG_CHANGED) ||
  1982. (Status == AFP_ERR_EOF))
  1983. {
  1984. // return the current catalog position and number of items returned
  1985. if (Status != AFP_ERR_EOF)
  1986. {
  1987. ASSERT(Flags != 0);
  1988. ASSERT(ActCount > 0);
  1989. pNewCatPosition->cp_Flags = Flags;
  1990. pNewCatPosition->cp_CurParentId = pCurParent->dfe_AfpId;
  1991. AfpGetCurrentTimeInMacFormat(&pNewCatPosition->cp_TimeStamp);
  1992. }
  1993. *pCount = ActCount;
  1994. ASSERT(SizeLeft >= 0);
  1995. *pSizeLeft = SizeLeft;
  1996. }
  1997. AfpSwmrRelease(pSwmr);
  1998. return Status;
  1999. }
  2000. /*** afpPackSearchParms
  2001. *
  2002. *
  2003. * LOCKS_ASSUMED: vds_IdDbAccessLock (Shared or Exclusive)
  2004. */
  2005. VOID
  2006. afpPackSearchParms(
  2007. IN PDFENTRY pDfe,
  2008. IN DWORD Bitmap,
  2009. IN PBYTE pBuf
  2010. )
  2011. {
  2012. DWORD Offset = 0;
  2013. ANSI_STRING AName;
  2014. BYTE NameBuf[AFP_LONGNAME_LEN+1];
  2015. PAGED_CODE( );
  2016. RtlZeroMemory (NameBuf, AFP_LONGNAME_LEN+1);
  2017. if (Bitmap & FD_BITMAP_PARENT_DIRID)
  2018. {
  2019. PUTDWORD2DWORD(pBuf, pDfe->dfe_Parent->dfe_AfpId);
  2020. Offset += sizeof(DWORD);
  2021. }
  2022. if (Bitmap & FD_BITMAP_LONGNAME)
  2023. {
  2024. PUTDWORD2SHORT(pBuf + Offset, Offset + sizeof(USHORT));
  2025. Offset += sizeof(USHORT);
  2026. #ifndef DBCS
  2027. // 1996.09.26 V-HIDEKK
  2028. PUTSHORT2BYTE(pBuf + Offset, pDfe->dfe_UnicodeName.Length/sizeof(WCHAR));
  2029. #endif
  2030. AfpInitAnsiStringWithNonNullTerm(&AName, sizeof(NameBuf), NameBuf);
  2031. AfpConvertMungedUnicodeToAnsi(&pDfe->dfe_UnicodeName,
  2032. &AName);
  2033. #ifdef DBCS
  2034. // FiX #11992 SFM: As a result of search, I get incorrect file information.
  2035. // 1996.09.26 V-HIDEKK
  2036. PUTSHORT2BYTE(pBuf + Offset, AName.Length);
  2037. #endif
  2038. RtlCopyMemory(pBuf + Offset + sizeof(BYTE),
  2039. NameBuf,
  2040. AName.Length);
  2041. #ifdef DBCS
  2042. // FiX #11992 SFM: As a result of search, I get incorrect file information.
  2043. // 1996.09.26 V-HIDEKK
  2044. Offset += sizeof(BYTE) + AName.Length;
  2045. #else
  2046. Offset += sizeof(BYTE) + pDfe->dfe_UnicodeName.Length/sizeof(WCHAR);
  2047. #endif
  2048. }
  2049. if (Offset & 1)
  2050. *(pBuf + Offset) = 0;
  2051. }
  2052. /*** AfpSetDFFileFlags
  2053. *
  2054. * Set or clear the DAlreadyOpen or RAlreadyOpen flags for a DFEntry of type
  2055. * File, or mark the file as having a FileId assigned.
  2056. *
  2057. * LOCKS: vds_idDbAccessLock (SWMR, Exclusive)
  2058. */
  2059. AFPSTATUS
  2060. AfpSetDFFileFlags(
  2061. IN PVOLDESC pVolDesc,
  2062. IN DWORD AfpId,
  2063. IN DWORD Flags OPTIONAL,
  2064. IN BOOLEAN SetFileId,
  2065. IN BOOLEAN ClrFileId
  2066. )
  2067. {
  2068. PDFENTRY pDfeFile;
  2069. AFPSTATUS Status = AFP_ERR_NONE;
  2070. PAGED_CODE( );
  2071. ASSERT(!(SetFileId | ClrFileId) || (SetFileId ^ ClrFileId));
  2072. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
  2073. pDfeFile = AfpFindDfEntryById(pVolDesc, AfpId, DFE_FILE);
  2074. if (pDfeFile != NULL)
  2075. {
  2076. #ifdef AGE_DFES
  2077. if (IS_VOLUME_AGING_DFES(pVolDesc))
  2078. {
  2079. if (Flags)
  2080. {
  2081. pDfeFile->dfe_Parent->dfe_pDirEntry->de_ChildForkOpenCount ++;
  2082. }
  2083. }
  2084. #endif
  2085. pDfeFile->dfe_Flags |= (Flags & DFE_FLAGS_OPEN_BITS);
  2086. if (SetFileId)
  2087. {
  2088. if (DFE_IS_FILE_WITH_ID(pDfeFile))
  2089. Status = AFP_ERR_ID_EXISTS;
  2090. DFE_SET_FILE_ID(pDfeFile);
  2091. }
  2092. if (ClrFileId)
  2093. {
  2094. if (!DFE_IS_FILE_WITH_ID(pDfeFile))
  2095. Status = AFP_ERR_ID_NOT_FOUND;
  2096. DFE_CLR_FILE_ID(pDfeFile);
  2097. }
  2098. }
  2099. else Status = AFP_ERR_OBJECT_NOT_FOUND;
  2100. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  2101. return Status;
  2102. }
  2103. /*** AfpCacheParentModTime
  2104. *
  2105. * When the contents of a directory change, the parent LastMod time must be
  2106. * updated. Since we don't want to wait for a notification of this,
  2107. * certain apis must go query for the new parent mod time and cache it.
  2108. * These include: CreateDir, CreateFile, CopyFile (Dest), Delete,
  2109. * Move (Src & Dest), Rename and ExchangeFiles.
  2110. *
  2111. * LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Exclusive)
  2112. */
  2113. VOID
  2114. AfpCacheParentModTime(
  2115. IN PVOLDESC pVolDesc,
  2116. IN PFILESYSHANDLE pHandle OPTIONAL, // if pPath not supplied
  2117. IN PUNICODE_STRING pPath OPTIONAL, // if pHandle not supplied
  2118. IN PDFENTRY pDfeParent OPTIONAL, // if ParentId not supplied
  2119. IN DWORD ParentId OPTIONAL // if pDfeParent not supplied
  2120. )
  2121. {
  2122. FILESYSHANDLE fshParent;
  2123. PFILESYSHANDLE phParent;
  2124. NTSTATUS Status;
  2125. PAGED_CODE( );
  2126. ASSERT(AfpSwmrLockedExclusive(&pVolDesc->vds_IdDbAccessLock));
  2127. if (!ARGUMENT_PRESENT(pDfeParent))
  2128. {
  2129. ASSERT(ARGUMENT_PRESENT((ULONG_PTR)ParentId));
  2130. pDfeParent = AfpFindDfEntryById(pVolDesc, ParentId, DFE_DIR);
  2131. if (pDfeParent == NULL)
  2132. {
  2133. return;
  2134. }
  2135. }
  2136. if (!ARGUMENT_PRESENT(pHandle))
  2137. {
  2138. ASSERT(ARGUMENT_PRESENT(pPath));
  2139. Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  2140. AFP_STREAM_DATA,
  2141. FILEIO_OPEN_DIR,
  2142. pPath,
  2143. FILEIO_ACCESS_NONE,
  2144. FILEIO_DENY_NONE,
  2145. False,
  2146. &fshParent);
  2147. if (!NT_SUCCESS(Status))
  2148. {
  2149. return;
  2150. }
  2151. phParent = &fshParent;
  2152. }
  2153. else
  2154. {
  2155. ASSERT(pHandle->fsh_FileHandle != NULL);
  2156. phParent = pHandle;
  2157. }
  2158. AfpIoQueryTimesnAttr(phParent,
  2159. NULL,
  2160. &pDfeParent->dfe_LastModTime,
  2161. NULL);
  2162. if (!ARGUMENT_PRESENT(pHandle))
  2163. {
  2164. AfpIoClose(&fshParent);
  2165. }
  2166. }
  2167. /*** afpAllocDfe
  2168. *
  2169. * Allocate a DFE from the DFE Blocks. The DFEs are allocated in 4K chunks and internally
  2170. * managed. The idea is primarily to reduce the number of faults we may take during
  2171. * enumeration/pathmap code in faulting in multiple pages to get multiple DFEs.
  2172. *
  2173. * The DFEs are allocated out of paged memory.
  2174. *
  2175. * It is important to keep blocks which are all used up at the end, so that if we hit a
  2176. * block which is empty, we can stop.
  2177. *
  2178. * LOCKS: afpDfeBlockLock (SWMR, Exclusive)
  2179. */
  2180. LOCAL PDFENTRY FASTCALL
  2181. afpAllocDfe(
  2182. IN LONG Index,
  2183. IN BOOLEAN fDir
  2184. )
  2185. {
  2186. PDFEBLOCK pDfb;
  2187. PDFENTRY pDfEntry = NULL;
  2188. #ifdef PROFILING
  2189. TIME TimeS, TimeE, TimeD;
  2190. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_DFEAllocCount);
  2191. AfpGetPerfCounter(&TimeS);
  2192. #endif
  2193. PAGED_CODE( );
  2194. ASSERT ((Index >= 0) && (Index < MAX_BLOCK_TYPE));
  2195. AfpSwmrAcquireExclusive(&afpDfeBlockLock);
  2196. // If the block head has no free entries then there are none !!
  2197. // Pick the right block based on whether it is file or dir
  2198. pDfb = fDir ? afpDirDfePartialBlockHead[Index] : afpFileDfePartialBlockHead[Index];
  2199. if (pDfb == NULL)
  2200. {
  2201. //
  2202. // There are no partial blocks. Check if there any free ones and if there move them to partial
  2203. // since we about to allocate from them
  2204. //
  2205. if (fDir)
  2206. {
  2207. pDfb = afpDirDfeFreeBlockHead[Index];
  2208. if (pDfb != NULL)
  2209. {
  2210. AfpUnlinkDouble(pDfb, dfb_Next, dfb_Prev);
  2211. AfpLinkDoubleAtHead(afpDirDfePartialBlockHead[Index],
  2212. pDfb,
  2213. dfb_Next,
  2214. dfb_Prev);
  2215. }
  2216. }
  2217. else
  2218. {
  2219. pDfb = afpFileDfeFreeBlockHead[Index];
  2220. if (pDfb != NULL)
  2221. {
  2222. AfpUnlinkDouble(pDfb, dfb_Next, dfb_Prev);
  2223. AfpLinkDoubleAtHead(afpFileDfePartialBlockHead[Index],
  2224. pDfb,
  2225. dfb_Next,
  2226. dfb_Prev);
  2227. }
  2228. }
  2229. }
  2230. if (pDfb != NULL)
  2231. {
  2232. ASSERT(VALID_DFB(pDfb));
  2233. ASSERT((fDir && (pDfb->dfb_NumFree <= afpDfeNumDirBlocks[Index])) ||
  2234. (!fDir && (pDfb->dfb_NumFree <= afpDfeNumFileBlocks[Index])));
  2235. ASSERT (pDfb->dfb_NumFree != 0);
  2236. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  2237. ("afpAllocDfe: Found space in Block %lx\n", pDfb));
  2238. }
  2239. if (pDfb == NULL)
  2240. {
  2241. ASSERT(QUAD_SIZED(sizeof(DFEBLOCK)));
  2242. ASSERT(QUAD_SIZED(sizeof(DIRENTRY)));
  2243. ASSERT(QUAD_SIZED(sizeof(DFENTRY)));
  2244. if ((pDfb = (PDFEBLOCK)AfpAllocateVirtualMemoryPage()) != NULL)
  2245. {
  2246. LONG i;
  2247. USHORT DfeSize, UnicodeSize, MaxDfes, DirEntrySize;
  2248. #if DBG
  2249. afpDfbAllocCount ++;
  2250. #endif
  2251. UnicodeSize = afpDfeUnicodeBufSize[Index];
  2252. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
  2253. ("afpAllocDfe: No free %s blocks. Allocated a new block %lx for index %ld\n",
  2254. fDir ? "Dir" : "File", pDfb, Index));
  2255. //
  2256. // Link it in the partial list as we are about to allocate one block out of it anyway.
  2257. //
  2258. if (fDir)
  2259. {
  2260. AfpLinkDoubleAtHead(afpDirDfePartialBlockHead[Index],
  2261. pDfb,
  2262. dfb_Next,
  2263. dfb_Prev);
  2264. DfeSize = afpDfeDirBlockSize[Index];
  2265. pDfb->dfb_NumFree = MaxDfes = afpDfeNumDirBlocks[Index];
  2266. DirEntrySize = sizeof(DIRENTRY);
  2267. }
  2268. else
  2269. {
  2270. AfpLinkDoubleAtHead(afpFileDfePartialBlockHead[Index],
  2271. pDfb,
  2272. dfb_Next,
  2273. dfb_Prev);
  2274. DfeSize = afpDfeFileBlockSize[Index];
  2275. pDfb->dfb_NumFree = MaxDfes = afpDfeNumFileBlocks[Index];
  2276. DirEntrySize = 0;
  2277. }
  2278. ASSERT(QUAD_SIZED(DfeSize));
  2279. pDfb->dfb_fDir = fDir;
  2280. pDfb->dfb_Age = 0;
  2281. // Initialize the list of free dfentries
  2282. for (i = 0, pDfEntry = pDfb->dfb_FreeHead = (PDFENTRY)((PBYTE)pDfb + sizeof(DFEBLOCK));
  2283. i < MaxDfes;
  2284. i++, pDfEntry = pDfEntry->dfe_NextFree)
  2285. {
  2286. pDfEntry->dfe_NextFree = (i == (MaxDfes - 1)) ?
  2287. NULL :
  2288. (PDFENTRY)((PBYTE)pDfEntry + DfeSize);
  2289. pDfEntry->dfe_pDirEntry = fDir ?
  2290. pDfEntry->dfe_pDirEntry = (PDIRENTRY)((PCHAR)pDfEntry+sizeof(DFENTRY)) : NULL;
  2291. pDfEntry->dfe_UnicodeName.Buffer = (PWCHAR)((PCHAR)pDfEntry+
  2292. DirEntrySize+
  2293. sizeof(DFENTRY));
  2294. pDfEntry->dfe_UnicodeName.MaximumLength = UnicodeSize;
  2295. }
  2296. }
  2297. else
  2298. {
  2299. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2300. ("afpAllocDfe: AfpAllocateVirtualMemoryPage failed\n"));
  2301. AFPLOG_ERROR(AFPSRVMSG_VIRTMEM_ALLOC_FAILED,
  2302. STATUS_INSUFFICIENT_RESOURCES,
  2303. NULL,
  2304. 0,
  2305. NULL);
  2306. }
  2307. }
  2308. if (pDfb != NULL)
  2309. {
  2310. PDFEBLOCK pTmp;
  2311. ASSERT(VALID_DFB(pDfb));
  2312. pDfEntry = pDfb->dfb_FreeHead;
  2313. ASSERT(VALID_DFE(pDfEntry));
  2314. ASSERT(pDfb->dfb_fDir ^ (pDfEntry->dfe_pDirEntry == NULL));
  2315. #if DBG
  2316. afpDfeAllocCount ++;
  2317. #endif
  2318. pDfb->dfb_FreeHead = pDfEntry->dfe_NextFree;
  2319. pDfb->dfb_NumFree --;
  2320. //
  2321. // If the block is now empty (completely used), unlink it from here and move it
  2322. // to the Used list.
  2323. //
  2324. if (pDfb->dfb_NumFree == 0)
  2325. {
  2326. AfpUnlinkDouble(pDfb, dfb_Next, dfb_Prev);
  2327. if (fDir)
  2328. {
  2329. AfpLinkDoubleAtHead(afpDirDfeUsedBlockHead[Index],
  2330. pDfb,
  2331. dfb_Next,
  2332. dfb_Prev);
  2333. }
  2334. else
  2335. {
  2336. AfpLinkDoubleAtHead(afpFileDfeUsedBlockHead[Index],
  2337. pDfb,
  2338. dfb_Next,
  2339. dfb_Prev);
  2340. }
  2341. }
  2342. pDfEntry->dfe_UnicodeName.Length = 0;
  2343. }
  2344. AfpSwmrRelease(&afpDfeBlockLock);
  2345. #ifdef PROFILING
  2346. AfpGetPerfCounter(&TimeE);
  2347. TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
  2348. INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_DFEAllocTime,
  2349. TimeD,
  2350. &AfpStatisticsLock);
  2351. #endif
  2352. if ((pDfEntry != NULL) &&
  2353. (pDfEntry->dfe_pDirEntry != NULL))
  2354. {
  2355. // For a directory ZERO out the directory entry
  2356. RtlZeroMemory(&pDfEntry->dfe_pDirEntry->de_ChildDir, sizeof(DIRENTRY));
  2357. }
  2358. return pDfEntry;
  2359. }
  2360. /*** afpFreeDfe
  2361. *
  2362. * Return a DFE to the allocation block.
  2363. *
  2364. * LOCKS: afpDfeBlockLock (SWMR, Exclusive)
  2365. */
  2366. LOCAL VOID FASTCALL
  2367. afpFreeDfe(
  2368. IN PDFENTRY pDfEntry
  2369. )
  2370. {
  2371. PDFEBLOCK pDfb;
  2372. USHORT NumBlks, index;
  2373. #ifdef PROFILING
  2374. TIME TimeS, TimeE, TimeD;
  2375. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_DFEFreeCount);
  2376. AfpGetPerfCounter(&TimeS);
  2377. #endif
  2378. PAGED_CODE( );
  2379. // NOTE: The following code *depends* on the fact that we allocate DFBs as
  2380. // 64K blocks and also that these are allocated *at* 64K boundaries
  2381. // This lets us *cheaply* get to the owning DFB from the DFE.
  2382. pDfb = (PDFEBLOCK)((ULONG_PTR)pDfEntry & ~(PAGE_SIZE-1));
  2383. ASSERT(VALID_DFB(pDfb));
  2384. ASSERT(pDfb->dfb_fDir ^ (pDfEntry->dfe_pDirEntry == NULL));
  2385. AfpSwmrAcquireExclusive(&afpDfeBlockLock);
  2386. #if DBG
  2387. afpDfeAllocCount --;
  2388. #endif
  2389. index = USIZE_TO_INDEX(pDfEntry->dfe_UnicodeName.MaximumLength);
  2390. NumBlks = (pDfb->dfb_fDir) ? afpDfeNumDirBlocks[index] : afpDfeNumFileBlocks[index];
  2391. ASSERT((pDfb->dfb_fDir && (pDfb->dfb_NumFree < NumBlks)) ||
  2392. (!pDfb->dfb_fDir && (pDfb->dfb_NumFree < NumBlks)));
  2393. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  2394. ("AfpFreeDfe: Returning pDfEntry %lx to Block %lx, size %d\n",
  2395. pDfEntry, pDfb, pDfEntry->dfe_UnicodeName.MaximumLength));
  2396. pDfb->dfb_NumFree ++;
  2397. pDfEntry->dfe_NextFree = pDfb->dfb_FreeHead;
  2398. pDfb->dfb_FreeHead = pDfEntry;
  2399. if (pDfb->dfb_NumFree == 1)
  2400. {
  2401. LONG Index;
  2402. //
  2403. // The block is now partially free (it used to be completely used). move it to the partial list.
  2404. //
  2405. Index = USIZE_TO_INDEX(pDfEntry->dfe_UnicodeName.MaximumLength);
  2406. AfpUnlinkDouble(pDfb, dfb_Next, dfb_Prev);
  2407. if (pDfb->dfb_fDir)
  2408. {
  2409. AfpLinkDoubleAtHead(afpDirDfePartialBlockHead[Index],
  2410. pDfb,
  2411. dfb_Next,
  2412. dfb_Prev);
  2413. }
  2414. else
  2415. {
  2416. AfpLinkDoubleAtHead(afpFileDfePartialBlockHead[Index],
  2417. pDfb,
  2418. dfb_Next,
  2419. dfb_Prev);
  2420. }
  2421. }
  2422. else if (pDfb->dfb_NumFree == NumBlks)
  2423. {
  2424. LONG Index;
  2425. //
  2426. // The block is now completely free (used to be partially used). move it to the free list
  2427. //
  2428. Index = USIZE_TO_INDEX(pDfEntry->dfe_UnicodeName.MaximumLength);
  2429. pDfb->dfb_Age = 0;
  2430. AfpUnlinkDouble(pDfb, dfb_Next, dfb_Prev);
  2431. if (AfpServerState == AFP_STATE_STOP_PENDING)
  2432. {
  2433. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
  2434. ("afpFreeDfe: Freeing Block %lx\n", pDfb));
  2435. AfpFreeVirtualMemoryPage(pDfb);
  2436. #if DBG
  2437. afpDfbAllocCount --;
  2438. #endif
  2439. }
  2440. else
  2441. {
  2442. if (pDfb->dfb_fDir)
  2443. {
  2444. AfpLinkDoubleAtHead(afpDirDfeFreeBlockHead[Index],
  2445. pDfb,
  2446. dfb_Next,
  2447. dfb_Prev);
  2448. }
  2449. else
  2450. {
  2451. AfpLinkDoubleAtHead(afpFileDfeFreeBlockHead[Index],
  2452. pDfb,
  2453. dfb_Next,
  2454. dfb_Prev);
  2455. }
  2456. }
  2457. }
  2458. AfpSwmrRelease(&afpDfeBlockLock);
  2459. #ifdef PROFILING
  2460. AfpGetPerfCounter(&TimeE);
  2461. TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
  2462. INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_DFEFreeTime,
  2463. TimeD,
  2464. &AfpStatisticsLock);
  2465. #endif
  2466. }
  2467. /*** afpDfeBlockAge
  2468. *
  2469. * Age out Dfe Blocks
  2470. *
  2471. * LOCKS: afpDfeBlockLock (SWMR, Exclusive)
  2472. */
  2473. AFPSTATUS FASTCALL
  2474. afpDfeBlockAge(
  2475. IN PPDFEBLOCK ppBlockHead
  2476. )
  2477. {
  2478. int index, MaxDfes;
  2479. PDFEBLOCK pDfb;
  2480. PAGED_CODE( );
  2481. AfpSwmrAcquireExclusive(&afpDfeBlockLock);
  2482. for (index = 0; index < MAX_BLOCK_TYPE; index++)
  2483. {
  2484. pDfb = ppBlockHead[index];
  2485. if (pDfb != NULL)
  2486. {
  2487. MaxDfes = pDfb->dfb_fDir ? afpDfeNumDirBlocks[index] : afpDfeNumFileBlocks[index];
  2488. }
  2489. while (pDfb != NULL)
  2490. {
  2491. PDFEBLOCK pFree;
  2492. ASSERT(VALID_DFB(pDfb));
  2493. pFree = pDfb;
  2494. pDfb = pDfb->dfb_Next;
  2495. ASSERT (pFree->dfb_NumFree == MaxDfes);
  2496. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
  2497. ("afpDfeBlockAge: Aging Block %lx, Size %d\n", pFree,
  2498. pFree->dfb_fDir ? afpDfeDirBlockSize[index] : afpDfeFileBlockSize[index]));
  2499. if (++(pFree->dfb_Age) >= MAX_BLOCK_AGE)
  2500. {
  2501. #ifdef PROFILING
  2502. INTERLOCKED_INCREMENT_LONG( &AfpServerProfile->perf_DFEAgeCount);
  2503. #endif
  2504. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
  2505. ("afpDfeBlockAge: Freeing Block %lx, Size %d\n", pFree,
  2506. pDfb->dfb_fDir ? afpDfeDirBlockSize[index] : afpDfeFileBlockSize[index]));
  2507. AfpUnlinkDouble(pFree, dfb_Next, dfb_Prev);
  2508. AfpFreeVirtualMemoryPage(pFree);
  2509. #if DBG
  2510. afpDfbAllocCount --;
  2511. #endif
  2512. }
  2513. }
  2514. }
  2515. AfpSwmrRelease(&afpDfeBlockLock);
  2516. return AFP_ERR_REQUEUE;
  2517. }
  2518. /*** AfpInitIdDb
  2519. *
  2520. * This routine initializes the memory image (and all related volume descriptor
  2521. * fields) of the ID index database for a new volume. The entire tree is
  2522. * scanned so all the file/dir cached info can be read in and our view of
  2523. * the volume tree will be complete. If an index database already exists
  2524. * on the disk for the volume root directory, that stream is read in. If this
  2525. * is a newly created volume, the Afp_IdIndex stream is created on the root of
  2526. * the volume. If this is a CDFS volume, only the memory image is initialized.
  2527. *
  2528. * The IdDb is not locked since the volume is still 'in transition' and not
  2529. * accessed by anybody.
  2530. */
  2531. NTSTATUS FASTCALL
  2532. AfpInitIdDb(
  2533. IN PVOLDESC pVolDesc,
  2534. OUT BOOLEAN *pfNewVolume,
  2535. OUT BOOLEAN *pfVerifyIndex
  2536. )
  2537. {
  2538. NTSTATUS Status;
  2539. ULONG CreateInfo;
  2540. FILESYSHANDLE fshIdDb;
  2541. IDDBHDR IdDbHdr;
  2542. BOOLEAN fLogEvent=FALSE;
  2543. PAGED_CODE( );
  2544. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  2545. ("AfpInitIdDb: Initializing Id Database...\n"));
  2546. *pfNewVolume = FALSE;
  2547. do
  2548. {
  2549. afpInitializeIdDb(pVolDesc);
  2550. // if this is not a CDFS volume, attempt to create the ID DB header
  2551. // stream. If it already exists, open it and read it in.
  2552. if (IS_VOLUME_NTFS(pVolDesc))
  2553. {
  2554. // Force the scavenger to write out the IdDb and header when the
  2555. // volume is successfully added
  2556. pVolDesc->vds_Flags |= VOLUME_IDDBHDR_DIRTY;
  2557. Status = AfpIoCreate(&pVolDesc->vds_hRootDir,
  2558. AFP_STREAM_IDDB,
  2559. &UNullString,
  2560. FILEIO_ACCESS_READWRITE,
  2561. FILEIO_DENY_WRITE,
  2562. FILEIO_OPEN_FILE_SEQ,
  2563. FILEIO_CREATE_INTERNAL,
  2564. FILE_ATTRIBUTE_NORMAL,
  2565. False,
  2566. NULL,
  2567. &fshIdDb,
  2568. &CreateInfo,
  2569. NULL,
  2570. NULL,
  2571. NULL);
  2572. if (!NT_SUCCESS(Status))
  2573. {
  2574. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  2575. ("AfpInitIdDb: AfpIoCreate failed with %lx\n", Status));
  2576. ASSERT(0);
  2577. fLogEvent = TRUE;
  2578. break;
  2579. }
  2580. if (CreateInfo == FILE_OPENED)
  2581. {
  2582. // read in the existing header. If we fail, just start from scratch
  2583. Status = afpReadIdDb(pVolDesc, &fshIdDb, pfVerifyIndex);
  2584. if (!NT_SUCCESS(Status) || (pVolDesc->vds_pDfeRoot == NULL))
  2585. CreateInfo = FILE_CREATED;
  2586. }
  2587. if (CreateInfo == FILE_CREATED)
  2588. {
  2589. // add the root and parent of root to the idindex
  2590. // and initialize a new header
  2591. Status = afpSeedIdDb(pVolDesc);
  2592. *pfNewVolume = TRUE;
  2593. }
  2594. else if (CreateInfo != FILE_OPENED)
  2595. {
  2596. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  2597. ("AfpInitIdDb: unexpected create action 0x%lx\n", CreateInfo));
  2598. ASSERTMSG("Unexpected Create Action\n", 0); // this should never happen
  2599. fLogEvent = TRUE;
  2600. Status = STATUS_UNSUCCESSFUL;
  2601. }
  2602. AfpIoClose(&fshIdDb);
  2603. //
  2604. // write back the IdDb header to the file, but with bad signature.
  2605. // If server shuts down, the correct signature will be
  2606. // written. If macfile is closed down using "net stop macfile"
  2607. // signature is corrupted with a different type
  2608. // If cool boot/bugcheck, a third type
  2609. // During volume startup, we will know from the signature,
  2610. // whether to rebuild completely, read iddb but verify or
  2611. // not rebuild at all
  2612. //
  2613. if (NT_SUCCESS(Status))
  2614. {
  2615. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  2616. ("AfpInitIdDb: ***** Corrupting IDDB header ***** \n"));
  2617. AfpVolDescToIdDbHdr(pVolDesc, &IdDbHdr);
  2618. IdDbHdr.idh_Signature = AFP_SERVER_SIGNATURE_INITIDDB;
  2619. AfpVolumeUpdateIdDbAndDesktop(pVolDesc,FALSE,FALSE,&IdDbHdr);
  2620. }
  2621. }
  2622. else
  2623. {
  2624. // its CDFS, just initialize the memory image of the IdDB
  2625. Status = afpSeedIdDb(pVolDesc);
  2626. *pfNewVolume = TRUE;
  2627. }
  2628. } while (False);
  2629. if (fLogEvent)
  2630. {
  2631. AFPLOG_ERROR(AFPSRVMSG_INIT_IDDB,
  2632. Status,
  2633. NULL,
  2634. 0,
  2635. &pVolDesc->vds_Name);
  2636. Status = STATUS_UNSUCCESSFUL;
  2637. }
  2638. return Status;
  2639. }
  2640. /*** afpSeedIdDb
  2641. *
  2642. * This routine adds the 'parent of root' and the root directory entries
  2643. * to a newly created ID index database (the memory image of the iddb).
  2644. *
  2645. **/
  2646. LOCAL NTSTATUS FASTCALL
  2647. afpSeedIdDb(
  2648. IN PVOLDESC pVolDesc
  2649. )
  2650. {
  2651. PDFENTRY pDfEntry;
  2652. AFPTIME CurrentTime;
  2653. AFPINFO afpinfo;
  2654. FILESYSHANDLE fshAfpInfo, fshComment, fshData;
  2655. DWORD i, crinfo, Attr;
  2656. FINDERINFO FinderInfo;
  2657. NTSTATUS Status = STATUS_SUCCESS;
  2658. PAGED_CODE( );
  2659. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  2660. ("afpSeedIdDb: Creating new Id Database...\n"));
  2661. do
  2662. {
  2663. pDfEntry = AfpFindDfEntryById(pVolDesc,
  2664. AFP_ID_PARENT_OF_ROOT,
  2665. DFE_DIR);
  2666. ASSERT (pDfEntry != NULL);
  2667. // add the root directory to the id index
  2668. if ((pDfEntry = AfpAddDfEntry(pVolDesc,
  2669. pDfEntry,
  2670. &pVolDesc->vds_Name,
  2671. True,
  2672. AFP_ID_ROOT)) == NULL )
  2673. {
  2674. Status = STATUS_NO_MEMORY;
  2675. break;
  2676. }
  2677. pVolDesc->vds_pDfeRoot = pDfEntry; // Initialize pointer to root.
  2678. // Attempt to open the comment stream. If it succeeds, set a flag in
  2679. // the DFE indicating that this thing does indeed have a comment.
  2680. if (NT_SUCCESS(AfpIoOpen(&pVolDesc->vds_hRootDir,
  2681. AFP_STREAM_COMM,
  2682. FILEIO_OPEN_FILE,
  2683. &UNullString,
  2684. FILEIO_ACCESS_NONE,
  2685. FILEIO_DENY_NONE,
  2686. False,
  2687. &fshComment)))
  2688. {
  2689. DFE_SET_COMMENT(pDfEntry);
  2690. AfpIoClose(&fshComment);
  2691. }
  2692. // Get the directory information for volume root dir. Do not get the
  2693. // mod-time. See below.
  2694. Status = AfpIoQueryTimesnAttr(&pVolDesc->vds_hRootDir,
  2695. &pDfEntry->dfe_CreateTime,
  2696. NULL,
  2697. &Attr);
  2698. // Setup up root directories Last ModTime such that it will
  2699. // get enumerated.
  2700. AfpConvertTimeFromMacFormat(BEGINNING_OF_TIME,
  2701. &pDfEntry->dfe_LastModTime);
  2702. ASSERT(NT_SUCCESS(Status));
  2703. pDfEntry->dfe_NtAttr = (USHORT)Attr & FILE_ATTRIBUTE_VALID_FLAGS;
  2704. if (IS_VOLUME_NTFS(pVolDesc))
  2705. {
  2706. if (NT_SUCCESS(Status = AfpCreateAfpInfo(&pVolDesc->vds_hRootDir,
  2707. &fshAfpInfo,
  2708. &crinfo)))
  2709. {
  2710. if ((crinfo == FILE_CREATED) ||
  2711. (!NT_SUCCESS(AfpReadAfpInfo(&fshAfpInfo, &afpinfo))))
  2712. {
  2713. Status = AfpSlapOnAfpInfoStream(NULL,
  2714. NULL,
  2715. &pVolDesc->vds_hRootDir,
  2716. &fshAfpInfo,
  2717. AFP_ID_ROOT,
  2718. True,
  2719. NULL,
  2720. &afpinfo);
  2721. }
  2722. else
  2723. {
  2724. // Just make sure the afp ID is ok, preserve the rest
  2725. if (afpinfo.afpi_Id != AFP_ID_ROOT)
  2726. {
  2727. afpinfo.afpi_Id = AFP_ID_ROOT;
  2728. AfpWriteAfpInfo(&fshAfpInfo, &afpinfo);
  2729. }
  2730. }
  2731. AfpIoClose(&fshAfpInfo);
  2732. pDfEntry->dfe_AfpAttr = afpinfo.afpi_Attributes;
  2733. pDfEntry->dfe_FinderInfo = afpinfo.afpi_FinderInfo;
  2734. if (pVolDesc->vds_Flags & AFP_VOLUME_HAS_CUSTOM_ICON)
  2735. {
  2736. // Don't bother writing back to disk since we do not
  2737. // try to keep this in sync in the permanent afpinfo
  2738. // stream with the actual existence of the icon<0d> file.
  2739. pDfEntry->dfe_FinderInfo.fd_Attr1 |= FINDER_FLAG_HAS_CUSTOM_ICON;
  2740. }
  2741. pDfEntry->dfe_BackupTime = afpinfo.afpi_BackupTime;
  2742. DFE_OWNER_ACCESS(pDfEntry) = afpinfo.afpi_AccessOwner;
  2743. DFE_GROUP_ACCESS(pDfEntry) = afpinfo.afpi_AccessGroup;
  2744. DFE_WORLD_ACCESS(pDfEntry) = afpinfo.afpi_AccessWorld;
  2745. }
  2746. }
  2747. else // CDFS
  2748. {
  2749. RtlZeroMemory(&pDfEntry->dfe_FinderInfo, sizeof(FINDERINFO));
  2750. if (IS_VOLUME_CD_HFS(pVolDesc))
  2751. {
  2752. Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  2753. AFP_STREAM_DATA,
  2754. FILEIO_OPEN_DIR,
  2755. &UNullString,
  2756. FILEIO_ACCESS_NONE,
  2757. FILEIO_DENY_NONE,
  2758. False,
  2759. &fshData);
  2760. if (!NT_SUCCESS(Status))
  2761. {
  2762. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  2763. ("afpSeedIdDb: AfpIoOpeno failed with %lx for CD_HFS\n", Status));
  2764. break;
  2765. }
  2766. AfpIoClose(&fshData);
  2767. }
  2768. pDfEntry->dfe_BackupTime = BEGINNING_OF_TIME;
  2769. DFE_OWNER_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
  2770. DFE_GROUP_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
  2771. DFE_WORLD_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
  2772. pDfEntry->dfe_AfpAttr = 0;
  2773. }
  2774. } while (False);
  2775. return Status;
  2776. }
  2777. /*** AfpFreeIdIndexTables
  2778. *
  2779. * Free the allocated memory for the volume id index tables. The volume is
  2780. * about to be deleted. Ensure that either the volume is readonly or it is
  2781. * clean i.e. the scavenger threads have written it back.
  2782. *
  2783. */
  2784. VOID FASTCALL
  2785. AfpFreeIdIndexTables(
  2786. IN PVOLDESC pVolDesc
  2787. )
  2788. {
  2789. DWORD i;
  2790. struct _DirFileEntry ** DfeDirBucketStart;
  2791. struct _DirFileEntry ** DfeFileBucketStart;
  2792. PAGED_CODE( );
  2793. ASSERT (IS_VOLUME_RO(pVolDesc) ||
  2794. (pVolDesc->vds_pOpenForkDesc == NULL));
  2795. // Traverse each of the hashed indices and free the entries.
  2796. // Need only traverse the overflow links. Ignore other links.
  2797. // JH - Do not bother if we are here during shutdown
  2798. if (AfpServerState != AFP_STATE_SHUTTINGDOWN)
  2799. {
  2800. PDFENTRY pDfEntry, pFree;
  2801. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
  2802. DfeFileBucketStart = pVolDesc->vds_pDfeFileBucketStart;
  2803. if (DfeFileBucketStart)
  2804. {
  2805. for (i = 0; i < pVolDesc->vds_FileHashTableSize; i++)
  2806. {
  2807. for (pDfEntry = DfeFileBucketStart[i];
  2808. pDfEntry != NULL;
  2809. NOTHING)
  2810. {
  2811. ASSERT(VALID_DFE(pDfEntry));
  2812. pFree = pDfEntry;
  2813. pDfEntry = pDfEntry->dfe_NextOverflow;
  2814. FREE_DFE(pFree);
  2815. }
  2816. DfeFileBucketStart[i] = NULL;
  2817. }
  2818. }
  2819. DfeDirBucketStart = pVolDesc->vds_pDfeDirBucketStart;
  2820. if (DfeDirBucketStart)
  2821. {
  2822. for (i = 0; i < pVolDesc->vds_DirHashTableSize; i++)
  2823. {
  2824. for (pDfEntry = DfeDirBucketStart[i];
  2825. pDfEntry != NULL;
  2826. NOTHING)
  2827. {
  2828. ASSERT(VALID_DFE(pDfEntry));
  2829. pFree = pDfEntry;
  2830. pDfEntry = pDfEntry->dfe_NextOverflow;
  2831. FREE_DFE(pFree);
  2832. }
  2833. DfeDirBucketStart[i] = NULL;
  2834. }
  2835. }
  2836. RtlZeroMemory(pVolDesc->vds_pDfeCache,
  2837. IDINDEX_CACHE_ENTRIES * sizeof(PDFENTRY));
  2838. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  2839. }
  2840. }
  2841. /*** afpRenameInvalidWin32Name
  2842. *
  2843. */
  2844. VOID
  2845. afpRenameInvalidWin32Name(
  2846. IN PFILESYSHANDLE phRootDir,
  2847. IN BOOLEAN IsDir,
  2848. IN PUNICODE_STRING pName
  2849. )
  2850. {
  2851. FILESYSHANDLE Fsh;
  2852. NTSTATUS rc;
  2853. WCHAR wc;
  2854. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2855. ("afpRenameInvalidWin32Name: renaming on the fly %Z\n", pName));
  2856. // Rename it now
  2857. if (NT_SUCCESS(AfpIoOpen(phRootDir,
  2858. AFP_STREAM_DATA,
  2859. IsDir ? FILEIO_OPEN_DIR : FILEIO_OPEN_FILE,
  2860. pName,
  2861. FILEIO_ACCESS_DELETE,
  2862. FILEIO_DENY_NONE,
  2863. False,
  2864. &Fsh)))
  2865. {
  2866. DWORD NtAttr;
  2867. // Before we attempt a rename, check if the RO bit is set. If it is
  2868. // reset it temporarily.
  2869. rc = AfpIoQueryTimesnAttr(&Fsh, NULL, NULL, &NtAttr);
  2870. ASSERT(NT_SUCCESS(rc));
  2871. if (NtAttr & FILE_ATTRIBUTE_READONLY)
  2872. {
  2873. rc = AfpIoSetTimesnAttr(&Fsh,
  2874. NULL,
  2875. NULL,
  2876. 0,
  2877. FILE_ATTRIBUTE_READONLY,
  2878. NULL,
  2879. NULL);
  2880. ASSERT(NT_SUCCESS(rc));
  2881. }
  2882. // Convert the name back to UNICODE so that munging happens !!!
  2883. wc = pName->Buffer[(pName->Length - 1)/sizeof(WCHAR)];
  2884. if (wc == UNICODE_SPACE)
  2885. pName->Buffer[(pName->Length - 1)/sizeof(WCHAR)] = AfpMungedUnicodeSpace;
  2886. if (wc == UNICODE_PERIOD)
  2887. pName->Buffer[(pName->Length - 1)/sizeof(WCHAR)] = AfpMungedUnicodePeriod;
  2888. rc = AfpIoMoveAndOrRename(&Fsh,
  2889. NULL,
  2890. pName,
  2891. NULL,
  2892. NULL,
  2893. NULL,
  2894. NULL,
  2895. NULL);
  2896. ASSERT(NT_SUCCESS(rc));
  2897. // Set the RO Attr back if it was set to begin with
  2898. if (NtAttr & FILE_ATTRIBUTE_READONLY)
  2899. {
  2900. rc = AfpIoSetTimesnAttr(&Fsh,
  2901. NULL,
  2902. NULL,
  2903. FILE_ATTRIBUTE_READONLY,
  2904. 0,
  2905. NULL,
  2906. NULL);
  2907. ASSERT(NT_SUCCESS(rc));
  2908. }
  2909. AfpIoClose(&Fsh);
  2910. }
  2911. }
  2912. LONG afpVirtualMemoryCount = 0;
  2913. LONG afpVirtualMemorySize = 0;
  2914. /*** AfpAllocVirtualMemory
  2915. *
  2916. * This is a wrapper over NtAllocateVirtualMemory.
  2917. */
  2918. PBYTE FASTCALL
  2919. AfpAllocateVirtualMemoryPage(
  2920. IN VOID
  2921. )
  2922. {
  2923. PBYTE pMem = NULL;
  2924. NTSTATUS Status;
  2925. PBLOCK64K pCurrBlock;
  2926. PBLOCK64K pTmpBlk;
  2927. SIZE_T Size64K;
  2928. DWORD i, dwMaxPages;
  2929. Size64K = BLOCK_64K_ALLOC;
  2930. dwMaxPages = (BLOCK_64K_ALLOC/PAGE_SIZE);
  2931. pCurrBlock = afp64kBlockHead;
  2932. //
  2933. // if we have never allocated a 64K block as yet, or we don't have one that
  2934. // has any free page(s) in it, allocate a new block!
  2935. //
  2936. if ((pCurrBlock == NULL) || (pCurrBlock->b64_PagesFree == 0))
  2937. {
  2938. pCurrBlock = (PBLOCK64K)AfpAllocNonPagedMemory(sizeof(BLOCK64K));
  2939. if (pCurrBlock == NULL)
  2940. {
  2941. return(NULL);
  2942. }
  2943. ExInterlockedIncrementLong(&afpVirtualMemoryCount, &AfpStatisticsLock);
  2944. ExInterlockedAddUlong(&afpVirtualMemorySize, (ULONG)Size64K, &(AfpStatisticsLock.SpinLock));
  2945. Status = NtAllocateVirtualMemory(NtCurrentProcess(),
  2946. &pMem,
  2947. 0L,
  2948. &Size64K,
  2949. MEM_COMMIT,
  2950. PAGE_READWRITE);
  2951. if (NT_SUCCESS(Status))
  2952. {
  2953. ASSERT(pMem != NULL);
  2954. #if DBG
  2955. afpDfe64kBlockCount++;
  2956. #endif
  2957. pCurrBlock->b64_Next = afp64kBlockHead;
  2958. pCurrBlock->b64_BaseAddress = pMem;
  2959. pCurrBlock->b64_PagesFree = dwMaxPages;
  2960. for (i=0; i<dwMaxPages; i++)
  2961. {
  2962. pCurrBlock->b64_PageInUse[i] = FALSE;
  2963. }
  2964. afp64kBlockHead = pCurrBlock;
  2965. }
  2966. else
  2967. {
  2968. AfpFreeMemory(pCurrBlock);
  2969. return(NULL);
  2970. }
  2971. }
  2972. //
  2973. // if we came this far, we are guaranteed that pCurrBlock is pointing to a
  2974. // block that has at least one page free
  2975. //
  2976. ASSERT ((pCurrBlock != NULL) &&
  2977. (pCurrBlock->b64_PagesFree > 0) &&
  2978. (pCurrBlock->b64_PagesFree <= dwMaxPages));
  2979. // find out which page is free
  2980. for (i=0; i<dwMaxPages; i++)
  2981. {
  2982. if (pCurrBlock->b64_PageInUse[i] == FALSE)
  2983. {
  2984. break;
  2985. }
  2986. }
  2987. ASSERT(i < dwMaxPages);
  2988. pCurrBlock->b64_PagesFree--;
  2989. pCurrBlock->b64_PageInUse[i] = TRUE;
  2990. pMem = ((PBYTE)pCurrBlock->b64_BaseAddress) + (i * PAGE_SIZE);
  2991. //
  2992. // if this 64kblock has no more free pages in it, move it to a spot after
  2993. // all the blocks that have some pages free in it. For that, we first
  2994. // find the guy who has no pages free in him and move this block after him
  2995. //
  2996. if (pCurrBlock->b64_PagesFree == 0)
  2997. {
  2998. pTmpBlk = pCurrBlock->b64_Next;
  2999. if (pTmpBlk != NULL)
  3000. {
  3001. while (1)
  3002. {
  3003. // found a guy who has no free page in it?
  3004. if (pTmpBlk->b64_PagesFree == 0)
  3005. {
  3006. break;
  3007. }
  3008. // is this the last guy on the list?
  3009. if (pTmpBlk->b64_Next == NULL)
  3010. {
  3011. break;
  3012. }
  3013. pTmpBlk = pTmpBlk->b64_Next;
  3014. }
  3015. }
  3016. // if we found a block
  3017. if (pTmpBlk)
  3018. {
  3019. ASSERT(afp64kBlockHead == pCurrBlock);
  3020. afp64kBlockHead = pCurrBlock->b64_Next;
  3021. pCurrBlock->b64_Next = pTmpBlk->b64_Next;
  3022. pTmpBlk->b64_Next = pCurrBlock;
  3023. }
  3024. }
  3025. return pMem;
  3026. }
  3027. VOID FASTCALL
  3028. AfpFreeVirtualMemoryPage(
  3029. IN PVOID pBuffer
  3030. )
  3031. {
  3032. NTSTATUS Status;
  3033. PBYTE BaseAddr;
  3034. PBLOCK64K pCurrBlock;
  3035. PBLOCK64K pPrevBlk;
  3036. SIZE_T Size64K;
  3037. DWORD i, dwMaxPages, dwPageNum, Offset;
  3038. dwMaxPages = (BLOCK_64K_ALLOC/PAGE_SIZE);
  3039. Size64K = BLOCK_64K_ALLOC;
  3040. pCurrBlock = afp64kBlockHead;
  3041. pPrevBlk = afp64kBlockHead;
  3042. BaseAddr = (PBYTE)((ULONG_PTR)pBuffer & ~(BLOCK_64K_ALLOC - 1));
  3043. Offset = (DWORD)(((PBYTE)pBuffer - BaseAddr));
  3044. dwPageNum = Offset/PAGE_SIZE;
  3045. ASSERT(Offset < BLOCK_64K_ALLOC);
  3046. while (pCurrBlock != NULL)
  3047. {
  3048. if (pCurrBlock->b64_BaseAddress == BaseAddr)
  3049. {
  3050. break;
  3051. }
  3052. pPrevBlk = pCurrBlock;
  3053. pCurrBlock = pCurrBlock->b64_Next;
  3054. }
  3055. ASSERT(pCurrBlock->b64_BaseAddress == BaseAddr);
  3056. pCurrBlock->b64_PageInUse[dwPageNum] = FALSE;
  3057. pCurrBlock->b64_PagesFree++;
  3058. //
  3059. // if all the pages in this block are unused, then it's time to free this block
  3060. // after removing from the list
  3061. //
  3062. if (pCurrBlock->b64_PagesFree == dwMaxPages)
  3063. {
  3064. // is our guy the first (and potentially the only one) on the list?
  3065. if (afp64kBlockHead == pCurrBlock)
  3066. {
  3067. afp64kBlockHead = pCurrBlock->b64_Next;
  3068. }
  3069. // nope, there are others and we're somewhere in the middle (or end)
  3070. else
  3071. {
  3072. pPrevBlk->b64_Next = pCurrBlock->b64_Next;
  3073. }
  3074. AfpFreeMemory(pCurrBlock);
  3075. ExInterlockedDecrementLong(&afpVirtualMemoryCount, &AfpStatisticsLock);
  3076. ExInterlockedAddUlong(&afpVirtualMemorySize,
  3077. -1*((ULONG)Size64K),
  3078. &(AfpStatisticsLock.SpinLock));
  3079. Status = NtFreeVirtualMemory(NtCurrentProcess(),
  3080. (PVOID *)&BaseAddr,
  3081. &Size64K,
  3082. MEM_RELEASE);
  3083. #if DBG
  3084. ASSERT(afpDfe64kBlockCount > 0);
  3085. afpDfe64kBlockCount--;
  3086. #endif
  3087. }
  3088. //
  3089. // if a page became available in this block for the first time, move this
  3090. // block to the front of the list (unless it already is there)
  3091. //
  3092. else if (pCurrBlock->b64_PagesFree == 1)
  3093. {
  3094. if (afp64kBlockHead != pCurrBlock)
  3095. {
  3096. pPrevBlk->b64_Next = pCurrBlock->b64_Next;
  3097. pCurrBlock->b64_Next = afp64kBlockHead;
  3098. afp64kBlockHead = pCurrBlock;
  3099. }
  3100. }
  3101. }
  3102. #ifdef AGE_DFES
  3103. /*** AfpAgeDfEntries
  3104. *
  3105. * Age out DfEntries out of the Id database. The Files in directories which have not been
  3106. * accessed for VOLUME_IDDB_AGE_DELAY are aged out. The directories are marked so that
  3107. * they will be enumerated when they are hit next.
  3108. *
  3109. * LOCKS: vds_idDbAccessLock(SWMR, Exclusive)
  3110. */
  3111. VOID FASTCALL
  3112. AfpAgeDfEntries(
  3113. IN PVOLDESC pVolDesc
  3114. )
  3115. {
  3116. PDFENTRY pDfEntry, *Stack = NULL;
  3117. LONG i, StackPtr = 0;
  3118. AFPTIME Now;
  3119. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  3120. AfpGetCurrentTimeInMacFormat(&Now);
  3121. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
  3122. // Potentially all of the files can be aged out. Allocate 'stack' space
  3123. // for all of the directory DFEs
  3124. if ((Stack = (PDFENTRY *)
  3125. AfpAllocNonPagedMemory(pVolDesc->vds_NumDirDfEntries*sizeof(PDFENTRY))) != NULL)
  3126. {
  3127. // 'Prime' the stack of Dfe's
  3128. Stack[StackPtr++] = pVolDesc->vds_pDfeRoot;
  3129. while (StackPtr > 0)
  3130. {
  3131. PDFENTRY pDir;
  3132. pDfEntry = Stack[--StackPtr];
  3133. ASSERT(DFE_IS_DIRECTORY(pDfEntry));
  3134. if ((pDfEntry->dfe_AfpId >= AFP_FIRST_DIRID) &&
  3135. (pDfEntry->dfe_pDirEntry->de_ChildForkOpenCount == 0) &&
  3136. ((Now - pDfEntry->dfe_pDirEntry->de_LastAccessTime) > VOLUME_IDDB_AGE_DELAY))
  3137. {
  3138. PDFENTRY pFile, pNext;
  3139. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3140. ("AfpAgeDfEntries: Aging out directory %Z\n", &pDfEntry->dfe_UnicodeName));
  3141. // This directory's files need to be nuked
  3142. pDfEntry->dfe_FileOffspring = 0;
  3143. pDfEntry->dfe_Flags &= ~DFE_FLAGS_FILES_CACHED;
  3144. for (i = 0; i < MAX_CHILD_HASH_BUCKETS; i++)
  3145. {
  3146. for (pFile = pDfEntry->dfe_pDirEntry->de_ChildFile[i];
  3147. pFile != NULL;
  3148. pFile = pNext)
  3149. {
  3150. pNext = pFile->dfe_NextSibling;
  3151. // Unlink it from the hash buckets
  3152. AfpUnlinkDouble(pFile,
  3153. dfe_NextOverflow,
  3154. dfe_PrevOverflow);
  3155. // Nuke it from the cache if it is there
  3156. if (pVolDesc->vds_pDfeCache[HASH_CACHE_ID(pFile->dfe_AfpId)] == pFile)
  3157. {
  3158. pVolDesc->vds_pDfeCache[HASH_CACHE_ID(pFile->dfe_AfpId)] = NULL;
  3159. }
  3160. // Finally free it
  3161. FREE_DFE(pFile);
  3162. }
  3163. pDfEntry->dfe_pDirEntry->de_ChildFile[i] = NULL;
  3164. }
  3165. }
  3166. #if 0
  3167. // NOTE: Should we leave the tree under 'Network Trash Folder' alone ?
  3168. if (pDfEntry->dfe_AfpId == AFP_ID_NETWORK_TRASH)
  3169. continue;
  3170. #endif
  3171. // Pick up all the child directories of this directory and 'push' them on stack
  3172. for (pDir = pDfEntry->dfe_pDirEntry->de_ChildDir;
  3173. pDir != NULL;
  3174. pDir = pDir->dfe_NextSibling)
  3175. {
  3176. Stack[StackPtr++] = pDir;
  3177. }
  3178. }
  3179. AfpFreeMemory(Stack);
  3180. }
  3181. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  3182. }
  3183. #endif
  3184. #if DBG
  3185. NTSTATUS FASTCALL
  3186. afpDumpDfeTree(
  3187. IN PVOID Context
  3188. )
  3189. {
  3190. PVOLDESC pVolDesc;
  3191. PDFENTRY pDfEntry, pChild;
  3192. LONG i, StackPtr;
  3193. if (afpDumpDfeTreeFlag)
  3194. {
  3195. afpDumpDfeTreeFlag = 0;
  3196. for (pVolDesc = AfpVolumeList; pVolDesc != NULL; pVolDesc = pVolDesc->vds_Next)
  3197. {
  3198. StackPtr = 0;
  3199. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3200. ("Volume : %Z\n", &pVolDesc->vds_Name));
  3201. afpDfeStack[StackPtr++] = pVolDesc->vds_pDfeRoot;
  3202. while (StackPtr > 0)
  3203. {
  3204. pDfEntry = afpDfeStack[--StackPtr];
  3205. afpDisplayDfe(pDfEntry);
  3206. for (i = 0; i < MAX_CHILD_HASH_BUCKETS; i++)
  3207. {
  3208. for (pChild = pDfEntry->dfe_pDirEntry->de_ChildFile[i];
  3209. pChild != NULL;
  3210. pChild = pChild->dfe_NextSibling)
  3211. {
  3212. afpDisplayDfe(pChild);
  3213. }
  3214. }
  3215. for (pChild = pDfEntry->dfe_pDirEntry->de_ChildDir;
  3216. pChild != NULL;
  3217. pChild = pChild->dfe_NextSibling)
  3218. {
  3219. afpDfeStack[StackPtr++] = pChild;
  3220. }
  3221. }
  3222. }
  3223. }
  3224. return AFP_ERR_REQUEUE;
  3225. }
  3226. VOID FASTCALL
  3227. afpDisplayDfe(
  3228. IN PDFENTRY pDfEntry
  3229. )
  3230. {
  3231. USHORT i;
  3232. // Figure out the indenting. One space for every depth unit of parent
  3233. // If this is a directory, a '+' and then the dir name
  3234. // If this is a file, then just the file name
  3235. for (i = 0; i < (pDfEntry->dfe_Parent->dfe_DirDepth + 1); i++)
  3236. {
  3237. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3238. ("%c ", 0xB3));
  3239. }
  3240. if (pDfEntry->dfe_NextSibling == NULL)
  3241. {
  3242. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3243. ("%c%c%c%c", 0xC0, 0xC4, 0xC4, 0xC4));
  3244. }
  3245. else
  3246. {
  3247. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3248. ("%c%c%c%c", 0xC3, 0xC4, 0xC4, 0xC4));
  3249. }
  3250. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3251. ("%Z ", &pDfEntry->dfe_UnicodeName));
  3252. if (pDfEntry->dfe_Flags & DFE_FLAGS_DIR)
  3253. {
  3254. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3255. ("(%c, %lx, Id = %lx)\n", 0x9F, pDfEntry, pDfEntry->dfe_AfpId));
  3256. }
  3257. else
  3258. {
  3259. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3260. ("(%c, %lx, Id = %lx)\n", 0x46, pDfEntry, pDfEntry->dfe_AfpId));
  3261. }
  3262. }
  3263. #endif