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

4808 lines
136 KiB

  1. /*
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. chgntfy.c
  5. Abstract:
  6. This module contains the code for processing change notifies.
  7. Author:
  8. Jameel Hyder (microsoft!jameelh)
  9. Revision History:
  10. 15 Jun 1995 JameelH Seperated the change notify code from idindex.c
  11. Notes: Tab stop: 4
  12. --*/
  13. #define IDINDEX_LOCALS
  14. #define FILENUM FILE_CHGNTFY
  15. #include <afp.h>
  16. #include <scavengr.h>
  17. #include <fdparm.h>
  18. #include <pathmap.h>
  19. #include <afpinfo.h>
  20. #include <access.h> // for AfpWorldId
  21. #ifdef ALLOC_PRAGMA
  22. #pragma alloc_text(PAGE, afpVerifyDFE)
  23. #pragma alloc_text(PAGE, afpAddDfEntryAndCacheInfo)
  24. #pragma alloc_text(PAGE, afpReadIdDb)
  25. #pragma alloc_text(PAGE, AfpProcessChangeNotify)
  26. #pragma alloc_text(PAGE, afpProcessPrivateNotify)
  27. #pragma alloc_text(PAGE, AfpQueuePrivateChangeNotify)
  28. #pragma alloc_text(PAGE, AfpCacheDirectoryTree)
  29. #pragma alloc_text(PAGE, AfpQueueOurChange)
  30. #endif
  31. /*** afpVerifyDFE
  32. *
  33. * Check if our view of this item in our data-base matches whats on disk. If not
  34. * update our view with whats on disk.
  35. */
  36. VOID
  37. afpVerifyDFE(
  38. IN struct _VolDesc * pVolDesc,
  39. IN PDFENTRY pDfeParent,
  40. IN PUNICODE_STRING pUName, // munged unicode name
  41. IN PFILESYSHANDLE pfshParentDir, // open handle to parent directory
  42. IN PFILE_BOTH_DIR_INFORMATION pFBDInfo, // from enumerate
  43. IN PUNICODE_STRING pNotifyPath, // to filter out our own AFP_AfpInfo change notifies
  44. IN PDFENTRY * ppDfEntry
  45. )
  46. {
  47. PDFENTRY pDfEntry = *ppDfEntry;
  48. if (pFBDInfo->LastWriteTime.QuadPart > pDfEntry->dfe_LastModTime.QuadPart)
  49. {
  50. FILESYSHANDLE fshAfpInfo, fshData;
  51. AFPINFO AfpInfo;
  52. DWORD crinfo, openoptions = 0;
  53. BOOLEAN SeenComment, WriteBackROAttr = False;
  54. PSTREAM_INFO pStreams = NULL, pCurStream;
  55. NTSTATUS Status = STATUS_SUCCESS;
  56. BOOLEAN IsDir;
  57. // Our view is stale, update it
  58. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  59. ("afpVerifyDFE: Updating stale database with fresh info\n\t%Z\n", pNotifyPath));
  60. IsDir = (pFBDInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? True : False;
  61. ASSERT (IS_VOLUME_NTFS(pVolDesc));
  62. ASSERT (!(DFE_IS_DIRECTORY(pDfEntry) ^ IsDir));
  63. // Update DFE from FBDInfo first
  64. pDfEntry->dfe_CreateTime = AfpConvertTimeToMacFormat(&pFBDInfo->CreationTime);
  65. pDfEntry->dfe_LastModTime = pFBDInfo->LastWriteTime;
  66. pDfEntry->dfe_NtAttr = (USHORT)(pFBDInfo->FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS);
  67. if (!IsDir)
  68. {
  69. pDfEntry->dfe_DataLen = pFBDInfo->EndOfFile.LowPart;
  70. }
  71. // Open/Create the AfpInfo stream
  72. fshAfpInfo.fsh_FileHandle = NULL;
  73. fshData.fsh_FileHandle = NULL;
  74. do
  75. {
  76. // open or create the AfpInfo stream
  77. if (!NT_SUCCESS(AfpCreateAfpInfoWithNodeName(pfshParentDir,
  78. pUName,
  79. pNotifyPath,
  80. pVolDesc,
  81. &fshAfpInfo,
  82. &crinfo)))
  83. {
  84. if (!(pFBDInfo->FileAttributes & FILE_ATTRIBUTE_READONLY))
  85. {
  86. // What other reason is there that we could not open
  87. // this stream except that this file/dir is readonly?
  88. Status = STATUS_UNSUCCESSFUL;
  89. break;
  90. }
  91. openoptions = IsDir ? FILEIO_OPEN_DIR : FILEIO_OPEN_FILE;
  92. Status = STATUS_UNSUCCESSFUL; // Assume failure
  93. if (NT_SUCCESS(AfpIoOpen(pfshParentDir,
  94. AFP_STREAM_DATA,
  95. openoptions,
  96. pUName,
  97. FILEIO_ACCESS_NONE,
  98. FILEIO_DENY_NONE,
  99. False,
  100. &fshData)))
  101. {
  102. if (NT_SUCCESS(AfpExamineAndClearROAttr(&fshData,
  103. &WriteBackROAttr,
  104. pVolDesc,
  105. pNotifyPath)))
  106. {
  107. if (NT_SUCCESS(AfpCreateAfpInfo(&fshData,
  108. &fshAfpInfo,
  109. &crinfo)))
  110. {
  111. Status = STATUS_SUCCESS;
  112. }
  113. }
  114. }
  115. if (!NT_SUCCESS(Status))
  116. {
  117. // Skip this entry if you cannot get to the AfpInfo, cannot
  118. // clear the RO attribute or whatever.
  119. break;
  120. }
  121. }
  122. // We successfully opened or created the AfpInfo stream. If
  123. // it existed, then validate the ID, otherwise create all new
  124. // Afpinfo for this file/dir.
  125. if ((crinfo == FILE_OPENED) &&
  126. (NT_SUCCESS(AfpReadAfpInfo(&fshAfpInfo, &AfpInfo))))
  127. {
  128. BOOLEAN fSuccess;
  129. if ((AfpInfo.afpi_Id != pDfEntry->dfe_AfpId) &&
  130. (pDfEntry->dfe_AfpId != AFP_ID_ROOT))
  131. {
  132. PDFENTRY pDFE;
  133. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  134. ("afpVerifyDFE: IdDb Id does not match the AfpInfo Id!!!\n"));
  135. // Unlink it from the hash-bucket since we have the wrong id.
  136. AfpUnlinkDouble(pDfEntry, dfe_NextOverflow, dfe_PrevOverflow);
  137. // If some other entity has this id, then assign a new one. Else
  138. // use the one from the AfpInfo stream.
  139. pDFE = AfpFindDfEntryById(pVolDesc, AfpInfo.afpi_Id, DFE_ANY);
  140. if (pDFE != NULL)
  141. {
  142. pDfEntry->dfe_AfpId = afpGetNextId(pVolDesc);
  143. }
  144. else
  145. {
  146. pDfEntry->dfe_AfpId = AfpInfo.afpi_Id;
  147. }
  148. // Re-insert it with the new id
  149. afpInsertDFEInHashBucket(pVolDesc, pDfEntry, IsDir, &fSuccess);
  150. }
  151. // NOTE: should we set the finder invisible bit if the
  152. // hidden attribute is set so system 6 will obey the
  153. // hiddenness in finder?
  154. pDfEntry->dfe_FinderInfo = AfpInfo.afpi_FinderInfo;
  155. pDfEntry->dfe_BackupTime = AfpInfo.afpi_BackupTime;
  156. pDfEntry->dfe_AfpAttr = AfpInfo.afpi_Attributes;
  157. }
  158. else
  159. {
  160. // AfpInfo stream was newly created, or we could not read
  161. // the existing one because it was corrupt. Create new
  162. // info for this file/dir. Trust the version from the IdDb
  163. AfpInitAfpInfo(&AfpInfo, pDfEntry->dfe_AfpId, IsDir, pDfEntry->dfe_BackupTime);
  164. AfpInfo.afpi_FinderInfo = pDfEntry->dfe_FinderInfo;
  165. AfpInfo.afpi_Attributes = pDfEntry->dfe_AfpAttr;
  166. if (IsDir)
  167. {
  168. // Keep track of see files vs. see folders
  169. AfpInfo.afpi_AccessOwner = DFE_OWNER_ACCESS(pDfEntry);
  170. AfpInfo.afpi_AccessGroup = DFE_GROUP_ACCESS(pDfEntry);
  171. AfpInfo.afpi_AccessWorld = DFE_WORLD_ACCESS(pDfEntry);
  172. }
  173. else
  174. {
  175. AfpProDosInfoFromFinderInfo(&AfpInfo.afpi_FinderInfo,
  176. &AfpInfo.afpi_ProDosInfo);
  177. pDfEntry->dfe_RescLen = 0; // Assume no resource fork
  178. // if this is a Mac application, make sure there is an APPL
  179. // mapping for it.
  180. if (AfpInfo.afpi_FinderInfo.fd_TypeD == *(PDWORD)"APPL")
  181. {
  182. AfpAddAppl(pVolDesc,
  183. pDfEntry->dfe_FinderInfo.fd_CreatorD,
  184. 0,
  185. pDfEntry->dfe_AfpId,
  186. True,
  187. pDfEntry->dfe_Parent->dfe_AfpId);
  188. }
  189. }
  190. Status = AfpWriteAfpInfo(&fshAfpInfo, &AfpInfo);
  191. if (!NT_SUCCESS(Status))
  192. {
  193. // We failed to write the AfpInfo stream;
  194. Status = STATUS_UNSUCCESSFUL;
  195. break;
  196. }
  197. }
  198. // Check for comment and resource stream
  199. pStreams = AfpIoQueryStreams(&fshAfpInfo);
  200. if (pStreams == NULL)
  201. {
  202. Status = STATUS_NO_MEMORY;
  203. break;
  204. }
  205. for (pCurStream = pStreams, SeenComment = False;
  206. pCurStream->si_StreamName.Buffer != NULL;
  207. pCurStream++)
  208. {
  209. if (IS_COMMENT_STREAM(&pCurStream->si_StreamName))
  210. {
  211. DFE_SET_COMMENT(pDfEntry);
  212. SeenComment = True;
  213. if (IsDir)
  214. break; // Scan no further for directories
  215. }
  216. else if (!IsDir && IS_RESOURCE_STREAM(&pCurStream->si_StreamName))
  217. {
  218. pDfEntry->dfe_RescLen = pCurStream->si_StreamSize.LowPart;
  219. if (SeenComment)
  220. break; // We have all we need to
  221. }
  222. }
  223. } while (False);
  224. if (fshData.fsh_FileHandle != NULL)
  225. {
  226. AfpPutBackROAttr(&fshData, WriteBackROAttr);
  227. AfpIoClose(&fshData);
  228. }
  229. if (fshAfpInfo.fsh_FileHandle != NULL)
  230. AfpIoClose(&fshAfpInfo);
  231. if (pStreams != NULL)
  232. AfpFreeMemory(pStreams);
  233. if (!NT_SUCCESS(Status))
  234. {
  235. *ppDfEntry = NULL;
  236. }
  237. else
  238. {
  239. // If this is the root directory, make sure we do not blow away the
  240. // AFP_VOLUME_HAS_CUSTOM_ICON bit for NTFS Volume.
  241. if (DFE_IS_ROOT(pDfEntry) &&
  242. (pVolDesc->vds_Flags & AFP_VOLUME_HAS_CUSTOM_ICON))
  243. {
  244. // Don't bother writing back to disk since we do not
  245. // try to keep this in sync in the permanent afpinfo
  246. // stream with the actual existence of the icon<0d> file.
  247. pDfEntry->dfe_FinderInfo.fd_Attr1 |= FINDER_FLAG_HAS_CUSTOM_ICON;
  248. }
  249. }
  250. }
  251. }
  252. /*** afpAddDfEntryAndCacheInfo
  253. *
  254. * During the initial sync with disk on volume startup, add each entry
  255. * we see during enumerate to the id index database.
  256. */
  257. VOID
  258. afpAddDfEntryAndCacheInfo(
  259. IN PVOLDESC pVolDesc,
  260. IN PDFENTRY pParentDfe,
  261. IN PUNICODE_STRING pUName, // munged unicode name
  262. IN PFILESYSHANDLE pfshEnumDir, // open handle to parent directory
  263. IN PFILE_BOTH_DIR_INFORMATION pFBDInfo, // from enumerate
  264. IN PUNICODE_STRING pNotifyPath, // For Afpinfo Stream
  265. IN PDFENTRY * ppDfEntry,
  266. IN BOOLEAN CheckDuplicate
  267. )
  268. {
  269. BOOLEAN IsDir, SeenComment, WriteBackROAttr = False;
  270. NTSTATUS Status = STATUS_SUCCESS;
  271. FILESYSHANDLE fshAfpInfo, fshData;
  272. DWORD crinfo, openoptions = 0;
  273. PDFENTRY pDfEntry;
  274. AFPINFO AfpInfo;
  275. FINDERINFO FinderInfo;
  276. PSTREAM_INFO pStreams = NULL, pCurStream;
  277. UNICODE_STRING EmptyString;
  278. DWORD NTAttr = 0;
  279. TIME ModTime;
  280. DWORD ModMacTime;
  281. PAGED_CODE();
  282. fshAfpInfo.fsh_FileHandle = NULL;
  283. fshData.fsh_FileHandle = NULL;
  284. IsDir = (pFBDInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? True : False;
  285. do
  286. {
  287. if (IS_VOLUME_NTFS(pVolDesc))
  288. {
  289. if (IsDir || CheckDuplicate)
  290. {
  291. // Make sure we don't already have this item in our database.
  292. // Multiple notifies for the same item could possibly occur if
  293. // the PC is renaming or moving items around on the disk, or
  294. // copying trees while we are trying to cache it, since we are
  295. // also queueing up private notifies for directories.
  296. afpFindDFEByUnicodeNameInSiblingList_CS(pVolDesc,
  297. pParentDfe,
  298. pUName,
  299. &pDfEntry,
  300. IsDir ? DFE_DIR : DFE_FILE);
  301. if (pDfEntry != NULL)
  302. {
  303. Status = AFP_ERR_OBJECT_EXISTS;
  304. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_WARN,
  305. ("afpAddDfEntryAndCacheInfo: Attempt to add a duplicate entry: %Z\n", pUName));
  306. break;
  307. }
  308. }
  309. openoptions = IsDir ? FILEIO_OPEN_DIR : FILEIO_OPEN_FILE;
  310. if (NT_SUCCESS(AfpIoOpen(pfshEnumDir,
  311. AFP_STREAM_DATA,
  312. openoptions,
  313. pUName,
  314. FILEIO_ACCESS_NONE,
  315. FILEIO_DENY_NONE,
  316. False,
  317. &fshData)))
  318. {
  319. // save the LastModify time on the data stream of the file: we need to restore it
  320. AfpIoQueryTimesnAttr(&fshData,
  321. NULL,
  322. &ModTime,
  323. &NTAttr);
  324. ModMacTime = AfpConvertTimeToMacFormat(&ModTime);
  325. }
  326. else
  327. {
  328. // if we can't open the data file, just skip this entry!
  329. Status = STATUS_UNSUCCESSFUL;
  330. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  331. ("Couldn't open data stream for %Z\n", pUName));
  332. break;
  333. }
  334. AfpSetEmptyUnicodeString(&EmptyString, 0, NULL);
  335. // open or create the AfpInfo stream
  336. if (!NT_SUCCESS(AfpCreateAfpInfoWithNodeName(&fshData,
  337. &EmptyString,
  338. pNotifyPath,
  339. pVolDesc,
  340. &fshAfpInfo,
  341. &crinfo)))
  342. {
  343. if (!(pFBDInfo->FileAttributes & FILE_ATTRIBUTE_READONLY))
  344. {
  345. // What other reason is there that we could not open
  346. // this stream except that this file/dir is readonly?
  347. Status = STATUS_UNSUCCESSFUL;
  348. break;
  349. }
  350. Status = STATUS_UNSUCCESSFUL; // Assume failure
  351. if (NT_SUCCESS(AfpExamineAndClearROAttr(&fshData,
  352. &WriteBackROAttr,
  353. pVolDesc,
  354. pNotifyPath)))
  355. {
  356. if (NT_SUCCESS(AfpCreateAfpInfo(&fshData,
  357. &fshAfpInfo,
  358. &crinfo)))
  359. {
  360. Status = STATUS_SUCCESS;
  361. }
  362. }
  363. if (!NT_SUCCESS(Status))
  364. {
  365. // Skip this entry if you cannot get to the AfpInfo, cannot
  366. // clear the RO attribute or whatever.
  367. break;
  368. }
  369. }
  370. // We successfully opened or created the AfpInfo stream. If
  371. // it existed, then validate the ID, otherwise create all new
  372. // Afpinfo for this file/dir.
  373. if ((crinfo == FILE_OPENED) &&
  374. (NT_SUCCESS(AfpReadAfpInfo(&fshAfpInfo, &AfpInfo))))
  375. {
  376. // the file/dir had an AfpInfo stream on it
  377. afpCheckDfEntry(pVolDesc,
  378. AfpInfo.afpi_Id,
  379. pUName,
  380. IsDir,
  381. pParentDfe,
  382. &pDfEntry);
  383. if (pDfEntry == NULL)
  384. {
  385. Status = STATUS_UNSUCCESSFUL;
  386. break;
  387. }
  388. if (pDfEntry->dfe_AfpId != AfpInfo.afpi_Id)
  389. {
  390. // Write out the AFP_AfpInfo with the new AfpId
  391. // and uninitialized icon coordinates
  392. AfpInfo.afpi_Id = pDfEntry->dfe_AfpId;
  393. AfpInfo.afpi_FinderInfo.fd_Location[0] =
  394. AfpInfo.afpi_FinderInfo.fd_Location[1] =
  395. AfpInfo.afpi_FinderInfo.fd_Location[2] =
  396. AfpInfo.afpi_FinderInfo.fd_Location[3] = 0xFF;
  397. AfpInfo.afpi_FinderInfo.fd_Attr1 &= ~FINDER_FLAG_SET;
  398. if (!NT_SUCCESS(AfpWriteAfpInfo(&fshAfpInfo, &AfpInfo)))
  399. {
  400. // We failed to write the AfpInfo stream;
  401. // delete the thing from the database
  402. AfpDeleteDfEntry(pVolDesc, pDfEntry);
  403. Status = STATUS_UNSUCCESSFUL;
  404. break;
  405. }
  406. }
  407. // NOTE: should we set the finder invisible bit if the
  408. // hidden attribute is set so system 6 will obey the
  409. // hiddenness in finder?
  410. pDfEntry->dfe_FinderInfo = AfpInfo.afpi_FinderInfo;
  411. pDfEntry->dfe_BackupTime = AfpInfo.afpi_BackupTime;
  412. pDfEntry->dfe_AfpAttr = AfpInfo.afpi_Attributes;
  413. }
  414. else
  415. {
  416. // AfpInfo stream was newly created, or we could not read
  417. // the existing one because it was corrupt. Create new
  418. // info for this file/dir
  419. pDfEntry = AfpAddDfEntry(pVolDesc,
  420. pParentDfe,
  421. pUName,
  422. IsDir,
  423. 0);
  424. if (pDfEntry == NULL)
  425. {
  426. Status = STATUS_UNSUCCESSFUL;
  427. break;
  428. }
  429. if (NT_SUCCESS(AfpSlapOnAfpInfoStream(pVolDesc,
  430. pNotifyPath,
  431. NULL,
  432. &fshAfpInfo,
  433. pDfEntry->dfe_AfpId,
  434. IsDir,
  435. pUName,
  436. &AfpInfo)))
  437. {
  438. // NOTE: should we set the finder invisible bit if the
  439. // hidden attribute is set so system 6 will obey the
  440. // hiddenness in finder?
  441. pDfEntry->dfe_FinderInfo = AfpInfo.afpi_FinderInfo;
  442. pDfEntry->dfe_BackupTime = AfpInfo.afpi_BackupTime;
  443. pDfEntry->dfe_AfpAttr = AfpInfo.afpi_Attributes;
  444. }
  445. else
  446. {
  447. // We failed to write the AfpInfo stream;
  448. // delete the thing from the database
  449. AfpDeleteDfEntry(pVolDesc, pDfEntry);
  450. Status = STATUS_UNSUCCESSFUL;
  451. break;
  452. }
  453. }
  454. ASSERT(pDfEntry != NULL);
  455. if (IsDir)
  456. {
  457. // Keep track of see files vs. see folders
  458. DFE_OWNER_ACCESS(pDfEntry) = AfpInfo.afpi_AccessOwner;
  459. DFE_GROUP_ACCESS(pDfEntry) = AfpInfo.afpi_AccessGroup;
  460. DFE_WORLD_ACCESS(pDfEntry) = AfpInfo.afpi_AccessWorld;
  461. }
  462. else
  463. {
  464. // it's a file
  465. pDfEntry->dfe_RescLen = 0; // Assume no resource fork
  466. // if this is a Mac application, make sure there is an APPL
  467. // mapping for it.
  468. if (AfpInfo.afpi_FinderInfo.fd_TypeD == *(PDWORD)"APPL")
  469. {
  470. AfpAddAppl(pVolDesc,
  471. AfpInfo.afpi_FinderInfo.fd_CreatorD,
  472. 0,
  473. pDfEntry->dfe_AfpId,
  474. True,
  475. pDfEntry->dfe_Parent->dfe_AfpId);
  476. }
  477. }
  478. // Check for comment and resource stream
  479. pStreams = AfpIoQueryStreams(&fshAfpInfo);
  480. if (pStreams == NULL)
  481. {
  482. Status = STATUS_NO_MEMORY;
  483. break;
  484. }
  485. for (pCurStream = pStreams, SeenComment = False;
  486. pCurStream->si_StreamName.Buffer != NULL;
  487. pCurStream++)
  488. {
  489. if (IS_COMMENT_STREAM(&pCurStream->si_StreamName))
  490. {
  491. DFE_SET_COMMENT(pDfEntry);
  492. SeenComment = True;
  493. if (IsDir)
  494. break; // Scan no further for directories
  495. }
  496. else if (!IsDir && IS_RESOURCE_STREAM(&pCurStream->si_StreamName))
  497. {
  498. pDfEntry->dfe_RescLen = pCurStream->si_StreamSize.LowPart;
  499. if (SeenComment)
  500. break; // We have all we need to
  501. }
  502. }
  503. AfpFreeMemory(pStreams);
  504. }
  505. else // CDFS
  506. {
  507. pDfEntry = AfpAddDfEntry(pVolDesc,
  508. pParentDfe,
  509. pUName,
  510. IsDir,
  511. 0);
  512. if (pDfEntry == NULL)
  513. {
  514. Status = STATUS_UNSUCCESSFUL;
  515. break;
  516. }
  517. RtlZeroMemory(&FinderInfo, sizeof(FINDERINFO));
  518. RtlZeroMemory(&pDfEntry->dfe_FinderInfo, sizeof(FINDERINFO));
  519. pDfEntry->dfe_BackupTime = BEGINNING_OF_TIME;
  520. pDfEntry->dfe_AfpAttr = 0;
  521. if (IsDir)
  522. {
  523. DFE_OWNER_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
  524. DFE_GROUP_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
  525. DFE_WORLD_ACCESS(pDfEntry) = (DIR_ACCESS_SEARCH | DIR_ACCESS_READ);
  526. }
  527. if (IS_VOLUME_CD_HFS(pVolDesc))
  528. {
  529. Status = AfpIoOpen(pfshEnumDir,
  530. AFP_STREAM_DATA,
  531. openoptions,
  532. pUName,
  533. FILEIO_ACCESS_NONE,
  534. FILEIO_DENY_NONE,
  535. False,
  536. &fshData);
  537. if (!NT_SUCCESS(Status))
  538. {
  539. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  540. ("afpAddDfEntryAndCacheInfo: AfpIoOpen on %Z failed (%lx) for CD_HFS\n", pUName,Status));
  541. break;
  542. }
  543. pDfEntry->dfe_RescLen = 0;
  544. // if it's a file...
  545. if (!IsDir)
  546. {
  547. // if this is a Mac application, make sure there is an APPL
  548. // mapping for it.
  549. if (FinderInfo.fd_TypeD == *(PDWORD)"APPL")
  550. {
  551. AfpAddAppl( pVolDesc,
  552. FinderInfo.fd_CreatorD,
  553. 0,
  554. pDfEntry->dfe_AfpId,
  555. True,
  556. pDfEntry->dfe_Parent->dfe_AfpId);
  557. }
  558. // Check for resource stream
  559. pStreams = AfpIoQueryStreams(&fshData);
  560. if (pStreams == NULL)
  561. {
  562. Status = STATUS_NO_MEMORY;
  563. break;
  564. }
  565. for (pCurStream = pStreams;
  566. pCurStream->si_StreamName.Buffer != NULL;
  567. pCurStream++)
  568. {
  569. if (IS_RESOURCE_STREAM(&pCurStream->si_StreamName))
  570. {
  571. pDfEntry->dfe_RescLen = pCurStream->si_StreamSize.LowPart;
  572. break;
  573. }
  574. }
  575. AfpFreeMemory(pStreams);
  576. }
  577. }
  578. // NOTE: if CdHfs doesn't have finder info, should we check for
  579. // that and set it ourselves using AfpSetFinderInfoByExtension?
  580. else
  581. {
  582. AfpSetFinderInfoByExtension(pUName, &pDfEntry->dfe_FinderInfo);
  583. pDfEntry->dfe_RescLen = 0;
  584. }
  585. }
  586. // Record common NTFS & CDFS information
  587. pDfEntry->dfe_CreateTime = AfpConvertTimeToMacFormat(&pFBDInfo->CreationTime);
  588. pDfEntry->dfe_LastModTime = pFBDInfo->LastWriteTime;
  589. pDfEntry->dfe_NtAttr = (USHORT)pFBDInfo->FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS;
  590. // if this is an HFS volume, check if Finder says file should be invisible
  591. if (IS_VOLUME_CD_HFS(pVolDesc))
  592. {
  593. if (pDfEntry->dfe_FinderInfo.fd_Attr1 & FINDER_FLAG_INVISIBLE)
  594. {
  595. pDfEntry->dfe_NtAttr |= FILE_ATTRIBUTE_HIDDEN;
  596. }
  597. }
  598. if (!IsDir)
  599. {
  600. pDfEntry->dfe_DataLen = pFBDInfo->EndOfFile.LowPart;
  601. }
  602. ASSERT(pDfEntry != NULL);
  603. } while (False); // error handling loop
  604. if (fshAfpInfo.fsh_FileHandle != NULL)
  605. {
  606. AfpIoClose(&fshAfpInfo);
  607. // NTFS will set the LastModify time to current time on file since we modified the
  608. // AfpInfo stream: restore the original time
  609. if (!IsDir)
  610. {
  611. // force ntfs to "flush" any pending time updates before we reset the time
  612. AfpIoChangeNTModTime(&fshData,
  613. &ModTime);
  614. AfpIoSetTimesnAttr(&fshData,
  615. NULL,
  616. &ModMacTime,
  617. 0,
  618. 0,
  619. NULL,
  620. NULL);
  621. }
  622. }
  623. if (fshData.fsh_FileHandle != NULL)
  624. {
  625. if (IS_VOLUME_NTFS(pVolDesc))
  626. {
  627. AfpPutBackROAttr(&fshData, WriteBackROAttr);
  628. }
  629. AfpIoClose(&fshData);
  630. }
  631. if (!NT_SUCCESS(Status))
  632. {
  633. pDfEntry = NULL;
  634. }
  635. *ppDfEntry = pDfEntry;
  636. }
  637. /*** afpReadIdDb
  638. *
  639. * This is called when a volume is added, if an existing Afp_IdIndex stream
  640. * is found on the root of the volume. The stream is is read in, the
  641. * VolDesc is initialized with the header image on disk, and the
  642. * IdDb sibling tree/hash tables are created from the data on disk.
  643. *
  644. **/
  645. LOCAL NTSTATUS FASTCALL
  646. afpReadIdDb(
  647. IN PVOLDESC pVolDesc,
  648. IN PFILESYSHANDLE pfshIdDb,
  649. OUT BOOLEAN * pfVerifyIndex
  650. )
  651. {
  652. PBYTE pReadBuf;
  653. PIDDBHDR pIdDbHdr;
  654. NTSTATUS Status;
  655. LONG SizeRead = 0, Count;
  656. FORKOFFST ForkOffst;
  657. UNICODE_STRING uName;
  658. UNALIGNED DISKENTRY * pCurDiskEnt = NULL;
  659. DWORD NameLen, CurEntSize, SizeLeft;
  660. LONG i, NumRead = 0;
  661. PDFENTRY pParentDfe = NULL, pCurDfe = NULL;
  662. struct _DirFileEntry ** DfeDirBucketStart;
  663. BOOLEAN LastBuf = False, ReadDb = False;
  664. PAGED_CODE( );
  665. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  666. ("afpReadIdDb: Reading existing Id Database header stream...\n") );
  667. do
  668. {
  669. if ((pReadBuf = AfpAllocPANonPagedMemory(IDDB_UPDATE_BUFLEN)) == NULL)
  670. {
  671. Status=STATUS_INSUFFICIENT_RESOURCES;
  672. break;
  673. }
  674. pIdDbHdr = (PIDDBHDR)pReadBuf;
  675. // read in the header
  676. Status = AfpIoRead(pfshIdDb,
  677. &LIZero,
  678. IDDB_UPDATE_BUFLEN,
  679. &SizeRead,
  680. (PBYTE)pIdDbHdr);
  681. if (!NT_SUCCESS(Status) ||
  682. (SizeRead < sizeof(IDDBHDR)) ||
  683. (pIdDbHdr->idh_Signature == AFP_SERVER_SIGNATURE_INITIDDB) ||
  684. (pIdDbHdr->idh_LastId < AFP_ID_NETWORK_TRASH))
  685. {
  686. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  687. ("Index database corrupted for volume %Z, rebuilding database\n",
  688. &pVolDesc->vds_Name) );
  689. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  690. ("Read sign = %lx, True sign = %lx\n",
  691. pIdDbHdr->idh_Signature, AFP_SERVER_SIGNATURE));
  692. // just recreate the stream
  693. Status = AFP_ERR_BAD_VERSION;
  694. AfpIoSetSize(pfshIdDb, 0);
  695. }
  696. else
  697. {
  698. if (pIdDbHdr->idh_Version == AFP_IDDBHDR_VERSION)
  699. {
  700. if ((pIdDbHdr->idh_Signature != AFP_SERVER_SIGNATURE) &&
  701. (pIdDbHdr->idh_Signature != AFP_SERVER_SIGNATURE_MANUALSTOP))
  702. {
  703. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  704. ("afpreadiddb: Totally corrupt sign = (%lx), required sign = (%lx)\n",
  705. pIdDbHdr->idh_Signature, AFP_SERVER_SIGNATURE));
  706. // just recreate the stream
  707. Status = AFP_ERR_BAD_VERSION;
  708. AfpIoSetSize(pfshIdDb, 0);
  709. }
  710. else
  711. {
  712. if (pIdDbHdr->idh_Signature == AFP_SERVER_SIGNATURE_MANUALSTOP)
  713. {
  714. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  715. ("afpreadIdDb: **** Need to verify index after reading it\n"));
  716. *pfVerifyIndex = True;
  717. }
  718. AfpIdDbHdrToVolDesc(pIdDbHdr, pVolDesc);
  719. if (SizeRead < (sizeof(IDDBHDR) + sizeof(DWORD)))
  720. {
  721. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  722. ("afpreadIdDb: Size incorrect\n"));
  723. Count = 0;
  724. Status = STATUS_END_OF_FILE;
  725. }
  726. else
  727. {
  728. Count = *(PDWORD)(pReadBuf + sizeof(IDDBHDR));
  729. if (Count != 0)
  730. {
  731. ReadDb = True;
  732. }
  733. else
  734. {
  735. Status = STATUS_END_OF_FILE;
  736. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  737. ("afpreadIdDb: Count incorrect\n"));
  738. }
  739. }
  740. }
  741. }
  742. else if ((pIdDbHdr->idh_Version != AFP_IDDBHDR_VERSION1) &&
  743. (pIdDbHdr->idh_Version != AFP_IDDBHDR_VERSION2) &&
  744. (pIdDbHdr->idh_Version != AFP_IDDBHDR_VERSION3) &&
  745. (pIdDbHdr->idh_Version != AFP_IDDBHDR_VERSION4))
  746. {
  747. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  748. ("afpreadIdDb: Bad Version (expected %x, actual %x)\n",
  749. AFP_IDDBHDR_VERSION,pIdDbHdr->idh_Version));
  750. // just recreate the stream
  751. AfpIoSetSize(pfshIdDb, 0);
  752. Status = AFP_ERR_BAD_VERSION;
  753. }
  754. else
  755. {
  756. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  757. ("afpreadIdDb: Downlevel Version, re-index volume\n"));
  758. Status = STATUS_END_OF_FILE;
  759. AfpIdDbHdrToVolDesc(pIdDbHdr, pVolDesc);
  760. // Dont blow away the header
  761. AfpIoSetSize(pfshIdDb, sizeof(IDDBHDR));
  762. AFPLOG_INFO(AFPSRVMSG_UPDATE_INDEX_VERSION,
  763. STATUS_SUCCESS,
  764. NULL,
  765. 0,
  766. &pVolDesc->vds_Name);
  767. }
  768. }
  769. if (!NT_SUCCESS(Status) || !ReadDb || (Count == 0))
  770. {
  771. break;
  772. }
  773. // read the count of entries from the stream
  774. ForkOffst.QuadPart = SizeRead;
  775. SizeLeft = SizeRead - (sizeof(IDDBHDR) + sizeof(DWORD));
  776. pCurDiskEnt = (UNALIGNED DISKENTRY *)(pReadBuf + (sizeof(IDDBHDR) + sizeof(DWORD)));
  777. // Start the database with the PARENT_OF_ROOT
  778. // At this point there is only the ParentOfRoot DFE in the volume.
  779. // Just access it rather can calling AfpFindDfEntryById
  780. DfeDirBucketStart = pVolDesc->vds_pDfeDirBucketStart;
  781. pParentDfe = DfeDirBucketStart[AFP_ID_PARENT_OF_ROOT];
  782. ASSERT (pParentDfe != NULL);
  783. ASSERT (pParentDfe->dfe_AfpId == AFP_ID_PARENT_OF_ROOT);
  784. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  785. ("afpreadIdDb: Number of entries %d\n", Count));
  786. while ((NumRead < Count) &&
  787. ((pVolDesc->vds_Flags & (VOLUME_STOPPED | VOLUME_DELETED)) == 0))
  788. {
  789. //
  790. // get the next entry
  791. //
  792. // We ensure that there are no partial entries. If an entry does not fit at
  793. // the end, we write an invalid entry (AfpId == 0) and skip to the next
  794. // buffer.
  795. if ((SizeLeft < (sizeof(DISKENTRY))) || (pCurDiskEnt->dsk_AfpId == 0))
  796. {
  797. if (LastBuf) // we have already read to the end of file
  798. {
  799. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  800. ("afpreadIdDb: Reached EOF\n"));
  801. Status = STATUS_UNSUCCESSFUL;
  802. break;
  803. }
  804. // Skip to the next page and continue with the next entry
  805. Status = AfpIoRead(pfshIdDb,
  806. &ForkOffst,
  807. IDDB_UPDATE_BUFLEN,
  808. &SizeRead,
  809. pReadBuf);
  810. if (!NT_SUCCESS(Status))
  811. {
  812. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  813. ("afpreadIdDb: Read Eror %lx\n", Status));
  814. break;
  815. }
  816. ForkOffst.QuadPart += SizeRead;
  817. // if we read less than we asked for, then we reached EOF
  818. LastBuf = (SizeRead < IDDB_UPDATE_BUFLEN) ? True : False;
  819. SizeLeft = SizeRead;
  820. pCurDiskEnt = (UNALIGNED DISKENTRY *)pReadBuf;
  821. continue;
  822. }
  823. //
  824. // check dsk_Signature for signature, just to be sure you are
  825. // still aligned on a structure and not off in la-la land
  826. //
  827. if (pCurDiskEnt->dsk_Signature != AFP_DISKENTRY_SIGNATURE)
  828. {
  829. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  830. ("afpreadIdDb: Signature mismatch\n"));
  831. Status = STATUS_DATATYPE_MISALIGNMENT;
  832. break;
  833. }
  834. ASSERT(pCurDiskEnt->dsk_AfpId != AFP_ID_NETWORK_TRASH);
  835. // add current entry to database
  836. if (pCurDiskEnt->dsk_AfpId != AFP_ID_ROOT)
  837. {
  838. AfpInitUnicodeStringWithNonNullTerm(&uName,
  839. (pCurDiskEnt->dsk_Flags & DFE_FLAGS_NAMELENBITS) * sizeof(WCHAR),
  840. (PWSTR)(pCurDiskEnt->dsk_Name));
  841. }
  842. else
  843. {
  844. // In case someone has reused a directory with an existing
  845. // AFP_IdIndex stream for a different volume name
  846. uName = pVolDesc->vds_Name;
  847. }
  848. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  849. ("afpReadIdDb: Read %d: %Z\n",
  850. pCurDiskEnt->dsk_AfpId,
  851. &uName));
  852. if ((pCurDfe = AfpAddDfEntry(pVolDesc,
  853. pParentDfe,
  854. &uName,
  855. (BOOLEAN)((pCurDiskEnt->dsk_Flags & DFE_FLAGS_DIR) == DFE_FLAGS_DIR),
  856. pCurDiskEnt->dsk_AfpId)) == NULL)
  857. {
  858. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  859. ("afpreadIdDb: AfpAddDfEntry failed for %Z (ParentId %x)\n",
  860. &uName, pParentDfe->dfe_AfpId));
  861. Status = STATUS_UNSUCCESSFUL;
  862. break;
  863. }
  864. // Initialize pointer to root.
  865. if (DFE_IS_ROOT(pCurDfe))
  866. {
  867. pVolDesc->vds_pDfeRoot = pCurDfe;
  868. }
  869. afpUpdateDfeWithSavedData(pCurDfe, pCurDiskEnt);
  870. NameLen = (DWORD)((pCurDiskEnt->dsk_Flags & DFE_FLAGS_NAMELENBITS)*sizeof(WCHAR));
  871. CurEntSize = DWLEN(FIELD_OFFSET(DISKENTRY, dsk_Name) + NameLen);
  872. NumRead ++;
  873. SizeLeft -= CurEntSize;
  874. pCurDiskEnt = (UNALIGNED DISKENTRY *)((PBYTE)pCurDiskEnt + CurEntSize);
  875. // figure out who the next parent should be
  876. if (pCurDfe->dfe_Flags & DFE_FLAGS_HAS_CHILD)
  877. {
  878. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  879. ("afpreadIdDb: Moving down %Z\n", &pCurDfe->dfe_UnicodeName));
  880. pParentDfe = pCurDfe;
  881. }
  882. else if (!(pCurDfe->dfe_Flags & DFE_FLAGS_HAS_SIBLING))
  883. {
  884. if (DFE_IS_PARENT_OF_ROOT(pParentDfe))
  885. {
  886. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  887. ("afpreadIdDb: Done, NumRead %d, Count %d\n", NumRead, Count));
  888. ASSERT(NumRead == Count);
  889. break;
  890. }
  891. while (!(pParentDfe->dfe_Flags & DFE_FLAGS_HAS_SIBLING))
  892. {
  893. if (DFE_IS_ROOT(pParentDfe))
  894. {
  895. break;
  896. }
  897. pParentDfe = pParentDfe->dfe_Parent;
  898. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  899. ("afpreadIdDb: Moving up\n"));
  900. }
  901. pParentDfe = pParentDfe->dfe_Parent;
  902. if (DFE_IS_PARENT_OF_ROOT(pParentDfe))
  903. {
  904. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  905. ("afpreadIdDb: Reached Id 1\n"));
  906. ASSERT(NumRead == Count);
  907. break;
  908. }
  909. }
  910. } // while
  911. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  912. ("Indexing: Read %ld entries (%lx)\n", NumRead,Status) );
  913. if (!NT_SUCCESS(Status))
  914. {
  915. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  916. ("afpReadIdDb: Indexing: Starting all over\n"));
  917. // Free everything and start over
  918. AfpFreeIdIndexTables(pVolDesc);
  919. afpInitializeIdDb(pVolDesc);
  920. }
  921. } while (False);
  922. if (pReadBuf != NULL)
  923. AfpFreePANonPagedMemory(pReadBuf, IDDB_UPDATE_BUFLEN);
  924. return Status;
  925. }
  926. VOID
  927. AfpGetDirFileHashSizes(
  928. IN PVOLDESC pVolDesc,
  929. OUT PDWORD pdwDirHashSz,
  930. OUT PDWORD pdwFileHashSz
  931. )
  932. {
  933. FILESYSHANDLE fshIdDb;
  934. ULONG CreateInfo;
  935. PBYTE pReadBuf=NULL;
  936. PIDDBHDR pIdDbHdr;
  937. DWORD dwDirFileCount=0;
  938. DWORD dwTemp;
  939. DWORD dwDirHshTblLen, dwFileHshTblLen;
  940. LONG SizeRead=0;
  941. NTSTATUS Status;
  942. BOOLEAN fFileOpened=FALSE;
  943. // in case we bail out..
  944. *pdwDirHashSz = IDINDEX_BUCKETS_DIR_INIT;
  945. *pdwFileHashSz = IDINDEX_BUCKETS_FILE_INIT;
  946. if (!IS_VOLUME_NTFS(pVolDesc))
  947. {
  948. goto AfpGetDirFileCount_Exit;
  949. }
  950. if ((pReadBuf = AfpAllocNonPagedMemory(1024)) == NULL)
  951. {
  952. goto AfpGetDirFileCount_Exit;
  953. }
  954. Status = AfpIoCreate(&pVolDesc->vds_hRootDir,
  955. AFP_STREAM_IDDB,
  956. &UNullString,
  957. FILEIO_ACCESS_READWRITE,
  958. FILEIO_DENY_WRITE,
  959. FILEIO_OPEN_FILE_SEQ,
  960. FILEIO_CREATE_INTERNAL,
  961. FILE_ATTRIBUTE_NORMAL,
  962. False,
  963. NULL,
  964. &fshIdDb,
  965. &CreateInfo,
  966. NULL,
  967. NULL,
  968. NULL);
  969. if (!NT_SUCCESS(Status))
  970. {
  971. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  972. ("AfpGetDirFileHashSizes: AfpIoCreate failed %lx for %Z\n",Status,&pVolDesc->vds_Name));
  973. goto AfpGetDirFileCount_Exit;
  974. }
  975. fFileOpened = TRUE;
  976. // if the file just got created, it didn't exist before!
  977. if (CreateInfo != FILE_OPENED)
  978. {
  979. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  980. ("AfpGetDirFileHashSizes: file created, not opened %d for %Z\n",CreateInfo,&pVolDesc->vds_Name));
  981. goto AfpGetDirFileCount_Exit;
  982. }
  983. //
  984. // if we reached here, it means that the file existed before, which means
  985. // we made an effort earlier to index this volume. We are going to read the
  986. // dwDirFileCount and calcualte the hash table sizes, but for now let's
  987. // initiliaze this to a high value
  988. //
  989. *pdwDirHashSz = IDINDEX_BUCKETS_32K;
  990. *pdwFileHashSz = IDINDEX_BUCKETS_32K;
  991. // read in the header
  992. Status = AfpIoRead(&fshIdDb,
  993. &LIZero,
  994. 1024,
  995. &SizeRead,
  996. pReadBuf);
  997. pIdDbHdr = (PIDDBHDR)pReadBuf;
  998. if (!NT_SUCCESS(Status) ||
  999. (SizeRead < (sizeof(IDDBHDR) + sizeof(DWORD))) ||
  1000. ((pIdDbHdr->idh_Signature != AFP_SERVER_SIGNATURE) &&
  1001. (pIdDbHdr->idh_Signature != AFP_SERVER_SIGNATURE_MANUALSTOP)))
  1002. {
  1003. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  1004. ("Reading DirFileCount: database corrupted for volume %Z!\n",
  1005. &pVolDesc->vds_Name) );
  1006. goto AfpGetDirFileCount_Exit;
  1007. }
  1008. else if (pIdDbHdr->idh_Version == AFP_IDDBHDR_VERSION)
  1009. {
  1010. dwDirFileCount = *(PDWORD)(pReadBuf + sizeof(IDDBHDR));
  1011. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1012. ("Reading DirFileCount = %d!\n",dwDirFileCount));
  1013. }
  1014. AfpGetDirFileCount_Exit:
  1015. if (fFileOpened)
  1016. {
  1017. AfpIoClose(&fshIdDb);
  1018. }
  1019. if (pReadBuf)
  1020. {
  1021. AfpFreeMemory(pReadBuf);
  1022. }
  1023. if (dwDirFileCount == 0)
  1024. {
  1025. return;
  1026. }
  1027. //
  1028. // first calculate the size of hashtable for Dirs
  1029. // We assume here that 10% of the (files+directories) are directories
  1030. // We want to try and keep no more than 20 nodes per hash bucket
  1031. //
  1032. dwDirHshTblLen = (dwDirFileCount / 10);
  1033. dwDirHshTblLen = (dwDirHshTblLen / 20);
  1034. if (dwDirHshTblLen <= IDINDEX_BUCKETS_DIR_MIN)
  1035. {
  1036. dwDirHshTblLen = IDINDEX_BUCKETS_DIR_MIN;
  1037. }
  1038. else if (dwDirHshTblLen >= IDINDEX_BUCKETS_MAX)
  1039. {
  1040. dwDirHshTblLen = IDINDEX_BUCKETS_MAX;
  1041. }
  1042. else
  1043. {
  1044. // find the smallest power of 2 that's bigger than dwDirHshTblLen
  1045. dwTemp = IDINDEX_BUCKETS_MAX;
  1046. while (dwDirHshTblLen < dwTemp)
  1047. {
  1048. dwTemp /= 2;
  1049. }
  1050. dwDirHshTblLen = 2*dwTemp;
  1051. }
  1052. //
  1053. // now, calculate the size of hashtable for Files
  1054. // (even though we should take 90% as number of files, we keep it that way!)
  1055. // We want to try and keep no more than 20 nodes per hash bucket
  1056. //
  1057. dwFileHshTblLen = (dwDirFileCount / 20);
  1058. if (dwFileHshTblLen <= IDINDEX_BUCKETS_FILE_MIN)
  1059. {
  1060. dwFileHshTblLen = IDINDEX_BUCKETS_FILE_MIN;
  1061. }
  1062. else if (dwFileHshTblLen >= IDINDEX_BUCKETS_MAX)
  1063. {
  1064. dwFileHshTblLen = IDINDEX_BUCKETS_MAX;
  1065. }
  1066. else
  1067. {
  1068. // find the smallest power of 2 that's bigger than dwFileHshTblLen
  1069. dwTemp = IDINDEX_BUCKETS_MAX;
  1070. while (dwFileHshTblLen < dwTemp)
  1071. {
  1072. dwTemp /= 2;
  1073. }
  1074. dwFileHshTblLen = 2*dwTemp;
  1075. }
  1076. *pdwDirHashSz = dwDirHshTblLen;
  1077. *pdwFileHashSz = dwFileHshTblLen;
  1078. }
  1079. /*** AfpFlushIdDb
  1080. *
  1081. * Initiated by the scavenger thread. If it is determined that the Afp_IdIndex
  1082. * stream for this volume is to be flushed to disk, then this is called to
  1083. * do the job. The swmr access is locked for read while the update is in
  1084. * progress. The vds_cScvgrIdDb and vds_cChangesIdDb are reset if there is
  1085. * no error writing the entire database. An initial count of zero is written
  1086. * to the database before it is updated, just in case an error occurs that
  1087. * prevents us from writing the entire database. When the entire thing is
  1088. * written, then the actual count of entries is written to disk. The
  1089. * parent-of-root entry is never saved on disk, nor is the network trash
  1090. * subtree of the database. The iddb header is written whether it is dirty
  1091. * or not.
  1092. *
  1093. * LOCKS: vds_VolLock (SPIN)
  1094. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Shared)
  1095. * LOCK_ORDER: vds_VolLock after vds_IdDbAccessLock
  1096. */
  1097. VOID FASTCALL
  1098. AfpFlushIdDb(
  1099. IN PVOLDESC pVolDesc,
  1100. IN PFILESYSHANDLE phIdDb
  1101. )
  1102. {
  1103. PBYTE pWriteBuf;
  1104. NTSTATUS Status;
  1105. BOOLEAN WriteEntireHdr = False;
  1106. PDFENTRY pCurDfe;
  1107. DWORD SizeLeft; // the number of free bytes left in the writebuffer
  1108. DWORD CurEntSize, NumWritten = 0;
  1109. FORKOFFST ForkOffst;
  1110. UNALIGNED DISKENTRY * pCurDiskEnt = NULL;
  1111. PIDDBHDR pIdDbHdr;
  1112. BOOLEAN HdrDirty = False;
  1113. KIRQL OldIrql;
  1114. DWORD fbi = 0, CreateInfo;
  1115. #ifdef PROFILING
  1116. TIME TimeS, TimeE, TimeD;
  1117. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_IdIndexUpdCount);
  1118. AfpGetPerfCounter(&TimeS);
  1119. #endif
  1120. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,("\tWriting out IdDb...\n") );
  1121. ASSERT(VALID_VOLDESC(pVolDesc));
  1122. // Take the Swmr so that nobody can initiate changes to the IdDb
  1123. AfpSwmrAcquireShared(&pVolDesc->vds_IdDbAccessLock);
  1124. do
  1125. {
  1126. if ((pWriteBuf = AfpAllocPANonPagedMemory(IDDB_UPDATE_BUFLEN)) == NULL)
  1127. {
  1128. Status = STATUS_INSUFFICIENT_RESOURCES;
  1129. break;
  1130. }
  1131. pIdDbHdr = (PIDDBHDR)pWriteBuf;
  1132. // Snapshot the IdDbHdr and dirty bit
  1133. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  1134. AfpVolDescToIdDbHdr(pVolDesc, pIdDbHdr);
  1135. if (!fAfpServerShutdownEvent)
  1136. {
  1137. DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_ERR,
  1138. ("AfpFlushIdDb: Corrupting signature during scavenger run\n"));
  1139. // If server going down due to admin stop, indicate it
  1140. // with a unique signature
  1141. if (fAfpAdminStop)
  1142. {
  1143. pIdDbHdr->idh_Signature = AFP_SERVER_SIGNATURE_MANUALSTOP;
  1144. }
  1145. else
  1146. {
  1147. pIdDbHdr->idh_Signature = AFP_SERVER_SIGNATURE_INITIDDB;
  1148. }
  1149. }
  1150. if (pVolDesc->vds_Flags & VOLUME_IDDBHDR_DIRTY)
  1151. {
  1152. HdrDirty = True;
  1153. // Clear the dirty bit
  1154. pVolDesc->vds_Flags &= ~VOLUME_IDDBHDR_DIRTY;
  1155. }
  1156. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  1157. // Set the count of entries to zero. Once we are done, we'll then
  1158. // overwrite it with the right value
  1159. *(PDWORD)(pWriteBuf + sizeof(IDDBHDR)) = 0;
  1160. // Set the pointer to the first entry we'll write past the header and
  1161. // the count of entries. Adjust space remaining.
  1162. pCurDiskEnt = (UNALIGNED DISKENTRY *)(pWriteBuf + sizeof(IDDBHDR) + sizeof(DWORD));
  1163. SizeLeft = IDDB_UPDATE_BUFLEN - (sizeof(IDDBHDR) + sizeof(DWORD));
  1164. ForkOffst.QuadPart = 0;
  1165. // start with the root (don't write out the parent of root)
  1166. pCurDfe = pVolDesc->vds_pDfeRoot;
  1167. ASSERT(pCurDfe != NULL);
  1168. Status = STATUS_SUCCESS;
  1169. DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_ERR,
  1170. ("AfpFlushIdDb: writing index file for vol %Z started..\n",&pVolDesc->vds_Name));
  1171. do
  1172. {
  1173. ASSERT(!DFE_IS_NWTRASH(pCurDfe));
  1174. // The size of the current entry is:
  1175. // DISKENTRY structure + namelen padded to 4*N
  1176. CurEntSize = DWLEN(FIELD_OFFSET(DISKENTRY, dsk_Name) + pCurDfe->dfe_UnicodeName.Length);
  1177. if (CurEntSize > SizeLeft)
  1178. {
  1179. // If there is atleast a DWORD left, write a ZERO there to
  1180. // indicate that the rest of the buffer is to be skipped.
  1181. // ZERO is an invalid AfpId.
  1182. // NOTE: dsk_AfpId NEEDS TO BE THE FIRST FIELD IN THE STRUCT
  1183. ASSERT(FIELD_OFFSET(DISKENTRY, dsk_AfpId) == 0);
  1184. if (SizeLeft > 0)
  1185. {
  1186. RtlZeroMemory(pWriteBuf + IDDB_UPDATE_BUFLEN - SizeLeft,
  1187. SizeLeft);
  1188. }
  1189. // write out the buffer and start at the beginning of buffer
  1190. Status = AfpIoWrite(phIdDb,
  1191. &ForkOffst,
  1192. IDDB_UPDATE_BUFLEN,
  1193. pWriteBuf);
  1194. if (!NT_SUCCESS(Status))
  1195. {
  1196. // Reset the dirty bit if need be
  1197. if (HdrDirty && (ForkOffst.QuadPart == 0))
  1198. {
  1199. AfpInterlockedSetDword(&pVolDesc->vds_Flags,
  1200. VOLUME_IDDBHDR_DIRTY,
  1201. &pVolDesc->vds_VolLock);
  1202. }
  1203. break;
  1204. }
  1205. ForkOffst.QuadPart += IDDB_UPDATE_BUFLEN;
  1206. SizeLeft = IDDB_UPDATE_BUFLEN;
  1207. pCurDiskEnt = (UNALIGNED DISKENTRY *)pWriteBuf;
  1208. }
  1209. NumWritten ++;
  1210. afpSaveDfeData(pCurDfe, pCurDiskEnt);
  1211. if (DFE_IS_DIRECTORY(pCurDfe))
  1212. {
  1213. PDIRENTRY pDirEntry = pCurDfe->dfe_pDirEntry;
  1214. pCurDiskEnt->dsk_RescLen = 0;
  1215. pCurDiskEnt->dsk_Access = *(PDWORD)(&pDirEntry->de_Access);
  1216. ASSERT (pCurDfe->dfe_pDirEntry != NULL);
  1217. if ((pCurDfe->dfe_FileOffspring > 0) ||
  1218. (pCurDfe->dfe_DirOffspring > 1) ||
  1219. ((pCurDfe->dfe_DirOffspring > 0) &&
  1220. !DFE_IS_NWTRASH(pDirEntry->de_ChildDir)))
  1221. {
  1222. pCurDiskEnt->dsk_Flags |= DFE_FLAGS_HAS_CHILD;
  1223. }
  1224. if (pCurDfe->dfe_NextSibling != NULL)
  1225. {
  1226. // Make sure we ignore the nwtrash folder
  1227. if (!DFE_IS_NWTRASH(pCurDfe->dfe_NextSibling) ||
  1228. (pCurDfe->dfe_NextSibling->dfe_NextSibling != NULL))
  1229. {
  1230. pCurDiskEnt->dsk_Flags |= DFE_FLAGS_HAS_SIBLING;
  1231. }
  1232. }
  1233. }
  1234. else
  1235. {
  1236. BOOLEAN fHasSibling;
  1237. pCurDiskEnt->dsk_RescLen = pCurDfe->dfe_RescLen;
  1238. pCurDiskEnt->dsk_DataLen = pCurDfe->dfe_DataLen;
  1239. DFE_FILE_HAS_SIBLING(pCurDfe, fbi, &fHasSibling);
  1240. if (fHasSibling)
  1241. {
  1242. pCurDiskEnt->dsk_Flags |= DFE_FLAGS_HAS_SIBLING;
  1243. }
  1244. }
  1245. // stick the current entry into the write buffer
  1246. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1247. ("AfpFlushIdDb: Writing %s %d: %Z, flags %x\n",
  1248. DFE_IS_DIRECTORY(pCurDfe) ? "Dir" : "File", pCurDfe->dfe_AfpId,
  1249. &pCurDfe->dfe_UnicodeName, pCurDiskEnt->dsk_Flags));
  1250. SizeLeft -= CurEntSize;
  1251. pCurDiskEnt = (UNALIGNED DISKENTRY *)((PBYTE)pCurDiskEnt + CurEntSize);
  1252. /*
  1253. * Get to the next DFE. The entire tree under the network-trash
  1254. * folder is ignored. The Next DFE is:
  1255. *
  1256. * if (the current DFE is a file)
  1257. * {
  1258. * if (there is another file sibling)
  1259. * {
  1260. * CurDfe = file Sibling;
  1261. * }
  1262. * else if (there is dir sibling)
  1263. * {
  1264. * CurDfe = dir Sibling;
  1265. * }
  1266. * else
  1267. * {
  1268. * CurDfe = Parent's next sibling directory;
  1269. * }
  1270. * }
  1271. * else
  1272. * {
  1273. * if (dir has any file children)
  1274. * {
  1275. * CurDfe = First file child;
  1276. * }
  1277. * else if (dir has any dir children)
  1278. * {
  1279. * CurDfe = First dir child;
  1280. * }
  1281. * if (there is a sibling)
  1282. * {
  1283. * CurDfe = dir Sibling;
  1284. * }
  1285. * else
  1286. * {
  1287. * CurDfe = Parent's next sibling directory;
  1288. * }
  1289. * }
  1290. * When we hit the root, we are done
  1291. *
  1292. */
  1293. if (DFE_IS_FILE(pCurDfe))
  1294. {
  1295. if (pCurDfe->dfe_NextSibling != NULL)
  1296. {
  1297. pCurDfe = pCurDfe->dfe_NextSibling;
  1298. }
  1299. else
  1300. {
  1301. PDIRENTRY pDirEntry;
  1302. pDirEntry = pCurDfe->dfe_Parent->dfe_pDirEntry;
  1303. fbi++;
  1304. while ((fbi < MAX_CHILD_HASH_BUCKETS) &&
  1305. (pDirEntry->de_ChildFile[fbi] == NULL))
  1306. {
  1307. fbi++;
  1308. }
  1309. if (fbi < MAX_CHILD_HASH_BUCKETS)
  1310. {
  1311. pCurDfe = pDirEntry->de_ChildFile[fbi];
  1312. continue;
  1313. }
  1314. // There are no more files from this parent. Try its children
  1315. // next. Ignore the NWTRASH folder.
  1316. fbi = 0;
  1317. if (pDirEntry->de_ChildDir != NULL)
  1318. {
  1319. if (!DFE_IS_NWTRASH(pDirEntry->de_ChildDir))
  1320. {
  1321. pCurDfe = pDirEntry->de_ChildDir;
  1322. continue;
  1323. }
  1324. if (pDirEntry->de_ChildDir->dfe_NextSibling != NULL)
  1325. {
  1326. pCurDfe = pDirEntry->de_ChildDir->dfe_NextSibling;
  1327. continue;
  1328. }
  1329. }
  1330. // No more 'dir' siblings, move on up.
  1331. if (!DFE_IS_ROOT(pCurDfe))
  1332. {
  1333. goto move_up;
  1334. }
  1335. else
  1336. {
  1337. // we've reached the root: we're done: get out of the loop
  1338. pCurDfe = NULL;
  1339. }
  1340. }
  1341. }
  1342. else
  1343. {
  1344. PDIRENTRY pDirEntry;
  1345. // Reset ChildFileBucket index. First check for and handle
  1346. // all file children of this directory and then move on to
  1347. // its children or siblings
  1348. fbi = 0;
  1349. pDirEntry = pCurDfe->dfe_pDirEntry;
  1350. while ((fbi < MAX_CHILD_HASH_BUCKETS) &&
  1351. (pDirEntry->de_ChildFile[fbi] == NULL))
  1352. {
  1353. fbi++;
  1354. }
  1355. if (fbi < MAX_CHILD_HASH_BUCKETS)
  1356. {
  1357. pCurDfe = pDirEntry->de_ChildFile[fbi];
  1358. continue;
  1359. }
  1360. if (pDirEntry->de_ChildDir != NULL)
  1361. {
  1362. pCurDfe = pDirEntry->de_ChildDir;
  1363. // don't bother writing out the network trash tree
  1364. if (DFE_IS_NWTRASH(pCurDfe))
  1365. {
  1366. // could be null, if so, we're done
  1367. pCurDfe = pCurDfe->dfe_NextSibling;
  1368. }
  1369. }
  1370. else if (pCurDfe->dfe_NextSibling != NULL)
  1371. {
  1372. pCurDfe = pCurDfe->dfe_NextSibling;
  1373. // don't bother writing out the network trash tree
  1374. if (DFE_IS_NWTRASH(pCurDfe))
  1375. {
  1376. // could be null, if so, we're done
  1377. pCurDfe = pCurDfe->dfe_NextSibling;
  1378. }
  1379. }
  1380. else if (!DFE_IS_ROOT(pCurDfe))
  1381. {
  1382. move_up:
  1383. while (pCurDfe->dfe_Parent->dfe_NextSibling == NULL)
  1384. {
  1385. if (DFE_IS_ROOT(pCurDfe->dfe_Parent))
  1386. {
  1387. break;
  1388. }
  1389. pCurDfe = pCurDfe->dfe_Parent;
  1390. }
  1391. // if ROOT then you get NULL
  1392. pCurDfe = pCurDfe->dfe_Parent->dfe_NextSibling; // if ROOT then you get NULL
  1393. // Make sure we ignore the nwtrash folder
  1394. if ((pCurDfe != NULL) && DFE_IS_NWTRASH(pCurDfe))
  1395. {
  1396. pCurDfe = pCurDfe->dfe_NextSibling; // Could be null
  1397. }
  1398. }
  1399. else break;
  1400. }
  1401. } while ((pCurDfe != NULL) && NT_SUCCESS(Status));
  1402. if (NT_SUCCESS(Status))
  1403. {
  1404. DWORD LastWriteSize, SizeRead;
  1405. LastWriteSize = (DWORD)ROUND_TO_PAGES(IDDB_UPDATE_BUFLEN - SizeLeft);
  1406. if (SizeLeft != IDDB_UPDATE_BUFLEN)
  1407. {
  1408. // write out the last bunch of the entries. Zero out unused space
  1409. RtlZeroMemory(pWriteBuf + IDDB_UPDATE_BUFLEN - SizeLeft,
  1410. LastWriteSize - (IDDB_UPDATE_BUFLEN - SizeLeft));
  1411. Status = AfpIoWrite(phIdDb,
  1412. &ForkOffst,
  1413. LastWriteSize,
  1414. pWriteBuf);
  1415. if (!NT_SUCCESS(Status))
  1416. {
  1417. // Reset the dirty bit if need be
  1418. if (HdrDirty && (ForkOffst.QuadPart == 0))
  1419. {
  1420. AfpInterlockedSetDword(&pVolDesc->vds_Flags,
  1421. VOLUME_IDDBHDR_DIRTY,
  1422. &pVolDesc->vds_VolLock);
  1423. }
  1424. break;
  1425. }
  1426. }
  1427. // set the file length to IdDb plus count plus header
  1428. Status = AfpIoSetSize(phIdDb,
  1429. ForkOffst.LowPart + LastWriteSize);
  1430. ASSERT(Status == AFP_ERR_NONE);
  1431. // write out the count of entries written to the file. Do a read-modify-write
  1432. // of the first page
  1433. ForkOffst.QuadPart = 0;
  1434. Status = AfpIoRead(phIdDb,
  1435. &ForkOffst,
  1436. PAGE_SIZE,
  1437. &SizeRead,
  1438. pWriteBuf);
  1439. ASSERT (NT_SUCCESS(Status) && (SizeRead == PAGE_SIZE));
  1440. if (!NT_SUCCESS(Status))
  1441. {
  1442. // set the file length to header plus count of zero entries
  1443. AfpIoSetSize(phIdDb,
  1444. sizeof(IDDBHDR)+sizeof(DWORD));
  1445. break;
  1446. }
  1447. *(PDWORD)(pWriteBuf + sizeof(IDDBHDR)) = NumWritten;
  1448. Status = AfpIoWrite(phIdDb,
  1449. &ForkOffst,
  1450. PAGE_SIZE,
  1451. pWriteBuf);
  1452. if (!NT_SUCCESS(Status))
  1453. {
  1454. // set the file length to header plus count of zero entries
  1455. AfpIoSetSize(phIdDb,
  1456. sizeof(IDDBHDR)+sizeof(NumWritten));
  1457. break;
  1458. }
  1459. // not protected since scavenger is the only consumer of this field
  1460. pVolDesc->vds_cScvgrIdDb = 0;
  1461. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  1462. ("Wrote %ld entries on volume %Z\n",NumWritten,&pVolDesc->vds_Name) );
  1463. ASSERT(Status == AFP_ERR_NONE);
  1464. }
  1465. } while (False);
  1466. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  1467. if (!NT_SUCCESS(Status))
  1468. {
  1469. DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_ERR,
  1470. ("AfpFlushIdDb: writing index file for vol %Z failed (%lx)\n",
  1471. &pVolDesc->vds_Name,Status));
  1472. AFPLOG_ERROR(AFPSRVMSG_WRITE_IDDB,
  1473. Status,
  1474. NULL,
  1475. 0,
  1476. &pVolDesc->vds_Name);
  1477. }
  1478. else
  1479. {
  1480. DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_INFO,
  1481. ("AfpFlushIdDb: writing index file for vol %Z finished.\n",&pVolDesc->vds_Name));
  1482. }
  1483. if (pWriteBuf != NULL)
  1484. {
  1485. AfpFreePANonPagedMemory(pWriteBuf, IDDB_UPDATE_BUFLEN);
  1486. }
  1487. #ifdef PROFILING
  1488. AfpGetPerfCounter(&TimeE);
  1489. TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
  1490. INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_IdIndexUpdTime,
  1491. TimeD,
  1492. &AfpStatisticsLock);
  1493. #endif
  1494. }
  1495. /*** AfpChangeNotifyThread
  1496. *
  1497. * Handle change notify requests queued by the notify completion routine.
  1498. */
  1499. VOID
  1500. AfpChangeNotifyThread(
  1501. IN PVOID pContext
  1502. )
  1503. {
  1504. PKQUEUE NotifyQueue;
  1505. PLIST_ENTRY pTimerList, pList, pNotifyList, pNext, pVirtualNotifyList;
  1506. LIST_ENTRY TransitionList;
  1507. PVOL_NOTIFY pVolNotify;
  1508. PVOLDESC pVolDesc;
  1509. PVOLDESC pCurrVolDesc=NULL;
  1510. ULONG BasePriority;
  1511. PLONG pNotifyQueueCount;
  1512. KIRQL OldIrql;
  1513. NTSTATUS Status;
  1514. DWORD ThisVolItems=0;
  1515. BOOLEAN fSwmrLocked=False;
  1516. #ifdef PROFILING
  1517. TIME TimeS, TimeE, TimeD;
  1518. #endif
  1519. LONG ShutdownPriority = LOW_REALTIME_PRIORITY; // Priority higher than file server
  1520. DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
  1521. ("AfpChangeNotifyThread: Starting %ld\n", pContext));
  1522. IoSetThreadHardErrorMode( FALSE );
  1523. // Boost our priority to just below low realtime.
  1524. // The idea is get the work done fast and get out of the way.
  1525. BasePriority = LOW_REALTIME_PRIORITY;
  1526. Status = NtSetInformationThread(NtCurrentThread(),
  1527. ThreadBasePriority,
  1528. &BasePriority,
  1529. sizeof(BasePriority));
  1530. ASSERT(NT_SUCCESS(Status));
  1531. pNotifyList = &AfpVolumeNotifyList[(LONG_PTR)pContext];
  1532. pVirtualNotifyList = &AfpVirtualMemVolumeNotifyList[(LONG_PTR)pContext];
  1533. NotifyQueue = &AfpVolumeNotifyQueue[(LONG_PTR)pContext];
  1534. pNotifyQueueCount = &AfpNotifyQueueCount[(LONG_PTR)pContext];
  1535. AfpThreadPtrsW[(LONG_PTR)pContext] = PsGetCurrentThread();
  1536. do
  1537. {
  1538. AFPTIME CurrentTime;
  1539. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  1540. if (!IsListEmpty(pVirtualNotifyList))
  1541. {
  1542. AfpSwmrAcquireExclusive(&AfpVolumeListSwmr);
  1543. pList = RemoveHeadList(pVirtualNotifyList);
  1544. AfpSwmrRelease(&AfpVolumeListSwmr);
  1545. }
  1546. else
  1547. {
  1548. // Wait for a change notify request to process or timeout
  1549. //pList = KeRemoveQueue(NotifyQueue, KernelMode, &TwoSecTimeOut);
  1550. pList = KeRemoveQueue(NotifyQueue, KernelMode, &OneSecTimeOut);
  1551. }
  1552. // We either have something to process or we timed out. In the latter case
  1553. // see if it is time to move some stuff from the list to the queue.
  1554. if ((NTSTATUS)((ULONG_PTR)pList) != STATUS_TIMEOUT)
  1555. {
  1556. pVolNotify = CONTAINING_RECORD(pList, VOL_NOTIFY, vn_List);
  1557. if (pVolNotify == &AfpTerminateNotifyThread)
  1558. {
  1559. DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_ERR,
  1560. ("ChangeNotifyThread Got AfpTerminateNotifyEvent Thread = %ld\n", (LONG_PTR)pContext));
  1561. if (pCurrVolDesc)
  1562. {
  1563. AfpSwmrRelease(&pCurrVolDesc->vds_IdDbAccessLock);
  1564. // remove that ref we put before grabbing the swmr lock
  1565. AfpVolumeDereference(pCurrVolDesc);
  1566. pCurrVolDesc = NULL;
  1567. }
  1568. // If these assertions fail, then there will be extra ref
  1569. // counts on some volumes due to unprocessed notifies,
  1570. // and the volumes will never go away.
  1571. ASSERT((*pNotifyQueueCount == 0) &&
  1572. (AfpNotifyListCount[(LONG_PTR)pContext] == 0) &&
  1573. IsListEmpty(pNotifyList));
  1574. break; // Asked to quit, so do so.
  1575. }
  1576. #ifdef PROFILING
  1577. AfpGetPerfCounter(&TimeS);
  1578. #endif
  1579. pVolDesc = pVolNotify->vn_pVolDesc;
  1580. //
  1581. // if we just moved to the next volume, release the lock for the
  1582. // previous volume, and also, grab the lock for the this volume
  1583. //
  1584. if (pVolDesc != pCurrVolDesc)
  1585. {
  1586. if ((pCurrVolDesc) && (fSwmrLocked))
  1587. {
  1588. AfpSwmrRelease(&pCurrVolDesc->vds_IdDbAccessLock);
  1589. // remove that ref we put before grabbing the swmr lock
  1590. AfpVolumeDereference(pCurrVolDesc);
  1591. }
  1592. pCurrVolDesc = pVolDesc;
  1593. ThisVolItems = 0;
  1594. // reference the volume, and deref when we release this swmr lock
  1595. if (AfpVolumeReference(pVolDesc))
  1596. {
  1597. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
  1598. fSwmrLocked = True;
  1599. }
  1600. else
  1601. {
  1602. DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_ERR,
  1603. ("AfpChangeNotifyThread: couldn't reference volume %Z!!\n",
  1604. &pVolDesc->vds_Name));
  1605. fSwmrLocked = False;
  1606. // Remove all private notifies which follow this one
  1607. AfpVolumeStopIndexing(pVolDesc, pVolNotify);
  1608. }
  1609. }
  1610. else
  1611. {
  1612. //
  1613. // if someone's waiting for the lock, release it and reacuire it
  1614. // to give him a chance
  1615. //
  1616. if ( (SWMR_SOMEONE_WAITING(&pVolDesc->vds_IdDbAccessLock)) &&
  1617. (ThisVolItems % 50 == 0) )
  1618. {
  1619. ASSERT(fSwmrLocked);
  1620. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  1621. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
  1622. }
  1623. }
  1624. ThisVolItems++;
  1625. if (!(((PFILE_NOTIFY_INFORMATION)(pVolNotify + 1))->Action & AFP_ACTION_PRIVATE))
  1626. {
  1627. (*pNotifyQueueCount) --;
  1628. }
  1629. ASSERT(VALID_VOLDESC(pVolDesc));
  1630. // The volume is already referenced for the notification processing.
  1631. // Dereference after finishing processing.
  1632. if ((pVolDesc->vds_Flags & (VOLUME_DELETED | VOLUME_STOPPED)) == 0)
  1633. {
  1634. #ifndef BLOCK_MACS_DURING_NOTIFYPROC
  1635. AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
  1636. #endif
  1637. (*pVolNotify->vn_Processor)(pVolNotify);
  1638. #ifndef BLOCK_MACS_DURING_NOTIFYPROC
  1639. AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
  1640. #endif
  1641. }
  1642. AfpVolumeDereference(pVolDesc);
  1643. // was this a private notify?
  1644. if (((PFILE_NOTIFY_INFORMATION)(pVolNotify + 1))->Action & AFP_ACTION_PRIVATE)
  1645. {
  1646. // Free virtual memory
  1647. afpFreeNotify(pVolNotify);
  1648. }
  1649. else
  1650. {
  1651. // Free non-paged memory
  1652. AfpFreeMemory(pVolNotify);
  1653. }
  1654. #ifdef PROFILING
  1655. AfpGetPerfCounter(&TimeE);
  1656. TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
  1657. INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_ChangeNotifyTime,
  1658. TimeD,
  1659. &AfpStatisticsLock);
  1660. INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_ChangeNotifyCount);
  1661. #endif
  1662. if (*pNotifyQueueCount > 0)
  1663. continue;
  1664. }
  1665. //
  1666. // release the lock for the volume we just finished
  1667. //
  1668. if ((pCurrVolDesc) && (fSwmrLocked))
  1669. {
  1670. AfpSwmrRelease(&pCurrVolDesc->vds_IdDbAccessLock);
  1671. // remove that ref we put before grabbing the swmr lock
  1672. AfpVolumeDereference(pCurrVolDesc);
  1673. }
  1674. // If we timed out because there was nothing in the queue, or we
  1675. // just processed the last thing in the queue, then see if there are
  1676. // more things that can be moved into the queue.
  1677. InitializeListHead(&TransitionList);
  1678. // Look at the list to see if some stuff should move to the
  1679. // Queue now i.e. if the delta has elapsed since we were notified of this change
  1680. AfpGetCurrentTimeInMacFormat(&CurrentTime);
  1681. ACQUIRE_SPIN_LOCK(&AfpVolumeListLock, &OldIrql);
  1682. while (!IsListEmpty(pNotifyList))
  1683. {
  1684. pList = RemoveHeadList(pNotifyList);
  1685. pVolNotify = CONTAINING_RECORD(pList, VOL_NOTIFY, vn_List);
  1686. pVolDesc = pVolNotify->vn_pVolDesc;
  1687. if ((pVolNotify->vn_TimeStamp == AFP_QUEUE_NOTIFY_IMMEDIATELY) ||
  1688. ((CurrentTime - pVolNotify->vn_TimeStamp) >= VOLUME_NTFY_DELAY) ||
  1689. (pVolDesc->vds_Flags & (VOLUME_STOPPED | VOLUME_DELETED)))
  1690. {
  1691. AfpNotifyListCount[(LONG_PTR)pContext] --;
  1692. (*pNotifyQueueCount) ++;
  1693. // Build up a list of items to queue up outside the spinlock
  1694. // since we will want to take the IdDb swmr for any volume
  1695. // which has notifies that we are about to process.
  1696. // Make sure you add things so they are processed in the
  1697. // order they came in with!!
  1698. InsertTailList(&TransitionList, pList);
  1699. }
  1700. else
  1701. {
  1702. // Put it back where we we took it from - its time has not come yet
  1703. // And we are done now since the list is ordered in time.
  1704. InsertHeadList(pNotifyList, pList);
  1705. break;
  1706. }
  1707. }
  1708. RELEASE_SPIN_LOCK(&AfpVolumeListLock, OldIrql);
  1709. while (!IsListEmpty(&TransitionList))
  1710. {
  1711. pList = TransitionList.Flink;
  1712. pVolNotify = CONTAINING_RECORD(pList, VOL_NOTIFY, vn_List);
  1713. pCurrVolDesc = pVolNotify->vn_pVolDesc;
  1714. //
  1715. // walk the entire list and collect all the items that belong to the
  1716. // same VolDesc and put them on the list. This way we take the swmr
  1717. // lock only for the volume we are working on
  1718. //
  1719. while (pList != &TransitionList)
  1720. {
  1721. pVolNotify = CONTAINING_RECORD(pList, VOL_NOTIFY, vn_List);
  1722. pVolDesc = pVolNotify->vn_pVolDesc;
  1723. pNext = pList->Flink;
  1724. if (pVolDesc == pCurrVolDesc)
  1725. {
  1726. RemoveEntryList(pList);
  1727. AfpVolumeQueueChangeNotify (pVolNotify, NotifyQueue);
  1728. //InsertTailList(pVirtualNotifyList, pList);
  1729. }
  1730. pList = pNext;
  1731. }
  1732. }
  1733. pCurrVolDesc = NULL;
  1734. } while (True);
  1735. DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
  1736. ("AfpChangeNotifyThread: Quitting %ld\n", pContext));
  1737. // Raise the priority of the current thread, so that this thread
  1738. // completes before any other thread interrupts it
  1739. NtSetInformationThread (
  1740. NtCurrentThread( ),
  1741. ThreadBasePriority,
  1742. &ShutdownPriority,
  1743. sizeof(ShutdownPriority)
  1744. );
  1745. AfpThreadPtrsW[(LONG_PTR)pContext] = NULL;
  1746. KeSetEvent(&AfpStopConfirmEvent, IO_NETWORK_INCREMENT, False);
  1747. Status = PsTerminateSystemThread (STATUS_SUCCESS);
  1748. ASSERT (NT_SUCCESS(Status));
  1749. }
  1750. /*** AfpProcessChangeNotify
  1751. *
  1752. * A change item has been dequeued by one of the notify processing threads.
  1753. *
  1754. * LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Exclusive)
  1755. */
  1756. VOID FASTCALL
  1757. AfpProcessChangeNotify(
  1758. IN PVOL_NOTIFY pVolNotify
  1759. )
  1760. {
  1761. PVOLDESC pVolDesc;
  1762. UNICODE_STRING UName, UParent, UTail;
  1763. PFILE_NOTIFY_INFORMATION pFNInfo;
  1764. BOOLEAN CleanupHandle;
  1765. PLIST_ENTRY pList;
  1766. NTSTATUS status;
  1767. PDFENTRY pDfEntry;
  1768. FILESYSHANDLE handle;
  1769. DWORD afpChangeAction;
  1770. DWORD StreamId;
  1771. PFILE_BOTH_DIR_INFORMATION pFBDInfo = NULL;
  1772. LONG infobuflen;
  1773. LONGLONG infobuf[(sizeof(FILE_BOTH_DIR_INFORMATION) + (AFP_LONGNAME_LEN + 1) * sizeof(WCHAR))/sizeof(LONGLONG) + 1];
  1774. #if DBG
  1775. static PBYTE Action[] = { "",
  1776. "ADDED",
  1777. "REMOVED",
  1778. "MODIFIED",
  1779. "RENAMED OLD",
  1780. "RENAMED NEW",
  1781. "STREAM ADDED",
  1782. "STREAM REMOVED",
  1783. "STREAM MODIFIED"};
  1784. #endif
  1785. PAGED_CODE( );
  1786. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  1787. ("AfpProcessChangeNotify: entered...\n"));
  1788. pFNInfo = (PFILE_NOTIFY_INFORMATION)(pVolNotify + 1);
  1789. ASSERT((pFNInfo->Action & AFP_ACTION_PRIVATE) == 0);
  1790. pVolDesc = pVolNotify->vn_pVolDesc;
  1791. ASSERT (VALID_VOLDESC(pVolDesc));
  1792. INTERLOCKED_DECREMENT_LONG(&pVolDesc->vds_cOutstandingNotifies);
  1793. StreamId = pVolNotify->vn_StreamId;
  1794. if ( (pFNInfo->Action == FILE_ACTION_REMOVED) ||
  1795. (pFNInfo->Action == FILE_ACTION_RENAMED_OLD_NAME) )
  1796. {
  1797. ASSERT(!IsListEmpty(&pVolDesc->vds_ChangeNotifyLookAhead));
  1798. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  1799. ("AfpProcessChangeNotify: removing %s LookAhead change\n",
  1800. Action[pFNInfo->Action]));
  1801. pList = ExInterlockedRemoveHeadList(&pVolDesc->vds_ChangeNotifyLookAhead,
  1802. &(pVolDesc->vds_VolLock.SpinLock));
  1803. ASSERT(pList == &(pVolNotify->vn_DelRenLink));
  1804. }
  1805. // Process each of the entries in the list for this volume
  1806. while (True)
  1807. {
  1808. CleanupHandle = False;
  1809. status = STATUS_SUCCESS;
  1810. AfpInitUnicodeStringWithNonNullTerm(&UName,
  1811. (USHORT)pFNInfo->FileNameLength,
  1812. pFNInfo->FileName);
  1813. UName.MaximumLength += (AFP_LONGNAME_LEN+1)*sizeof(WCHAR);
  1814. ASSERT(IS_VOLUME_NTFS(pVolDesc) && !EXCLUSIVE_VOLUME(pVolDesc));
  1815. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  1816. ("AfpProcessChangeNotify: Action: %s Name: %Z\n",
  1817. Action[pFNInfo->Action], &UName));
  1818. do
  1819. {
  1820. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  1821. ("AfpProcessChangeNotify: !!!!Processing Change!!!!\n"));
  1822. // We have the idindex write lock, look up the entry by path,
  1823. // open a handle to the item, query the appropriate info,
  1824. // cache the info in the DFE. Where necessary, open a handle
  1825. // to the parent directory and update its cached ModTime.
  1826. Lookup_Entry:
  1827. pDfEntry = afpFindEntryByNtPath(pVolDesc,
  1828. pFNInfo->Action,
  1829. &UName,
  1830. &UParent,
  1831. &UTail);
  1832. if (pDfEntry != NULL)
  1833. {
  1834. // Open a handle to parent or entity relative to the volume root handle
  1835. CleanupHandle = True;
  1836. status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  1837. StreamId,
  1838. FILEIO_OPEN_EITHER,
  1839. ((pFNInfo->Action == FILE_ACTION_ADDED) ||
  1840. (pFNInfo->Action == FILE_ACTION_REMOVED) ||
  1841. (pFNInfo->Action == FILE_ACTION_RENAMED_OLD_NAME)) ?
  1842. &UParent : &UName,
  1843. FILEIO_ACCESS_NONE,
  1844. FILEIO_DENY_NONE,
  1845. False,
  1846. &handle);
  1847. if (!NT_SUCCESS(status))
  1848. {
  1849. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  1850. ("AfpProcessChangeNotify: Failed to open (0x%lx) for %Z\n", status, &UName));
  1851. if (pFNInfo->Action == FILE_ACTION_ADDED) {
  1852. // Add the full-pathname of file relative to volume root
  1853. // to the DelayedNotifyList for the current VolumeDesc
  1854. // We assume here that the filename relative to the
  1855. // volume path will not be greater than 512
  1856. // characters (unicode)
  1857. status = AddToDelayedNotifyList(pVolDesc, &UName);
  1858. if (!NT_SUCCESS(status)) {
  1859. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  1860. ("AfpProcessChangeNotify: Error in addToDelayedList (0x%lx)\n", status));
  1861. break;
  1862. }
  1863. }
  1864. CleanupHandle = False;
  1865. break;
  1866. }
  1867. switch (pFNInfo->Action)
  1868. {
  1869. case FILE_ACTION_ADDED:
  1870. {
  1871. UNICODE_STRING UEntity;
  1872. UNICODE_STRING UTemp;
  1873. PDFENTRY pDfeNew;
  1874. FILESYSHANDLE fshEnumDir;
  1875. BOOLEAN firstEnum = True;
  1876. ULONG fileLength;
  1877. // Update timestamp of parent dir.
  1878. AfpIoQueryTimesnAttr(&handle,
  1879. NULL,
  1880. &pDfEntry->dfe_LastModTime,
  1881. NULL);
  1882. // enumerate parent handle for this entity to get
  1883. // the file/dir info, then add entry to the IDDB
  1884. /*
  1885. if ((UTail.Length/sizeof(WCHAR)) <= AFP_LONGNAME_LEN)
  1886. */
  1887. // Bug 311023
  1888. fileLength = RtlUnicodeStringToAnsiSize(&UTail)-1;
  1889. if (fileLength <= AFP_LONGNAME_LEN)
  1890. {
  1891. pFBDInfo = (PFILE_BOTH_DIR_INFORMATION)infobuf;
  1892. infobuflen = sizeof(infobuf);
  1893. }
  1894. else
  1895. {
  1896. infobuflen = sizeof(FILE_BOTH_DIR_INFORMATION) +
  1897. (MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR));
  1898. if ((pFBDInfo = (PFILE_BOTH_DIR_INFORMATION)
  1899. AfpAllocNonPagedMemory(infobuflen)) == NULL)
  1900. {
  1901. status = STATUS_NO_MEMORY;
  1902. break; // out of case FILE_ACTION_ADDED
  1903. }
  1904. }
  1905. do
  1906. {
  1907. status = AfpIoQueryDirectoryFile(&handle,
  1908. pFBDInfo,
  1909. infobuflen,
  1910. FileBothDirectoryInformation,
  1911. True,
  1912. True,
  1913. firstEnum ? &UTail : NULL);
  1914. if ((status == STATUS_BUFFER_TOO_SMALL) ||
  1915. (status == STATUS_BUFFER_OVERFLOW))
  1916. {
  1917. // Since we queue our own ACTION_ADDED for directories when
  1918. // caching a tree, we may have the case where we queued it
  1919. // by shortname because it actually had a name > 31 chars.
  1920. // Note that a 2nd call to QueryDirectoryFile after a buffer
  1921. // Overflow must send a null filename, since if the name is
  1922. // not null, it will override the restartscan parameter
  1923. // which means the scan will not restart from the beginning
  1924. // and we will not find the file name we are looking for.
  1925. ASSERT((PBYTE)pFBDInfo == (PBYTE)infobuf);
  1926. // This should never happen, but if it does...
  1927. if ((PBYTE)pFBDInfo != (PBYTE)infobuf)
  1928. {
  1929. status = STATUS_UNSUCCESSFUL;
  1930. break;
  1931. }
  1932. firstEnum = False;
  1933. infobuflen = sizeof(FILE_BOTH_DIR_INFORMATION) +
  1934. (MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR));
  1935. if ((pFBDInfo = (PFILE_BOTH_DIR_INFORMATION)
  1936. AfpAllocNonPagedMemory(infobuflen)) == NULL)
  1937. status = STATUS_NO_MEMORY;
  1938. }
  1939. } while ((status == STATUS_BUFFER_TOO_SMALL) ||
  1940. (status == STATUS_BUFFER_OVERFLOW));
  1941. if (status == STATUS_SUCCESS)
  1942. {
  1943. // If this file was created in a directory that has
  1944. // not been looked at by a mac, just ignore it.
  1945. // If this was not a FILE_ACTION_ADDED we would not
  1946. // have found it in the database at all if it was
  1947. // a file and the parent has not had its file
  1948. // children cached in, so we will ignore those
  1949. // notifies by default anyway since the DFE would
  1950. // come back as NULL from afpFindEntryByNtPath.
  1951. // We *do* want to process changes for directories
  1952. // even if the parent has not had its
  1953. // file children brought in since directories
  1954. // are only cached in once at volume startup.
  1955. if (((pFBDInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) &&
  1956. !DFE_CHILDREN_ARE_PRESENT(pDfEntry))
  1957. {
  1958. break;
  1959. }
  1960. // figure out which name to use
  1961. // If NT name > AFP_LONGNAME_LEN, use the NT shortname for
  1962. // Mac longname on NTFS, any other file system the shortname
  1963. // will be null, so ignore the file
  1964. // Bug 311023
  1965. AfpInitUnicodeStringWithNonNullTerm(&UTemp,
  1966. (USHORT)pFBDInfo->FileNameLength,
  1967. pFBDInfo->FileName);
  1968. fileLength=RtlUnicodeStringToAnsiSize(&UTemp)-1;
  1969. if (fileLength <= AFP_LONGNAME_LEN)
  1970. {
  1971. AfpInitUnicodeStringWithNonNullTerm(&UEntity,
  1972. (USHORT)pFBDInfo->FileNameLength,
  1973. pFBDInfo->FileName);
  1974. }
  1975. else if (pFBDInfo->ShortNameLength > 0)
  1976. {
  1977. AfpInitUnicodeStringWithNonNullTerm(&UEntity,
  1978. (USHORT)pFBDInfo->ShortNameLength,
  1979. pFBDInfo->ShortName);
  1980. }
  1981. else
  1982. {
  1983. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  1984. ("AfpProcessChangeNotify: Name is > 31 with no short name ?\n") );
  1985. status = STATUS_UNSUCCESSFUL;
  1986. }
  1987. if (NT_SUCCESS(status))
  1988. {
  1989. // add the entry
  1990. afpAddDfEntryAndCacheInfo(pVolDesc,
  1991. pDfEntry,
  1992. &UEntity,
  1993. &handle,
  1994. pFBDInfo,
  1995. &UName,
  1996. &pDfeNew,
  1997. True);
  1998. if (pDfeNew == NULL)
  1999. {
  2000. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_WARN,
  2001. ("AfpProcessChangeNotify: Could not add DFE for %Z\n", &UName));
  2002. }
  2003. else if (DFE_IS_DIRECTORY(pDfeNew))
  2004. {
  2005. // if a directory was added, we must see if it has
  2006. // children that we must cache as well
  2007. if (NT_SUCCESS(status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  2008. AFP_STREAM_DATA,
  2009. FILEIO_OPEN_DIR,
  2010. &UName,
  2011. FILEIO_ACCESS_NONE,
  2012. FILEIO_DENY_NONE,
  2013. False,
  2014. &fshEnumDir)))
  2015. {
  2016. status = AfpCacheDirectoryTree(pVolDesc,
  2017. pDfeNew,
  2018. GETENTIRETREE | REENUMERATE,
  2019. &fshEnumDir,
  2020. &UName);
  2021. AfpIoClose(&fshEnumDir);
  2022. if (!NT_SUCCESS(status))
  2023. {
  2024. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2025. ("AfpProcessChangeNotify: Could not cache dir tree for %Z\n",
  2026. &UName) );
  2027. }
  2028. }
  2029. else
  2030. {
  2031. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2032. ("AfpProcessChangeNotify: Added dir %Z but couldn't open it for enumerating\n", &UName) );
  2033. }
  2034. }
  2035. }
  2036. }
  2037. if (((PBYTE)pFBDInfo != NULL) &&
  2038. ((PBYTE)pFBDInfo != (PBYTE)infobuf))
  2039. {
  2040. AfpFreeMemory(pFBDInfo);
  2041. pFBDInfo = NULL;
  2042. }
  2043. }
  2044. break;
  2045. case FILE_ACTION_REMOVED:
  2046. {
  2047. // Remove entries from DelayedNotifyList for the
  2048. // volume belonging to the directory which is
  2049. // being removed since they need not be added to
  2050. // the IDDB after this
  2051. status = RemoveFromDelayedNotifyList(
  2052. pVolDesc,
  2053. &UName,
  2054. pFNInfo
  2055. );
  2056. if (!NT_SUCCESS(status)) {
  2057. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2058. ("Error in RemoveFromDelayedNotifyList 0x%lx\n", status));
  2059. }
  2060. // update timestamp of parent dir.
  2061. AfpIoQueryTimesnAttr(&handle,
  2062. NULL,
  2063. &pDfEntry->dfe_Parent->dfe_LastModTime,
  2064. NULL);
  2065. // remove entry from IDDb.
  2066. AfpDeleteDfEntry(pVolDesc, pDfEntry);
  2067. }
  2068. break;
  2069. case FILE_ACTION_MODIFIED:
  2070. {
  2071. FORKSIZE forklen;
  2072. DWORD NtAttr;
  2073. // NOTE: if a file is SUPERSEDED or OVERWRITTEN,
  2074. // you will only get a MODIFIED notification. Is
  2075. // there a way to check the creation date against
  2076. // what we have cached in order to figure out if
  2077. // this is what happened?
  2078. // query for times and attributes. If its a file,
  2079. // also query for the data fork length.
  2080. if (NT_SUCCESS(AfpIoQueryTimesnAttr(&handle,
  2081. &pDfEntry->dfe_CreateTime,
  2082. &pDfEntry->dfe_LastModTime,
  2083. &NtAttr)))
  2084. {
  2085. pDfEntry->dfe_NtAttr = (USHORT)NtAttr &
  2086. FILE_ATTRIBUTE_VALID_FLAGS;
  2087. }
  2088. if (DFE_IS_FILE(pDfEntry) &&
  2089. NT_SUCCESS(AfpIoQuerySize(&handle, &forklen)))
  2090. {
  2091. pDfEntry->dfe_DataLen = forklen.LowPart;
  2092. }
  2093. }
  2094. break;
  2095. case FILE_ACTION_RENAMED_OLD_NAME:
  2096. {
  2097. UNICODE_STRING UNewname;
  2098. PFILE_NOTIFY_INFORMATION pFNInfo2;
  2099. ULONG fileLength;
  2100. BOOLEAN checkIfDirectory=False;
  2101. status = STATUS_SUCCESS;
  2102. // The next item in the change buffer better be the
  2103. // new name -- consume this entry so we don't find
  2104. // it next time thru the loop. If there is none,
  2105. // then throw the whole thing out and assume the
  2106. // rename aborted in NTFS
  2107. if (pFNInfo->NextEntryOffset == 0)
  2108. break; // from switch
  2109. // If the next change in the buffer is not the
  2110. // new name, we don't want to advance over it,
  2111. // we want it to be processed next time thru
  2112. // the loop. Note we are assuming it is valid.
  2113. (PBYTE)pFNInfo2 = (PBYTE)pFNInfo + pFNInfo->NextEntryOffset;
  2114. ASSERT(pFNInfo2->Action == FILE_ACTION_RENAMED_NEW_NAME);
  2115. if (pFNInfo2->Action != FILE_ACTION_RENAMED_NEW_NAME)
  2116. {
  2117. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2118. ("AfpProcessChangeNotify: Rename did not come with new name!!!\n"));
  2119. break; // from switch
  2120. }
  2121. pFNInfo = pFNInfo2;
  2122. // update timestamp of parent dir.
  2123. AfpIoQueryTimesnAttr(&handle,
  2124. NULL,
  2125. &pDfEntry->dfe_Parent->dfe_LastModTime,
  2126. NULL);
  2127. // get the entity name from the path (subtract the
  2128. // parent path length from total length), if it is
  2129. // > 31 chars, we have to get the shortname by
  2130. // enumerating the parent for this item, since we
  2131. // already have a handle to the parent
  2132. AfpInitUnicodeStringWithNonNullTerm(&UNewname,
  2133. (USHORT)pFNInfo->FileNameLength,
  2134. pFNInfo->FileName);
  2135. if (DFE_IS_DIRECTORY(pDfEntry))
  2136. {
  2137. checkIfDirectory = True;
  2138. }
  2139. if (UParent.Length > 0)
  2140. {
  2141. // if the rename is not in the volume root,
  2142. // get rid of the path separator before the name
  2143. UNewname.Length -= UParent.Length + sizeof(WCHAR);
  2144. UNewname.Buffer = (PWCHAR)((PBYTE)UNewname.Buffer + UParent.Length + sizeof(WCHAR));
  2145. }
  2146. // Bug 311023
  2147. fileLength = RtlUnicodeStringToAnsiSize(&UNewname)-1;
  2148. if (fileLength > AFP_LONGNAME_LEN)
  2149. {
  2150. infobuflen = sizeof(FILE_BOTH_DIR_INFORMATION) +
  2151. (MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR));
  2152. if ((pFBDInfo = (PFILE_BOTH_DIR_INFORMATION)
  2153. AfpAllocNonPagedMemory(infobuflen)) == NULL)
  2154. {
  2155. status = STATUS_NO_MEMORY;
  2156. break; // out of case FILE_ACTION_RENAMED
  2157. }
  2158. status = AfpIoQueryDirectoryFile(&handle,
  2159. pFBDInfo,
  2160. infobuflen,
  2161. FileBothDirectoryInformation,
  2162. True,
  2163. True,
  2164. &UNewname);
  2165. if (status == STATUS_SUCCESS)
  2166. {
  2167. // figure out which name to use
  2168. // If NT name > AFP_LONGNAME_LEN, use the NT shortname for
  2169. // Mac longname on NTFS, any other file system the shortname
  2170. // will be null, so ignore the file
  2171. // Bug 311023
  2172. AfpInitUnicodeStringWithNonNullTerm(
  2173. &UNewname,
  2174. (USHORT)
  2175. pFBDInfo->FileNameLength,
  2176. pFBDInfo->FileName);
  2177. fileLength=RtlUnicodeStringToAnsiSize(&UNewname)-1;
  2178. if (fileLength <= AFP_LONGNAME_LEN)
  2179. {
  2180. AfpInitUnicodeStringWithNonNullTerm(&UNewname,
  2181. (USHORT)pFBDInfo->FileNameLength,
  2182. pFBDInfo->FileName);
  2183. }
  2184. else if (pFBDInfo->ShortNameLength > 0)
  2185. {
  2186. AfpInitUnicodeStringWithNonNullTerm(&UNewname,
  2187. (USHORT)pFBDInfo->ShortNameLength,
  2188. pFBDInfo->ShortName);
  2189. }
  2190. else
  2191. {
  2192. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2193. ("AfpProcessChangeNotify: Name is > 31 with no short name ?\n") );
  2194. status = STATUS_UNSUCCESSFUL;
  2195. }
  2196. }
  2197. }
  2198. // rename the entry
  2199. if (NT_SUCCESS(status))
  2200. {
  2201. AfpRenameDfEntry(pVolDesc, pDfEntry, &UNewname);
  2202. }
  2203. // Check if a directory is being renamed
  2204. // If yes, check if there are any files/directories
  2205. // which were not added to the IDDB due to ChangeNotify
  2206. // requests getting delayed
  2207. if (checkIfDirectory) {
  2208. status = CheckAndProcessDelayedNotify (
  2209. pVolDesc,
  2210. &UName,
  2211. &UNewname,
  2212. &UParent
  2213. );
  2214. if (!NT_SUCCESS(status))
  2215. {
  2216. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2217. ("Error in CheckAndProcessDelayedNotify 0x%lx\n", status));
  2218. }
  2219. }
  2220. if ((PBYTE)pFBDInfo != NULL)
  2221. {
  2222. AfpFreeMemory(pFBDInfo);
  2223. pFBDInfo = NULL;
  2224. }
  2225. }
  2226. break;
  2227. case FILE_ACTION_MODIFIED_STREAM:
  2228. {
  2229. FORKSIZE forklen;
  2230. // If it is the AFP_Resource stream on a file,
  2231. // cache the resource fork length.
  2232. if ((StreamId == AFP_STREAM_RESC) &&
  2233. DFE_IS_FILE(pDfEntry) &&
  2234. NT_SUCCESS(AfpIoQuerySize(&handle, &forklen)))
  2235. {
  2236. pDfEntry->dfe_RescLen = forklen.LowPart;
  2237. }
  2238. else if (StreamId == AFP_STREAM_INFO)
  2239. {
  2240. AFPINFO afpinfo;
  2241. FILEDIRPARM fdparms;
  2242. // Read the afpinfo stream. If the file ID in
  2243. // the DfEntry does not match the one in the
  2244. // stream, write back the ID we know it by.
  2245. // Update our cached FinderInfo.
  2246. if (NT_SUCCESS(AfpReadAfpInfo(&handle, &afpinfo)))
  2247. {
  2248. pDfEntry->dfe_FinderInfo = afpinfo.afpi_FinderInfo;
  2249. pDfEntry->dfe_BackupTime = afpinfo.afpi_BackupTime;
  2250. if (pDfEntry->dfe_AfpId != afpinfo.afpi_Id)
  2251. {
  2252. // munge up a fake FILEDIRPARMS structure
  2253. AfpInitializeFDParms(&fdparms);
  2254. fdparms._fdp_Flags = pDfEntry->dfe_Flags;
  2255. fdparms._fdp_AfpId = pDfEntry->dfe_AfpId;
  2256. AfpConvertMungedUnicodeToAnsi(&pDfEntry->dfe_UnicodeName,
  2257. &fdparms._fdp_LongName);
  2258. // NOTE: can we open a handle to afpinfo
  2259. // relative to a afpinfo handle??
  2260. AfpSetAfpInfo(&handle,
  2261. FILE_BITMAP_FILENUM,
  2262. &fdparms,
  2263. NULL,
  2264. NULL);
  2265. }
  2266. }
  2267. }
  2268. }
  2269. break; // from switch
  2270. default:
  2271. ASSERTMSG("AfpProcessChangeNotify: Unexpected Action\n", False);
  2272. break;
  2273. } // switch
  2274. }
  2275. else
  2276. {
  2277. PFILE_NOTIFY_INFORMATION pFNInfo2;
  2278. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_WARN,
  2279. ("AfpProcessChangeNotify: Could not find DFE for %Z\n", &UName));
  2280. // This item may have been deleted, renamed or moved
  2281. // by someone else in the interim, ignore this change
  2282. // if it was not a rename. If it was a rename, then
  2283. // try to add the item using the new name.
  2284. if ((pFNInfo->Action == FILE_ACTION_RENAMED_OLD_NAME) &&
  2285. (pFNInfo->NextEntryOffset != 0))
  2286. {
  2287. // If the next change in the buffer is not the
  2288. // new name, we don't want to advance over it,
  2289. // we want it to be processed next time thru
  2290. // the loop. Note we are assuming it is valid.
  2291. (PBYTE)pFNInfo2 = (PBYTE)pFNInfo + pFNInfo->NextEntryOffset;
  2292. ASSERT(pFNInfo2->Action == FILE_ACTION_RENAMED_NEW_NAME);
  2293. if (pFNInfo2->Action != FILE_ACTION_RENAMED_NEW_NAME)
  2294. {
  2295. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2296. ("AfpProcessChangeNotify: Rename did not come with new name!!! (no-DFE case)\n"));
  2297. break; // from error loop
  2298. }
  2299. pFNInfo = pFNInfo2;
  2300. pFNInfo->Action = FILE_ACTION_ADDED;
  2301. AfpInitUnicodeStringWithNonNullTerm(&UName,
  2302. (USHORT)pFNInfo->FileNameLength,
  2303. pFNInfo->FileName);
  2304. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  2305. ("AfpProcessChangeNotify: Converting RENAME into Action: %s for Name: %Z\n",
  2306. Action[pFNInfo->Action], &UName));
  2307. goto Lookup_Entry;
  2308. }
  2309. }
  2310. } while (False);
  2311. if (CleanupHandle)
  2312. AfpIoClose(&handle);
  2313. // Advance to the next entry in the change buffer
  2314. if (pFNInfo->NextEntryOffset == 0)
  2315. {
  2316. break;
  2317. }
  2318. (PBYTE)pFNInfo += pFNInfo->NextEntryOffset;
  2319. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_WARN,
  2320. ("More than one entry in notify ?\n"));
  2321. }
  2322. // Get new values for Free space on disk and update volume time
  2323. AfpUpdateVolFreeSpaceAndModTime(pVolDesc, TRUE);
  2324. }
  2325. /*** AddToDelayedNotifyList
  2326. *
  2327. * Add Pathname of delayed notify for added file to DelayedNotifyList of the
  2328. * corresponding volume
  2329. *
  2330. * LOCKS_ASSUMED: none
  2331. */
  2332. NTSTATUS FASTCALL
  2333. AddToDelayedNotifyList(
  2334. IN PVOLDESC pVolDesc,
  2335. IN PUNICODE_STRING pUName
  2336. )
  2337. {
  2338. KIRQL OldIrql;
  2339. PDELAYED_NOTIFY pDelayedNotify;
  2340. NTSTATUS status = STATUS_SUCCESS;
  2341. pDelayedNotify = (PDELAYED_NOTIFY)AfpAllocNonPagedMemory (sizeof(DELAYED_NOTIFY));
  2342. if (pDelayedNotify == NULL)
  2343. {
  2344. status = STATUS_INSUFFICIENT_RESOURCES;
  2345. return status;
  2346. }
  2347. pDelayedNotify->filename.Length = 0;
  2348. pDelayedNotify->filename.MaximumLength = pUName->MaximumLength;
  2349. pDelayedNotify->filename.Buffer = (PWSTR)AfpAllocNonPagedMemory(pUName->MaximumLength);
  2350. if (pDelayedNotify->filename.Buffer == NULL)
  2351. {
  2352. status = STATUS_INSUFFICIENT_RESOURCES;
  2353. AfpFreeMemory(pDelayedNotify);
  2354. return status;
  2355. }
  2356. RtlCopyUnicodeString(&pDelayedNotify->filename, pUName);
  2357. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  2358. InsertHeadList (&pVolDesc->vds_DelayedNotifyList, &pDelayedNotify->dn_List);
  2359. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  2360. return status;
  2361. }
  2362. /*** RemoveFromDelayedNotifyList
  2363. *
  2364. * Remove entry from DelayedNotifyList for files which are within the
  2365. * directory that was deleted
  2366. *
  2367. * LOCKS_ASSUMED: none
  2368. */
  2369. NTSTATUS
  2370. RemoveFromDelayedNotifyList (
  2371. IN PVOLDESC pVolDesc,
  2372. IN PUNICODE_STRING pUName,
  2373. IN PFILE_NOTIFY_INFORMATION pFNInfo
  2374. )
  2375. {
  2376. PLIST_ENTRY pList, pNext;
  2377. PDELAYED_NOTIFY pDelayedNotify;
  2378. KIRQL OldIrql;
  2379. NTSTATUS status=STATUS_SUCCESS;
  2380. AfpInitUnicodeStringWithNonNullTerm(pUName,
  2381. (USHORT)pFNInfo->FileNameLength,
  2382. pFNInfo->FileName);
  2383. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  2384. ("AfpProcessChangeNotify: Going to remove %Z \n", pUName));
  2385. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  2386. if (!IsListEmpty(&pVolDesc->vds_DelayedNotifyList)) {
  2387. pList = pVolDesc->vds_DelayedNotifyList.Flink;
  2388. while (pList != &pVolDesc->vds_DelayedNotifyList)
  2389. {
  2390. pDelayedNotify = CONTAINING_RECORD (pList, DELAYED_NOTIFY, dn_List);
  2391. pNext = pList->Flink;
  2392. if (pDelayedNotify->filename.Length >= pUName->Length)
  2393. {
  2394. if (RtlCompareMemory ((PVOID)pUName->Buffer, (PVOID)pDelayedNotify->filename.Buffer, pUName->Length) == pUName->Length) {
  2395. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR, ("AfpProcessChangeNotify: Correction no longer to be done for %Z\n", &pDelayedNotify->filename));
  2396. RemoveEntryList(pList);
  2397. AfpFreeMemory(pDelayedNotify->filename.Buffer);
  2398. AfpFreeMemory(pDelayedNotify);
  2399. }
  2400. }
  2401. pList = pNext;
  2402. }
  2403. }
  2404. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  2405. return status;
  2406. }
  2407. /*** CheckAndProcessDelayedNotify
  2408. *
  2409. * Check if the directory being renamed had entries that were not added to
  2410. * the IDDB. Rename the entries and issue new FILE_ACTION_ADDED ChangeNotify
  2411. * requests as if the NTFS filesystem has made the requests
  2412. *
  2413. * LOCKS_ASSUMED: none
  2414. */
  2415. NTSTATUS
  2416. CheckAndProcessDelayedNotify (
  2417. IN PVOLDESC pVolDesc,
  2418. IN PUNICODE_STRING pUName,
  2419. IN PUNICODE_STRING pUNewname,
  2420. IN PUNICODE_STRING pUParent
  2421. )
  2422. {
  2423. PLIST_ENTRY pList;
  2424. PDELAYED_NOTIFY pDelayedNotify;
  2425. KIRQL OldIrql;
  2426. NTSTATUS status = STATUS_SUCCESS;
  2427. PFILE_NOTIFY_INFORMATION pFNInfo;
  2428. PDFENTRY pParentDfEntry;
  2429. UNICODE_STRING newNotifyName;
  2430. PVOL_NOTIFY pVolNotify;
  2431. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  2432. pList = pVolDesc->vds_DelayedNotifyList.Flink;
  2433. while (1)
  2434. {
  2435. // finished the list?
  2436. if (pList == &pVolDesc->vds_DelayedNotifyList)
  2437. {
  2438. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  2439. break;
  2440. }
  2441. pDelayedNotify = CONTAINING_RECORD (pList, DELAYED_NOTIFY, dn_List);
  2442. pList = pList->Flink;
  2443. if (pDelayedNotify->filename.Length < pUName->Length)
  2444. {
  2445. continue;
  2446. }
  2447. if (RtlCompareMemory ((PVOID)pUName->Buffer,
  2448. (PVOID)pDelayedNotify->filename.Buffer,
  2449. pUName->Length) == pUName->Length)
  2450. {
  2451. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2452. ("AfpProcessChangeNotify: Correction required to be done for %Z\n", &pDelayedNotify->filename));
  2453. RemoveEntryList(&pDelayedNotify->dn_List);
  2454. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  2455. if ((newNotifyName.Buffer = (PWSTR)AfpAllocNonPagedMemory(1024)) == NULL)
  2456. {
  2457. status = STATUS_INSUFFICIENT_RESOURCES;
  2458. AfpFreeMemory(pDelayedNotify->filename.Buffer);
  2459. AfpFreeMemory(pDelayedNotify);
  2460. return status;
  2461. };
  2462. newNotifyName.Length = 0;
  2463. newNotifyName.MaximumLength = 1024;
  2464. if (pUParent->Length > 0)
  2465. {
  2466. RtlCopyUnicodeString (&newNotifyName, pUName);
  2467. // Copy the Parent name and the "/" separator and then the new name
  2468. RtlCopyMemory (newNotifyName.Buffer + pUParent->Length/2 + 1,
  2469. pUNewname->Buffer, pUNewname->Length);
  2470. newNotifyName.Length = pUParent->Length + pUNewname->Length + 2;
  2471. }
  2472. else
  2473. {
  2474. RtlCopyUnicodeString (&newNotifyName, pUNewname);
  2475. }
  2476. RtlAppendUnicodeToString (&newNotifyName,
  2477. pDelayedNotify->filename.Buffer + pUName->Length/2);
  2478. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2479. ("AfpProcessChangeNotify: After correction, name = %Z, Old=%Z, New=%Z, chgto=%Z\n",
  2480. &pDelayedNotify->filename, pUName, pUNewname, &newNotifyName));
  2481. pVolNotify = (PVOL_NOTIFY)AfpAllocNonPagedMemory(
  2482. sizeof(VOL_NOTIFY) +
  2483. (ULONG)(newNotifyName.Length) +
  2484. sizeof(FILE_NOTIFY_INFORMATION) +
  2485. (AFP_LONGNAME_LEN + 1)*sizeof(WCHAR));
  2486. if (pVolNotify != NULL)
  2487. {
  2488. AfpGetCurrentTimeInMacFormat(&pVolNotify->vn_TimeStamp);
  2489. pVolNotify->vn_pVolDesc = pVolDesc;
  2490. pVolNotify->vn_Processor = AfpProcessChangeNotify;
  2491. pVolNotify->vn_StreamId = AFP_STREAM_DATA;
  2492. pVolNotify->vn_TailLength = newNotifyName.Length;
  2493. pFNInfo = (PFILE_NOTIFY_INFORMATION)(pVolNotify + 1);
  2494. pFNInfo->Action = FILE_ACTION_ADDED;
  2495. pFNInfo->NextEntryOffset = 0;
  2496. pFNInfo->FileNameLength = newNotifyName.Length;
  2497. RtlCopyMemory((PVOID)&pFNInfo->FileName,
  2498. (PVOID)newNotifyName.Buffer,
  2499. newNotifyName.Length);
  2500. if (AfpVolumeReference(pVolDesc))
  2501. {
  2502. AfpVolumeInsertChangeNotifyList(pVolNotify, pVolDesc);
  2503. }
  2504. }
  2505. else
  2506. {
  2507. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,("Out of memory!!\n"));
  2508. ASSERT(0);
  2509. status = STATUS_INSUFFICIENT_RESOURCES;
  2510. AfpFreeMemory(newNotifyName.Buffer);
  2511. AfpFreeMemory(pDelayedNotify->filename.Buffer);
  2512. AfpFreeMemory(pDelayedNotify);
  2513. return status;
  2514. }
  2515. AfpFreeMemory(pDelayedNotify->filename.Buffer);
  2516. AfpFreeMemory(pDelayedNotify);
  2517. AfpFreeMemory(newNotifyName.Buffer);
  2518. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  2519. pList = pVolDesc->vds_DelayedNotifyList.Flink; // start from the head again
  2520. }
  2521. }
  2522. return status;
  2523. }
  2524. /*** afpProcessPrivateNotify
  2525. *
  2526. * Similar to AfpProcessChangeNotify but optimized/special-cased for private notifies.
  2527. *
  2528. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Exclusive)
  2529. */
  2530. VOID FASTCALL
  2531. afpProcessPrivateNotify(
  2532. IN PVOL_NOTIFY pVolNotify
  2533. )
  2534. {
  2535. PVOLDESC pVolDesc;
  2536. UNICODE_STRING UName;
  2537. PFILE_NOTIFY_INFORMATION pFNInfo;
  2538. NTSTATUS status;
  2539. PDFENTRY pParentDFE;
  2540. BOOLEAN CloseParentHandle = False, Verify = True;
  2541. BOOLEAN DirModified=TRUE;
  2542. BOOLEAN fNewVolume=FALSE;
  2543. PFILE_BOTH_DIR_INFORMATION pFBDInfo;
  2544. LONGLONG infobuf[(sizeof(FILE_BOTH_DIR_INFORMATION) +
  2545. (AFP_LONGNAME_LEN + 1) * sizeof(WCHAR))/sizeof(LONGLONG) + 1];
  2546. LONG infobuflen;
  2547. BOOLEAN fMemAlloced=FALSE;
  2548. PAGED_CODE( );
  2549. pFNInfo = (PFILE_NOTIFY_INFORMATION)(pVolNotify + 1);
  2550. ASSERT (pFNInfo->Action & AFP_ACTION_PRIVATE);
  2551. pVolDesc = pVolNotify->vn_pVolDesc;
  2552. ASSERT (VALID_VOLDESC(pVolDesc));
  2553. INTERLOCKED_DECREMENT_LONG(&pVolDesc->vds_cPrivateNotifies);
  2554. if (pVolDesc->vds_Flags & VOLUME_NEW_FIRST_PASS)
  2555. {
  2556. fNewVolume = TRUE;
  2557. }
  2558. pFBDInfo = (PFILE_BOTH_DIR_INFORMATION)infobuf;
  2559. AfpInitUnicodeStringWithNonNullTerm(&UName,
  2560. (USHORT)pFNInfo->FileNameLength,
  2561. pFNInfo->FileName);
  2562. // We always allocate extra space for notifies
  2563. UName.MaximumLength += (AFP_LONGNAME_LEN+1)*sizeof(WCHAR);
  2564. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  2565. ("ParentId %d, Path: %Z\n",pVolNotify->vn_ParentId, &UName));
  2566. // Lookup the parent DFE using the AfpId stored in the notify
  2567. // structure, and setup the UParent and UTail appropriately
  2568. pParentDFE = AfpFindDfEntryById(pVolDesc,
  2569. pVolNotify->vn_ParentId,
  2570. DFE_DIR);
  2571. if (pParentDFE != NULL)
  2572. {
  2573. FILESYSHANDLE ParentHandle, DirHandle;
  2574. status = STATUS_SUCCESS;
  2575. // Open a handle to the directory relative to the volume root handle
  2576. // Special case volume root
  2577. ASSERT((UName.Length != 0) || (pVolNotify->vn_ParentId == AFP_ID_ROOT));
  2578. do
  2579. {
  2580. PDFENTRY pDfeNew;
  2581. UNICODE_STRING UParent, UTail;
  2582. if (pVolNotify->vn_ParentId == AFP_ID_ROOT)
  2583. {
  2584. AfpSetEmptyUnicodeString(&UParent, 0, NULL);
  2585. UTail = UName;
  2586. }
  2587. else
  2588. {
  2589. UParent.MaximumLength =
  2590. UParent.Length = UName.Length - (USHORT)pVolNotify->vn_TailLength - sizeof(WCHAR);
  2591. UParent.Buffer = UName.Buffer;
  2592. UTail.MaximumLength =
  2593. UTail.Length = (USHORT)pVolNotify->vn_TailLength;
  2594. UTail.Buffer = (PWCHAR)((PBYTE)UName.Buffer + UParent.Length + sizeof(WCHAR));
  2595. }
  2596. if (UName.Length != 0)
  2597. {
  2598. // Open a handle to parent relative to the volume root handle
  2599. status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  2600. AFP_STREAM_DATA,
  2601. FILEIO_OPEN_DIR,
  2602. &UParent,
  2603. FILEIO_ACCESS_NONE,
  2604. FILEIO_DENY_NONE,
  2605. False,
  2606. &ParentHandle);
  2607. if (!NT_SUCCESS(status))
  2608. {
  2609. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2610. ("afpProcessPrivateNotify: Failed to open parent: %Z (0x%lx)\n",
  2611. &UParent, status));
  2612. break;
  2613. }
  2614. CloseParentHandle = True;
  2615. status = AfpIoQueryDirectoryFile(&ParentHandle,
  2616. pFBDInfo,
  2617. sizeof(infobuf),
  2618. FileBothDirectoryInformation,
  2619. True,
  2620. True,
  2621. &UTail);
  2622. //
  2623. // dir name longer than 31 char? Then we must allocate a buffer
  2624. //
  2625. if ((status == STATUS_BUFFER_OVERFLOW) ||
  2626. (status == STATUS_BUFFER_TOO_SMALL))
  2627. {
  2628. infobuflen = sizeof(FILE_BOTH_DIR_INFORMATION) +
  2629. (MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR));
  2630. pFBDInfo = (PFILE_BOTH_DIR_INFORMATION)
  2631. AfpAllocNonPagedMemory(infobuflen);
  2632. if (pFBDInfo == NULL)
  2633. {
  2634. status = STATUS_NO_MEMORY;
  2635. break;
  2636. }
  2637. fMemAlloced = TRUE;
  2638. status = AfpIoQueryDirectoryFile(&ParentHandle,
  2639. pFBDInfo,
  2640. infobuflen,
  2641. FileBothDirectoryInformation,
  2642. True,
  2643. True,
  2644. &UTail);
  2645. }
  2646. if (!NT_SUCCESS(status))
  2647. {
  2648. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2649. ("afpProcessPrivateNotify: AfpIoQueryDirectoryFile failed: %d %Z at %Z(0x%lx)\n",
  2650. sizeof(infobuf),&UTail, &UParent,status));
  2651. break;
  2652. }
  2653. // Lookup this entry in the data-base. If not there then we need to add
  2654. // it. If its there, we need to verify it.
  2655. // NOTE: Use DFE_ANY here and not DFE_DIR !!!
  2656. pDfeNew = AfpFindEntryByUnicodeName(pVolDesc,
  2657. &UTail,
  2658. AFP_LONGNAME,
  2659. pParentDFE,
  2660. DFE_ANY);
  2661. if (pDfeNew == NULL)
  2662. {
  2663. Verify = False;
  2664. }
  2665. }
  2666. else
  2667. {
  2668. FILE_BASIC_INFORMATION FBasInfo;
  2669. status = AfpIoQueryBasicInfo(&pVolDesc->vds_hRootDir,
  2670. &FBasInfo);
  2671. if (!NT_SUCCESS(status))
  2672. {
  2673. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2674. ("afpProcessPrivateNotify: Could not get basic information for root\n"));
  2675. status = STATUS_UNSUCCESSFUL;
  2676. break;
  2677. }
  2678. afpConvertBasicToBothDirInfo(&FBasInfo, pFBDInfo);
  2679. pDfeNew = pParentDFE;
  2680. ParentHandle = pVolDesc->vds_hRootDir;
  2681. // Root directory needs special casing. The reason here is that we have
  2682. // no parent directory. Also we need to handle the AFP_HAS_CUSTOM_ICON
  2683. // bit in the volume descriptor since the finderinfo on the root volume
  2684. // doesn't have this bit saved.
  2685. if (pVolDesc->vds_Flags & AFP_VOLUME_HAS_CUSTOM_ICON)
  2686. {
  2687. pDfeNew->dfe_FinderInfo.fd_Attr1 |= FINDER_FLAG_HAS_CUSTOM_ICON;
  2688. }
  2689. }
  2690. if (!Verify)
  2691. {
  2692. ASSERT(pDfeNew == NULL);
  2693. afpAddDfEntryAndCacheInfo(pVolDesc,
  2694. pParentDFE,
  2695. &UTail,
  2696. &ParentHandle,
  2697. pFBDInfo,
  2698. &UName,
  2699. &pDfeNew,
  2700. False);
  2701. }
  2702. else if (pFBDInfo->LastWriteTime.QuadPart > pDfeNew->dfe_LastModTime.QuadPart)
  2703. {
  2704. pDfeNew->dfe_Flags &= ~DFE_FLAGS_INIT_COMPLETED;
  2705. afpVerifyDFE(pVolDesc,
  2706. pParentDFE,
  2707. &UTail,
  2708. &ParentHandle,
  2709. pFBDInfo,
  2710. &UName,
  2711. &pDfeNew);
  2712. DirModified = TRUE;
  2713. }
  2714. else
  2715. {
  2716. DirModified = FALSE;
  2717. }
  2718. if (pDfeNew == NULL)
  2719. {
  2720. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2721. ("afpProcessPrivateNotify: Could not add DFE for %Z\n", &UName));
  2722. status = STATUS_UNSUCCESSFUL;
  2723. break;
  2724. }
  2725. pParentDFE = pDfeNew;
  2726. //
  2727. // Now open the directory itself so that it can be enumerated
  2728. // Open the directory relative to its parent since we already
  2729. // have the parent handle open.
  2730. //
  2731. if (Verify && !DirModified && (pVolNotify->vn_ParentId != AFP_ID_ROOT))
  2732. {
  2733. }
  2734. else
  2735. {
  2736. status = AfpIoOpen(&ParentHandle,
  2737. AFP_STREAM_DATA,
  2738. FILEIO_OPEN_DIR,
  2739. &UTail,
  2740. FILEIO_ACCESS_NONE,
  2741. FILEIO_DENY_NONE,
  2742. False,
  2743. &DirHandle);
  2744. if (!NT_SUCCESS(status))
  2745. {
  2746. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2747. ("afpProcessPrivateNotify: AfpIoOpen failed: %Z (0x%lx)\n",
  2748. &UTail, status));
  2749. break;
  2750. }
  2751. }
  2752. } while (False);
  2753. if (fMemAlloced)
  2754. {
  2755. ASSERT(pFBDInfo != ((PFILE_BOTH_DIR_INFORMATION)(infobuf)));
  2756. AfpFreeMemory(pFBDInfo);
  2757. }
  2758. if (CloseParentHandle)
  2759. {
  2760. AfpIoClose(&ParentHandle);
  2761. }
  2762. if (NT_SUCCESS(status))
  2763. {
  2764. DWORD Method;
  2765. // Always get the root level files
  2766. if (Verify && !DirModified && (pVolNotify->vn_ParentId != AFP_ID_ROOT))
  2767. {
  2768. Method = GETDIRSKELETON;
  2769. }
  2770. else
  2771. {
  2772. Method = (GETENTIRETREE | REENUMERATE);
  2773. }
  2774. status = AfpCacheDirectoryTree(pVolDesc,
  2775. pParentDFE,
  2776. Method,
  2777. &DirHandle,
  2778. &UName);
  2779. if (!NT_SUCCESS(status))
  2780. {
  2781. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2782. ("afpProcessPrivateNotify: CacheDir failed %lx tree for %Z\n", status,&UName));
  2783. }
  2784. if (Method != GETDIRSKELETON)
  2785. {
  2786. AfpIoClose(&DirHandle);
  2787. }
  2788. }
  2789. }
  2790. afpActivateVolume(pVolDesc);
  2791. }
  2792. /*** afpActivateVolume
  2793. *
  2794. * If we just finished caching in the directory structure, activate the volume now.
  2795. * This is keyed off the AFP_INITIAL_CACHE bit in the volume flags.
  2796. */
  2797. VOID FASTCALL
  2798. afpActivateVolume(
  2799. IN PVOLDESC pVolDesc
  2800. )
  2801. {
  2802. BOOLEAN fCdfs;
  2803. KIRQL OldIrql;
  2804. NTSTATUS Status = STATUS_SUCCESS;
  2805. UNICODE_STRING RootName;
  2806. PVOLDESC pWalkVolDesc;
  2807. IDDBHDR IdDbHdr;
  2808. BOOLEAN fPostIrp;
  2809. BOOLEAN fRetry=TRUE;
  2810. LARGE_INTEGER ActivationTime;
  2811. ULONG HighPart;
  2812. ULONG LowPart;
  2813. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  2814. // if we have more notifies queued up, we aren't done yet
  2815. if (pVolDesc->vds_cPrivateNotifies != 0)
  2816. {
  2817. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  2818. return;
  2819. }
  2820. //
  2821. // ok, we're here because the scan of the volume completed
  2822. //
  2823. //
  2824. // if this was a newly created volume and if this was its first pass, we must
  2825. // post the change-notify irp and restart the scan
  2826. //
  2827. if (pVolDesc->vds_Flags & VOLUME_NEW_FIRST_PASS)
  2828. {
  2829. pVolDesc->vds_Flags &= ~VOLUME_NEW_FIRST_PASS;
  2830. // we post a change notify irp if this volume is not an exclusive volume
  2831. fPostIrp = (!EXCLUSIVE_VOLUME(pVolDesc));
  2832. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  2833. if (fPostIrp)
  2834. {
  2835. // Begin monitoring changes to the tree. Even though we may
  2836. // start processing PC changes before we have finished
  2837. // enumerating the tree, if we get notified of part of the
  2838. // tree we have yet to cache (and therefore can't find it's
  2839. // path in our database its ok, since we will end up
  2840. // picking up the change when we enumerate that branch. Also,
  2841. // by posting this before starting to cache the tree instead
  2842. // of after, we will pick up any changes that are made to parts
  2843. // of the tree we have already seen, otherwise we would miss
  2844. // those.
  2845. // Explicitly reference this volume for ChangeNotifies and post it
  2846. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  2847. DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_ERR,
  2848. ("AfpAdmWVolumeAdd: posting chg-notify irp on volume %Z in second pass\n",
  2849. &pVolDesc->vds_Name));
  2850. if (AfpVolumeReference(pVolDesc))
  2851. {
  2852. pVolDesc->vds_RequiredNotifyBufLen = AFP_VOLUME_NOTIFY_STARTING_BUFSIZE;
  2853. Status = AfpVolumePostChangeNotify(pVolDesc);
  2854. if (NT_SUCCESS(Status))
  2855. {
  2856. // scan the entire directory tree and sync disk with iddb, now that
  2857. // we are in the second pass for this (not-so) newly created volume
  2858. AfpSetEmptyUnicodeString(&RootName, 0, NULL);
  2859. AfpQueuePrivateChangeNotify(pVolDesc,
  2860. &RootName,
  2861. &RootName,
  2862. AFP_ID_ROOT);
  2863. }
  2864. else
  2865. {
  2866. AFPLOG_ERROR(AFPSRVMSG_START_VOLUME,
  2867. Status,
  2868. NULL,
  2869. 0,
  2870. &pVolDesc->vds_Name);
  2871. DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_ERR,
  2872. ("AfpAdmWVolumeAdd: posting chg-notify failed (%lx)!!\n",Status));
  2873. AfpVolumeDereference(pVolDesc);
  2874. ASSERT(0);
  2875. }
  2876. }
  2877. else
  2878. {
  2879. DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_ERR,
  2880. ("AfpAdmWVolumeAdd: couldn't reference volume %Z!!\n",&pVolDesc->vds_Name));
  2881. }
  2882. }
  2883. }
  2884. //
  2885. // ok, we are through with all the passes and the scan is successful:
  2886. // mark the volume as 'officially' available to the clients
  2887. //
  2888. else if (pVolDesc->vds_Flags & VOLUME_INITIAL_CACHE)
  2889. {
  2890. pVolDesc->vds_Flags |= VOLUME_SCAVENGER_RUNNING;
  2891. pVolDesc->vds_Flags &= ~VOLUME_INITIAL_CACHE;
  2892. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  2893. // Set the type of the ICON<0d> file to 0's so that
  2894. // mac apps will not list it in their file-open dialogs
  2895. fCdfs = !IS_VOLUME_NTFS(pVolDesc);
  2896. if (!fCdfs)
  2897. {
  2898. PDFENTRY pdfetmp;
  2899. UNICODE_STRING iconstr;
  2900. WCHAR iconname[5] = AFPSERVER_VOLUME_ICON_FILE_ANSI;
  2901. AfpInitUnicodeStringWithNonNullTerm(&iconstr,
  2902. sizeof(iconname),
  2903. iconname);
  2904. if ((pdfetmp = AfpFindEntryByUnicodeName(pVolDesc,
  2905. &iconstr,
  2906. AFP_LONGNAME,
  2907. pVolDesc->vds_pDfeRoot,
  2908. DFE_FILE)) != NULL)
  2909. {
  2910. pdfetmp->dfe_FinderInfo.fd_TypeD = 0;
  2911. }
  2912. // Kick off the OurChange scavenger scheduled routine
  2913. // Explicitly reference this for the scavenger routine
  2914. if (AfpVolumeReference(pVolDesc))
  2915. {
  2916. // Schedule the scavenger to run periodically. This scavenger
  2917. // is queued since it acquires a SWMR.
  2918. AfpScavengerScheduleEvent(AfpOurChangeScavenger,
  2919. pVolDesc,
  2920. VOLUME_OURCHANGE_AGE,
  2921. False);
  2922. }
  2923. else
  2924. {
  2925. DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_ERR,
  2926. ("afpActivateVolume: couldn't reference volume %Z!!\n",
  2927. &pVolDesc->vds_Name));
  2928. }
  2929. }
  2930. Status = AfpIoQueryTimesnAttr(&pVolDesc->vds_hRootDir,
  2931. &pVolDesc->vds_pDfeRoot->dfe_CreateTime,
  2932. &pVolDesc->vds_pDfeRoot->dfe_LastModTime,
  2933. NULL);
  2934. pVolDesc->vds_CreateTime = pVolDesc->vds_pDfeRoot->dfe_CreateTime;
  2935. if (NT_SUCCESS(Status))
  2936. {
  2937. pVolDesc->vds_ModifiedTime =
  2938. AfpConvertTimeToMacFormat(&pVolDesc->vds_pDfeRoot->dfe_LastModTime);
  2939. }
  2940. else
  2941. {
  2942. pVolDesc->vds_ModifiedTime = pVolDesc->vds_pDfeRoot->dfe_CreateTime;
  2943. }
  2944. // Kick off the scavenger thread scheduled routine
  2945. // Explicitly reference this for the scavenger routine
  2946. if (AfpVolumeReference(pVolDesc))
  2947. {
  2948. //
  2949. // let's save the index file the way we know it now
  2950. // (setting vds_cScvgrIdDb to 1 will trigger it via scavenger thread)
  2951. //
  2952. if (IS_VOLUME_NTFS(pVolDesc))
  2953. {
  2954. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  2955. pVolDesc->vds_cScvgrIdDb = 1;
  2956. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  2957. }
  2958. // Schedule the scavenger to run periodically. Always make the
  2959. // scavenger use the worker thread for CD-ROM volumes since we
  2960. // 'nudge' it every invocation to see if the CD is in the drive
  2961. AfpScavengerScheduleEvent(AfpVolumeScavenger,
  2962. pVolDesc,
  2963. fCdfs ?
  2964. VOLUME_CDFS_SCAVENGER_INTERVAL :
  2965. VOLUME_NTFS_SCAVENGER_INTERVAL,
  2966. fCdfs);
  2967. //
  2968. // another workaround for an Apple bug. If creation date on two
  2969. // volumes is identical, the alias manager gets all confused and points
  2970. // alias for one guy to another!
  2971. // See if this volume's creation date is the same as any of the other
  2972. // volume's creation date: if so, add 1 second
  2973. //
  2974. while (fRetry)
  2975. {
  2976. fRetry = FALSE;
  2977. ACQUIRE_SPIN_LOCK(&AfpVolumeListLock, &OldIrql);
  2978. for (pWalkVolDesc = AfpVolumeList;
  2979. pWalkVolDesc != NULL;
  2980. pWalkVolDesc = pWalkVolDesc->vds_Next)
  2981. {
  2982. if (pWalkVolDesc == pVolDesc)
  2983. {
  2984. continue;
  2985. }
  2986. if (pWalkVolDesc->vds_CreateTime == pVolDesc->vds_CreateTime)
  2987. {
  2988. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  2989. ("Vol creation date conflict: %Z and %Z. Hack-o-rama at work\n",
  2990. &pVolDesc->vds_Name,&pWalkVolDesc->vds_Name));
  2991. pVolDesc->vds_CreateTime += 1;
  2992. fRetry = TRUE;
  2993. break;
  2994. }
  2995. }
  2996. RELEASE_SPIN_LOCK(&AfpVolumeListLock,OldIrql);
  2997. }
  2998. KeQuerySystemTime (&ActivationTime);
  2999. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  3000. ("afpActivateVolume: volume %Z activated %s at %8lx%08lx , max notifies=%ld\n",
  3001. &pVolDesc->vds_Name,
  3002. (pVolDesc->vds_Flags & AFP_VOLUME_SUPPORTS_CATSRCH) ?
  3003. " " : "(CatSearch disabled)",
  3004. 0xffffffff*ActivationTime.HighPart,
  3005. 0xffffffff*ActivationTime.LowPart,
  3006. pVolDesc->vds_maxPrivateNotifies
  3007. ));
  3008. if ((int)(pVolDesc->vds_IndxStTime.LowPart-ActivationTime.LowPart) >= 0)
  3009. {
  3010. LowPart = pVolDesc->vds_IndxStTime.LowPart-ActivationTime.LowPart;
  3011. HighPart = pVolDesc->vds_IndxStTime.HighPart-ActivationTime.HighPart;
  3012. }
  3013. else
  3014. {
  3015. LowPart = 0xffffffff - ActivationTime.LowPart + 1 + pVolDesc->vds_IndxStTime.LowPart;
  3016. HighPart = pVolDesc->vds_IndxStTime.HighPart-ActivationTime.HighPart -1;
  3017. }
  3018. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  3019. ("Time taken for indexing = %8lx%08lx\n",
  3020. 0xffffffff*(pVolDesc->vds_IndxStTime.HighPart-ActivationTime.HighPart),
  3021. 0xffffffff*(pVolDesc->vds_IndxStTime.LowPart-ActivationTime.LowPart)
  3022. ));
  3023. }
  3024. else
  3025. {
  3026. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  3027. ("afpActivateVolume: couldn't reference volume %Z\n",&pVolDesc->vds_Name));
  3028. }
  3029. }
  3030. else
  3031. {
  3032. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  3033. }
  3034. }
  3035. /*** AfpShouldWeIgnoreThisNotification
  3036. *
  3037. * Check to see if this notification should be ignored. The following events are
  3038. * ignored.
  3039. *
  3040. * (((Action == FILE_ACTION_MODIFIED_STREAM) &&
  3041. * (Stream != AFP_RESC_STREAM) &&
  3042. * (Stream != AFP_INFO_STREAM)) ||
  3043. * (Its one of our own changes))
  3044. *
  3045. * LOCKS: vds_VolLock (SPIN)
  3046. */
  3047. BOOLEAN FASTCALL
  3048. AfpShouldWeIgnoreThisNotification(
  3049. IN PVOL_NOTIFY pVolNotify
  3050. )
  3051. {
  3052. PFILE_NOTIFY_INFORMATION pFNInfo;
  3053. PVOLDESC pVolDesc;
  3054. UNICODE_STRING UName;
  3055. BOOLEAN ignore = False;
  3056. pFNInfo = (PFILE_NOTIFY_INFORMATION)(pVolNotify + 1);
  3057. pVolDesc = pVolNotify->vn_pVolDesc;
  3058. AfpInitUnicodeStringWithNonNullTerm(&UName,
  3059. (USHORT)pFNInfo->FileNameLength,
  3060. pFNInfo->FileName);
  3061. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  3062. ("AfpShouldWeIgnoreThisNotification: Action: %d Name: %Z\n", pFNInfo->Action, &UName));
  3063. pVolNotify->vn_StreamId = AFP_STREAM_DATA;
  3064. if (pFNInfo->Action == FILE_ACTION_MODIFIED_STREAM)
  3065. {
  3066. UNICODE_STRING UStream;
  3067. ignore = True;
  3068. UStream.Length = UStream.MaximumLength = AfpInfoStream.Length;
  3069. UStream.Buffer = (PWCHAR)((PBYTE)UName.Buffer +
  3070. UName.Length - AfpInfoStream.Length);
  3071. if (EQUAL_UNICODE_STRING(&UStream, &AfpInfoStream, False))
  3072. {
  3073. pVolNotify->vn_StreamId = AFP_STREAM_INFO;
  3074. pFNInfo->FileNameLength -= AfpInfoStream.Length;
  3075. UName.Length -= AfpInfoStream.Length;
  3076. ignore = False;
  3077. }
  3078. else
  3079. {
  3080. UStream.Length = UStream.MaximumLength = AfpResourceStream.Length;
  3081. UStream.Buffer = (PWCHAR)((PBYTE)UName.Buffer +
  3082. UName.Length - AfpResourceStream.Length);
  3083. if (EQUAL_UNICODE_STRING(&UStream, &AfpResourceStream, False))
  3084. {
  3085. pVolNotify->vn_StreamId = AFP_STREAM_RESC;
  3086. pFNInfo->FileNameLength -= AfpResourceStream.Length;
  3087. UName.Length -= AfpResourceStream.Length;
  3088. ignore = False;
  3089. }
  3090. }
  3091. }
  3092. if (!ignore)
  3093. {
  3094. PLIST_ENTRY pList, pListHead;
  3095. POUR_CHANGE pChange;
  3096. DWORD afpChangeAction;
  3097. KIRQL OldIrql;
  3098. afpChangeAction = AFP_CHANGE_ACTION(pFNInfo->Action);
  3099. ASSERT(afpChangeAction <= AFP_CHANGE_ACTION_MAX);
  3100. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  3101. // point to the head of the appropriate change action list
  3102. pListHead = &pVolDesc->vds_OurChangeList[afpChangeAction];
  3103. for (pList = pListHead->Flink;
  3104. pList != pListHead;
  3105. pList = pList->Flink)
  3106. {
  3107. pChange = CONTAINING_RECORD(pList, OUR_CHANGE, oc_Link);
  3108. // do a case *sensitive* unicode string compare
  3109. if (EQUAL_UNICODE_STRING_CS(&UName, &pChange->oc_Path))
  3110. {
  3111. RemoveEntryList(&pChange->oc_Link);
  3112. AfpFreeMemory(pChange);
  3113. // We were notified of our own change
  3114. ignore = True;
  3115. if (pFNInfo->Action == FILE_ACTION_RENAMED_OLD_NAME)
  3116. {
  3117. // consume the RENAMED_NEW_NAME if it exists
  3118. if (pFNInfo->NextEntryOffset != 0)
  3119. {
  3120. PFILE_NOTIFY_INFORMATION pFNInfo2;
  3121. (PBYTE)pFNInfo2 = (PBYTE)pFNInfo + pFNInfo->NextEntryOffset;
  3122. if (pFNInfo2->Action == FILE_ACTION_RENAMED_NEW_NAME)
  3123. {
  3124. ASSERT(pFNInfo2->NextEntryOffset == 0);
  3125. }
  3126. else
  3127. {
  3128. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  3129. ("AfpShouldWeIgnoreThisNotification: Our Rename did not come with new name!!!\n"));
  3130. }
  3131. }
  3132. else
  3133. {
  3134. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  3135. ("AfpShouldWeIgnoreThisNotification: Our Rename did not come with new name!!!\n"));
  3136. }
  3137. } // if rename
  3138. else
  3139. {
  3140. // We are ignoring this notify. Make sure its not a multiple
  3141. ASSERT(pFNInfo->NextEntryOffset == 0);
  3142. }
  3143. break;
  3144. }
  3145. } // while there are more of our changes to look thru
  3146. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  3147. }
  3148. else
  3149. {
  3150. // We are ignoring this notify. Make sure its not a multiple
  3151. ASSERT(pFNInfo ->NextEntryOffset == 0);
  3152. }
  3153. if (!ignore)
  3154. {
  3155. INTERLOCKED_INCREMENT_LONG(&pVolDesc->vds_cOutstandingNotifies);
  3156. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  3157. ("ShouldWe: Action: %d Name: \t\t\t\t\t\n%Z\n", pFNInfo->Action, &UName));
  3158. }
  3159. return ignore;
  3160. }
  3161. /*** AfpCacheDirectoryTree
  3162. *
  3163. * Scan a directory tree and build the idindex database from this information.
  3164. * Do a breadth-first search. On volume start, this will cache the tree
  3165. * beginning at the root directory. For Directory ADDED notifications, this
  3166. * will cache the subtree beginning at the added directory, since a PC user can
  3167. * potentially move an entire subtree into a mac volume, but we will only
  3168. * be notified of the one directory addition.
  3169. *
  3170. * Only the first level is actually handled here. Sub-directories are queued up
  3171. * as faked notifies and handled that way.
  3172. *
  3173. * Method:
  3174. * REENUMERATE: In case we need to reenumerate just
  3175. * this level in the tree in order to
  3176. * get rid of any dead wood that a PC
  3177. * removed by its 'other' name
  3178. *
  3179. * GETDIRSKELETON: When we want to bring in only the skeleton
  3180. * of the tree by adding directories only
  3181. *
  3182. * GETFILES: When we need to fill in the files for this
  3183. * level of the tree because a mac has accessed
  3184. * a dir for the first time.
  3185. *
  3186. * GetDirSkeletonAndFiles:
  3187. * A Combination of the above two.
  3188. * When we want to bring in both files and directories.
  3189. * This is used when adding a volume, and we want the
  3190. * files in the root directory cached in, but no others.
  3191. * Also this will be used if we are rebuilding the
  3192. * Desktop database APPLs while caching the disk tree.
  3193. * The private ChangeNotifies we queue up for ADDED dirs
  3194. * will also read in the files if the volume is marked
  3195. * for rebuilding of the desktop.
  3196. *
  3197. * GETENTIRETREE: When we want to cache in the entire tree
  3198. *
  3199. * GetEntireTreeAndReEnumerate:
  3200. * Combines GETENTIRETREE and REENUMERATE
  3201. *
  3202. * LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Exclusive)
  3203. */
  3204. NTSTATUS
  3205. AfpCacheDirectoryTree(
  3206. IN PVOLDESC pVolDesc,
  3207. IN PDFENTRY pDFETreeRoot, // DFE of the tree root directory
  3208. IN DWORD Method, // See explanation in procedure comment
  3209. IN PFILESYSHANDLE phRootDir OPTIONAL, // open handle to tree root directory
  3210. IN PUNICODE_STRING pDirPath OPTIONAL
  3211. )
  3212. {
  3213. UNICODE_STRING UName, Path, ParentPath;
  3214. UNICODE_STRING RootName;
  3215. PDFENTRY pDFE;
  3216. PDFENTRY pChainDFE;
  3217. PDFENTRY pCurrDfe;
  3218. NTSTATUS Status = STATUS_SUCCESS;
  3219. PBYTE enumbuf = NULL;
  3220. PFILE_BOTH_DIR_INFORMATION pNextEntry;
  3221. FILESYSHANDLE fshEnumDir;
  3222. USHORT SavedPathLength;
  3223. BOOLEAN fQueueThisSubDir=FALSE;
  3224. BOOLEAN fOneSubDirAlreadyQueued=FALSE;
  3225. BOOLEAN fAllSubDirsVisited=TRUE;
  3226. BOOLEAN fExitLoop=FALSE;
  3227. #ifdef PROFILING
  3228. TIME TimeS, TimeE, TimeD;
  3229. DWORD NumScanned = 0;
  3230. AfpGetPerfCounter(&TimeS);
  3231. #endif
  3232. PAGED_CODE( );
  3233. ASSERT (VALID_DFE(pDFETreeRoot));
  3234. ASSERT (DFE_IS_DIRECTORY(pDFETreeRoot));
  3235. ASSERT((Method != GETFILES) || !DFE_CHILDREN_ARE_PRESENT(pDFETreeRoot));
  3236. // allocate the buffer that will hold enumerated files and dirs
  3237. if ((pVolDesc->vds_EnumBuffer == NULL) &&
  3238. ((pVolDesc->vds_EnumBuffer = (PBYTE)AfpAllocPANonPagedMemory(AFP_ENUMBUF_SIZE)) == NULL))
  3239. {
  3240. return STATUS_NO_MEMORY;
  3241. }
  3242. do
  3243. {
  3244. fshEnumDir.fsh_FileHandle = NULL;
  3245. enumbuf = pVolDesc->vds_EnumBuffer;
  3246. // Get the volume root relative path to the directory tree being scanned
  3247. // Get extra space for one more entry to tag on for queuing notifies.
  3248. // In case we already have the path corres. to directory we are attempting
  3249. // to cache, get it from there. Note that in this case we are always
  3250. // guaranteed that extra space is available
  3251. if (ARGUMENT_PRESENT(pDirPath))
  3252. {
  3253. Path = *pDirPath;
  3254. }
  3255. else
  3256. {
  3257. AfpSetEmptyUnicodeString(&Path, 0, NULL);
  3258. Status = AfpHostPathFromDFEntry(pDFETreeRoot,
  3259. (AFP_LONGNAME_LEN+1)*sizeof(WCHAR),
  3260. &Path);
  3261. if (!NT_SUCCESS(Status))
  3262. {
  3263. break;
  3264. }
  3265. }
  3266. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3267. ("AfpCacheDirectoryTree: ParentId %d, Path %Z\n",
  3268. pDFETreeRoot->dfe_AfpId, &Path));
  3269. if (Method != GETDIRSKELETON)
  3270. {
  3271. if (!ARGUMENT_PRESENT(phRootDir))
  3272. {
  3273. // Need to open a handle to the directory in order to enumerate
  3274. if (NT_SUCCESS(Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
  3275. AFP_STREAM_DATA,
  3276. FILEIO_OPEN_DIR,
  3277. &Path,
  3278. FILEIO_ACCESS_NONE,
  3279. FILEIO_DENY_NONE,
  3280. False,
  3281. &fshEnumDir)))
  3282. {
  3283. phRootDir = &fshEnumDir;
  3284. }
  3285. else
  3286. {
  3287. break;
  3288. }
  3289. }
  3290. }
  3291. SavedPathLength = Path.Length;
  3292. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
  3293. ("AfpCacheDirectoryTree: Scanning Tree: %Z\n", &Path));
  3294. if (Method & REENUMERATE)
  3295. {
  3296. afpMarkAllChildrenUnseen(pDFETreeRoot);
  3297. }
  3298. if (Method != GETDIRSKELETON)
  3299. {
  3300. while (True)
  3301. {
  3302. // keep enumerating till we get all the entries
  3303. Status = AfpIoQueryDirectoryFile(phRootDir,
  3304. (PFILE_BOTH_DIR_INFORMATION)enumbuf,
  3305. AFP_ENUMBUF_SIZE,
  3306. FileBothDirectoryInformation,
  3307. False, // return multiple entries
  3308. False, // don't restart scan
  3309. NULL);
  3310. ASSERT(Status != STATUS_PENDING);
  3311. if (Status != STATUS_SUCCESS)
  3312. {
  3313. if ((Status == STATUS_NO_MORE_FILES) ||
  3314. (Status == STATUS_NO_SUCH_FILE))
  3315. {
  3316. Status = STATUS_SUCCESS;
  3317. break; // that's it, we've seen everything there is
  3318. }
  3319. else
  3320. {
  3321. AFPLOG_HERROR(AFPSRVMSG_ENUMERATE,
  3322. Status,
  3323. NULL,
  3324. 0,
  3325. phRootDir->fsh_FileHandle);
  3326. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  3327. ("AfpCacheDirectoryTree: dir enum failed %lx\n", Status));
  3328. break; // enumerate failed, bail out
  3329. }
  3330. }
  3331. // process the enumerated files and dirs in the current enumbuf
  3332. pNextEntry = (PFILE_BOTH_DIR_INFORMATION)enumbuf;
  3333. while (True)
  3334. {
  3335. BOOLEAN IsDir, WriteBackROAttr, FixIt;
  3336. WCHAR wc;
  3337. PFILE_BOTH_DIR_INFORMATION pCurrEntry;
  3338. fQueueThisSubDir = FALSE;
  3339. if (pNextEntry == NULL)
  3340. {
  3341. Status = STATUS_NO_MORE_ENTRIES;
  3342. break;
  3343. }
  3344. WriteBackROAttr = False;
  3345. IsDir = False;
  3346. // Move the structure to the next entry or NULL if we hit the end
  3347. pCurrEntry = pNextEntry;
  3348. (PBYTE)pNextEntry += pCurrEntry->NextEntryOffset;
  3349. if (pCurrEntry->NextEntryOffset == 0)
  3350. {
  3351. pNextEntry = NULL;
  3352. }
  3353. if (pCurrEntry->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  3354. {
  3355. // Ignore dirs if we are only getting files for this level
  3356. if (Method == GETFILES)
  3357. {
  3358. continue;
  3359. }
  3360. IsDir = True;
  3361. }
  3362. else if (Method == GETDIRSKELETON)
  3363. {
  3364. // Ignore files if we are only getting the dir skeleton
  3365. continue;
  3366. }
  3367. // If NT name > AFP_LONGNAME_LEN, use the NT shortname for
  3368. // Mac longname on NTFS, any other file system the shortname
  3369. // will be null, so ignore the file
  3370. //if (pCurrEntry->FileNameLength <= (AFP_LONGNAME_LEN*sizeof(WCHAR)))
  3371. AfpInitUnicodeStringWithNonNullTerm(&UName,
  3372. (USHORT)pCurrEntry->FileNameLength,
  3373. pCurrEntry->FileName);
  3374. if ((RtlUnicodeStringToAnsiSize(&UName)-1) <= AFP_LONGNAME_LEN)
  3375. {
  3376. AfpInitUnicodeStringWithNonNullTerm(&UName,
  3377. (USHORT)pCurrEntry->FileNameLength,
  3378. pCurrEntry->FileName);
  3379. }
  3380. else if (pCurrEntry->ShortNameLength > 0)
  3381. {
  3382. AfpInitUnicodeStringWithNonNullTerm(&UName,
  3383. (USHORT)pCurrEntry->ShortNameLength,
  3384. pCurrEntry->ShortName);
  3385. }
  3386. else
  3387. {
  3388. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  3389. ("AfpCacheDirectoryTree: Name is > 31 with no short name ?\n"));
  3390. continue;
  3391. }
  3392. if (IsDir &&
  3393. (EQUAL_UNICODE_STRING_CS(&Dot, &UName) ||
  3394. EQUAL_UNICODE_STRING_CS(&DotDot, &UName)))
  3395. {
  3396. continue;
  3397. }
  3398. // Check if this entry is an invalid win32 name i.e. it has either
  3399. // a period or a space at end, if so convert it to the new format.
  3400. // NOTE: can we construct a path to use to catch our own changes ?
  3401. wc = UName.Buffer[(UName.Length - 1)/sizeof(WCHAR)];
  3402. if ((wc == UNICODE_SPACE) || (wc == UNICODE_PERIOD))
  3403. {
  3404. // NOTE: MacCD driver should fix this??
  3405. if (IS_VOLUME_NTFS(pVolDesc))
  3406. {
  3407. afpRenameInvalidWin32Name(phRootDir, IsDir, &UName);
  3408. }
  3409. }
  3410. #ifdef PROFILING
  3411. NumScanned++;
  3412. #endif
  3413. pDFE = NULL;
  3414. FixIt = False;
  3415. if (Method & REENUMERATE)
  3416. {
  3417. // If we have this item in our DB, just mark it as seen.
  3418. // Use DFE_ANY here since a mismatch is fatal.
  3419. afpFindDFEByUnicodeNameInSiblingList(pVolDesc,
  3420. pDFETreeRoot,
  3421. &UName,
  3422. &pDFE,
  3423. DFE_ANY);
  3424. if (pDFE != NULL)
  3425. {
  3426. // If we have a wrong type, fix it.
  3427. if (IsDir ^ DFE_IS_DIRECTORY(pDFE))
  3428. {
  3429. AfpDeleteDfEntry(pVolDesc, pDFE);
  3430. pDFE = NULL;
  3431. FixIt = True;
  3432. }
  3433. else
  3434. {
  3435. DFE_MARK_AS_SEEN(pDFE);
  3436. }
  3437. }
  3438. }
  3439. if ((Method != REENUMERATE) || FixIt)
  3440. {
  3441. // add this entry to the idindex database, and cache all the required
  3442. // information, but only for files since the directories are queued
  3443. // back and added at that time.
  3444. if (!IsDir)
  3445. {
  3446. // Construct a full path to the file in order to filter our
  3447. // own changes to AFP_AfpInfo stream when adding the file
  3448. if (Path.Length > 0)
  3449. {
  3450. // Append a path separator
  3451. Path.Buffer[Path.Length / sizeof(WCHAR)] = L'\\';
  3452. Path.Length += sizeof(WCHAR);
  3453. }
  3454. ASSERT(Path.Length + UName.Length <= Path.MaximumLength);
  3455. RtlAppendUnicodeStringToString(&Path, &UName);
  3456. if (pDFE == NULL)
  3457. {
  3458. afpAddDfEntryAndCacheInfo(pVolDesc,
  3459. pDFETreeRoot,
  3460. &UName,
  3461. phRootDir,
  3462. pCurrEntry,
  3463. &Path,
  3464. &pDFE,
  3465. True);
  3466. }
  3467. else if (pCurrEntry->LastWriteTime.QuadPart > pDFE->dfe_LastModTime.QuadPart)
  3468. {
  3469. afpVerifyDFE(pVolDesc,
  3470. pDFETreeRoot,
  3471. &UName,
  3472. phRootDir,
  3473. pCurrEntry,
  3474. &Path,
  3475. &pDFE);
  3476. }
  3477. // Restore the original length of the path to enum dir
  3478. Path.Length = SavedPathLength;
  3479. if (pDFE == NULL)
  3480. {
  3481. // one reason this could fail is if we encounter pagefile.sys
  3482. // if our volume is rooted at the drive root
  3483. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
  3484. ("AfpCacheDirectoryTree: AddDfEntry failed %Z at %Z\n",&UName,&Path));
  3485. continue;
  3486. }
  3487. else if (Method == (GETENTIRETREE | REENUMERATE))
  3488. {
  3489. DFE_MARK_AS_SEEN(pDFE);
  3490. }
  3491. }
  3492. else
  3493. {
  3494. ASSERT(IsDir);
  3495. ASSERT ((Method != GETFILES) &&
  3496. (Method != REENUMERATE));
  3497. // queue this directory as a simulated Notify of a directory add.
  3498. // If we have too much stuff on the queue already, then queue
  3499. // only one subdirectory so that we are guaranteed to eventually
  3500. // visit all the subdirs. Also, on a huge volume, we want to limit
  3501. // how many directories enque per level of the tree
  3502. //
  3503. fQueueThisSubDir = TRUE;
  3504. // We dont use this flag DFE_FLAGS_INIT_COMPLETED
  3505. // anymore. So, reducing one lookup
  3506. #if 0
  3507. pCurrDfe = AfpFindEntryByUnicodeName(pVolDesc,
  3508. &UName,
  3509. AFP_LONGNAME,
  3510. pDFETreeRoot,
  3511. DFE_ANY);
  3512. //
  3513. // if this subdir is already complete, skip it
  3514. //
  3515. if ((pCurrDfe != NULL) &&
  3516. (pCurrDfe->dfe_Flags & DFE_FLAGS_INIT_COMPLETED))
  3517. {
  3518. fQueueThisSubDir = FALSE;
  3519. }
  3520. #endif
  3521. if (fQueueThisSubDir)
  3522. {
  3523. AfpQueuePrivateChangeNotify(pVolDesc,
  3524. &UName,
  3525. &Path,
  3526. pDFETreeRoot->dfe_AfpId);
  3527. }
  3528. }
  3529. }
  3530. if (fExitLoop)
  3531. {
  3532. break;
  3533. }
  3534. } // while more entries in the enumbuf
  3535. if ((!NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) ||
  3536. (fExitLoop))
  3537. {
  3538. break;
  3539. }
  3540. } // while there are more files to enumerate
  3541. if (NT_SUCCESS(Status))
  3542. {
  3543. if (Method & REENUMERATE)
  3544. {
  3545. afpPruneUnseenChildren(pVolDesc, pDFETreeRoot);
  3546. }
  3547. DFE_MARK_CHILDREN_PRESENT(pDFETreeRoot);
  3548. }
  3549. else
  3550. {
  3551. DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
  3552. ("AfpCacheDirectoryTree: Status %lx\n", Status));
  3553. }
  3554. } /* if Method != GETDIRSKELETON */
  3555. else if (Method == GETDIRSKELETON)
  3556. {
  3557. PDFENTRY pcurrDfEntry;
  3558. PDFENTRY pDfEntry;
  3559. pcurrDfEntry = (pDFETreeRoot)->dfe_pDirEntry->de_ChildDir;
  3560. do
  3561. {
  3562. for (NOTHING;
  3563. pcurrDfEntry != NULL;
  3564. pcurrDfEntry = pcurrDfEntry->dfe_NextSibling)
  3565. {
  3566. #if 0
  3567. if (((*(_ppDfEntry))->dfe_NameHash == NameHash) &&
  3568. EQUAL_UNICODE_STRING(&((*(_ppDfEntry))->dfe_UnicodeName),
  3569. _pName,
  3570. True))
  3571. {
  3572. afpUpdateDfeAccessTime(_pVolDesc, *(_ppDfEntry));
  3573. Found = True;
  3574. break;
  3575. }
  3576. #endif
  3577. AfpQueuePrivateChangeNotify(pVolDesc,
  3578. &(pcurrDfEntry->dfe_UnicodeName),
  3579. &Path,
  3580. pDFETreeRoot->dfe_AfpId);
  3581. }
  3582. Status = STATUS_SUCCESS;
  3583. } while (False);
  3584. } /* if Method == GETDIRSKELETON */
  3585. } while (False);
  3586. ASSERT (enumbuf != NULL);
  3587. if ((pVolDesc->vds_cPrivateNotifies == 0) &&
  3588. (pVolDesc->vds_cOutstandingNotifies == 0))
  3589. {
  3590. if (enumbuf != NULL)
  3591. {
  3592. AfpFreePANonPagedMemory(enumbuf, AFP_ENUMBUF_SIZE);
  3593. }
  3594. pVolDesc->vds_EnumBuffer = NULL;
  3595. }
  3596. ASSERT (Path.Buffer != NULL);
  3597. if (!ARGUMENT_PRESENT(pDirPath) && (Path.Buffer != NULL))
  3598. {
  3599. AfpFreeMemory(Path.Buffer);
  3600. }
  3601. if (fshEnumDir.fsh_FileHandle != NULL)
  3602. {
  3603. AfpIoClose(&fshEnumDir);
  3604. }
  3605. #ifdef PROFILING
  3606. AfpGetPerfCounter(&TimeE);
  3607. TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
  3608. INTERLOCKED_ADD_ULONG(&AfpServerProfile->perf_ScanTreeCount,
  3609. NumScanned,
  3610. &AfpStatisticsLock);
  3611. INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_ScanTreeTime,
  3612. TimeD,
  3613. &AfpStatisticsLock);
  3614. #endif
  3615. return Status;
  3616. }
  3617. BOOLEAN FASTCALL
  3618. AfpVolumeAbortIndexing(
  3619. IN PVOLDESC pVolDesc
  3620. )
  3621. {
  3622. KIRQL OldIrql;
  3623. PKQUEUE pNotifyQueue;
  3624. PLIST_ENTRY pNotifyList;
  3625. PLIST_ENTRY pPrivateNotifyList;
  3626. LIST_ENTRY TransitionList;
  3627. PLIST_ENTRY pList, pNext;
  3628. LARGE_INTEGER Immediate;
  3629. PVOL_NOTIFY pVolNotify;
  3630. LONG index;
  3631. DWORD DerefCount=0;
  3632. DWORD PvtNotifyCount=0;
  3633. BOOLEAN fResult=TRUE;
  3634. BOOLEAN fNewVolume=FALSE;
  3635. BOOLEAN fCancelNotify=FALSE;
  3636. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  3637. ("AbortIndexing: Aborting Index for Volume\n"));
  3638. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  3639. fNewVolume = (pVolDesc->vds_Flags & VOLUME_NEW_FIRST_PASS) ? TRUE : FALSE;
  3640. pVolDesc->vds_Flags |= VOLUME_DELETED;
  3641. // set this so we don't reset the Indexing global flag again!
  3642. pVolDesc->vds_Flags |= VOLUME_INTRANSITION;
  3643. if (pVolDesc->vds_Flags & VOLUME_NOTIFY_POSTED)
  3644. {
  3645. ASSERT(pVolDesc->vds_pIrp != NULL);
  3646. fCancelNotify = TRUE;
  3647. }
  3648. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  3649. if (fCancelNotify)
  3650. {
  3651. IoCancelIrp(pVolDesc->vds_pIrp);
  3652. }
  3653. InitializeListHead(&TransitionList);
  3654. index = pVolDesc->vds_VolId % NUM_NOTIFY_QUEUES;
  3655. pNotifyQueue = &AfpVolumeNotifyQueue[index];
  3656. pNotifyList = &AfpVolumeNotifyList[index];
  3657. pPrivateNotifyList = &AfpVirtualMemVolumeNotifyList[index];
  3658. Immediate.HighPart = Immediate.LowPart = 0;
  3659. while (1)
  3660. {
  3661. pList = KeRemoveQueue(pNotifyQueue, KernelMode, &Immediate);
  3662. //
  3663. // finished the list?
  3664. //
  3665. if ((NTSTATUS)((ULONG_PTR)pList) == STATUS_TIMEOUT)
  3666. {
  3667. break;
  3668. }
  3669. pVolNotify = CONTAINING_RECORD(pList, VOL_NOTIFY, vn_List);
  3670. //
  3671. // some other notifications? keep them on temp list for now
  3672. //
  3673. if ((pVolNotify->vn_pVolDesc != pVolDesc) ||
  3674. (pVolNotify == &AfpTerminateNotifyThread))
  3675. {
  3676. InsertTailList(&TransitionList, pList);
  3677. }
  3678. //
  3679. // notification for this volume: get rid of it
  3680. //
  3681. else
  3682. {
  3683. ASSERT(pVolNotify->vn_pVolDesc == pVolDesc);
  3684. ASSERT((pVolNotify->vn_TimeStamp == AFP_QUEUE_NOTIFY_IMMEDIATELY) ||
  3685. (!fNewVolume));
  3686. // was this a private notify?
  3687. if (((PFILE_NOTIFY_INFORMATION)(pVolNotify + 1))->Action & AFP_ACTION_PRIVATE)
  3688. {
  3689. INTERLOCKED_DECREMENT_LONG(&pVolDesc->vds_cPrivateNotifies);
  3690. }
  3691. AfpFreeMemory(pVolNotify);
  3692. AfpVolumeDereference(pVolDesc);
  3693. AfpNotifyQueueCount[index]--;
  3694. }
  3695. }
  3696. ACQUIRE_SPIN_LOCK(&AfpVolumeListLock, &OldIrql);
  3697. pList = pNotifyList->Flink;
  3698. while (pList != pNotifyList)
  3699. {
  3700. pVolNotify = CONTAINING_RECORD(pList, VOL_NOTIFY, vn_List);
  3701. pNext = pList->Flink;
  3702. if (pVolNotify->vn_pVolDesc == pVolDesc)
  3703. {
  3704. RemoveEntryList(pList);
  3705. // was this a private notify?
  3706. if (((PFILE_NOTIFY_INFORMATION)(pVolNotify + 1))->Action & AFP_ACTION_PRIVATE)
  3707. {
  3708. PvtNotifyCount++;
  3709. }
  3710. DerefCount++;
  3711. AfpFreeMemory(pVolNotify);
  3712. AfpNotifyListCount[index]--;
  3713. }
  3714. pList = pNext;
  3715. }
  3716. RELEASE_SPIN_LOCK(&AfpVolumeListLock, OldIrql);
  3717. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  3718. AfpSwmrAcquireExclusive(&AfpVolumeListSwmr);
  3719. pList = pPrivateNotifyList->Flink;
  3720. while (pList != pPrivateNotifyList)
  3721. {
  3722. pVolNotify = CONTAINING_RECORD(pList, VOL_NOTIFY, vn_List);
  3723. pNext = pList->Flink;
  3724. if (pVolNotify->vn_pVolDesc == pVolDesc)
  3725. {
  3726. RemoveEntryList(pList);
  3727. afpFreeNotify(pVolNotify);
  3728. AfpVolumeDereference(pVolDesc);
  3729. //AfpNotifyListCount[index]--;
  3730. }
  3731. pList = pNext;
  3732. }
  3733. AfpSwmrRelease(&AfpVolumeListSwmr);
  3734. if (DerefCount > 0)
  3735. {
  3736. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  3737. ASSERT(pVolDesc->vds_RefCount >= DerefCount);
  3738. ASSERT(pVolDesc->vds_cPrivateNotifies >= (LONG)PvtNotifyCount);
  3739. pVolDesc->vds_RefCount -= (DerefCount - 1);
  3740. pVolDesc->vds_cPrivateNotifies -= PvtNotifyCount;
  3741. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  3742. AfpVolumeDereference(pVolDesc);
  3743. }
  3744. //
  3745. // if there were any other notifications, put them back on the queue
  3746. //
  3747. while (!IsListEmpty(&TransitionList))
  3748. {
  3749. pList = TransitionList.Flink;
  3750. pVolNotify = CONTAINING_RECORD(pList, VOL_NOTIFY, vn_List);
  3751. ASSERT(pVolNotify->vn_pVolDesc != pVolDesc);
  3752. RemoveEntryList(pList);
  3753. AfpVolumeQueueChangeNotify(pVolNotify, pNotifyQueue);
  3754. }
  3755. return(fResult);
  3756. }
  3757. BOOLEAN FASTCALL
  3758. AfpVolumeStopIndexing(
  3759. IN PVOLDESC pVolDesc,
  3760. IN PVOL_NOTIFY pInVolNotify
  3761. )
  3762. {
  3763. PKQUEUE pNotifyQueue;
  3764. PLIST_ENTRY pNotifyList;
  3765. PLIST_ENTRY pPrivateNotifyList;
  3766. PLIST_ENTRY pList, pNext;
  3767. LARGE_INTEGER Immediate;
  3768. PVOL_NOTIFY pVolNotify;
  3769. LONG index;
  3770. BOOLEAN fResult=TRUE;
  3771. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  3772. ("StopIndexing: Stopping Index for Volume\n"));
  3773. index = pVolDesc->vds_VolId % NUM_NOTIFY_QUEUES;
  3774. pNotifyQueue = &AfpVolumeNotifyQueue[index];
  3775. pNotifyList = &AfpVolumeNotifyList[index];
  3776. pPrivateNotifyList = &AfpVirtualMemVolumeNotifyList[index];
  3777. Immediate.HighPart = Immediate.LowPart = 0;
  3778. AfpSwmrAcquireExclusive(&AfpVolumeListSwmr);
  3779. pList = pPrivateNotifyList->Flink;
  3780. while (pList != pPrivateNotifyList)
  3781. {
  3782. pVolNotify = CONTAINING_RECORD(pList, VOL_NOTIFY, vn_List);
  3783. pNext = pList->Flink;
  3784. if ((pVolNotify->vn_pVolDesc == pVolDesc) &&
  3785. (pVolNotify != pInVolNotify))
  3786. {
  3787. RemoveEntryList(pList);
  3788. afpFreeNotify(pVolNotify);
  3789. AfpVolumeDereference(pVolDesc);
  3790. //AfpNotifyListCount[index]--;
  3791. }
  3792. pList = pNext;
  3793. }
  3794. AfpSwmrRelease(&AfpVolumeListSwmr);
  3795. return(fResult);
  3796. }
  3797. /*** AfpQueuePrivateChangeNotify
  3798. *
  3799. * LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Exclusive)
  3800. */
  3801. VOID
  3802. AfpQueuePrivateChangeNotify(
  3803. IN PVOLDESC pVolDesc,
  3804. IN PUNICODE_STRING pName,
  3805. IN PUNICODE_STRING pPath,
  3806. IN DWORD ParentId
  3807. )
  3808. {
  3809. DWORD dwSize;
  3810. LONG Index;
  3811. PLIST_ENTRY pVirtualNotifyList;
  3812. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  3813. ("PvtNotify: ParentId %d, Path %Z, Name %Z\n",
  3814. ParentId, pPath, pName));
  3815. pVirtualNotifyList = &AfpVirtualMemVolumeNotifyList[pVolDesc->vds_VolId % NUM_NOTIFY_QUEUES];
  3816. // Reference the volume for Notify processing
  3817. if (AfpVolumeReference(pVolDesc))
  3818. {
  3819. PVOL_NOTIFY pVolNotify;
  3820. PFILE_NOTIFY_INFORMATION pNotifyInfo;
  3821. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  3822. ("AfpQueuePrivateChangeNotify: Queuing directory %Z\\%Z\n", pPath, pName));
  3823. // Allocate an extra component worths
  3824. dwSize = sizeof(VOL_NOTIFY) +
  3825. sizeof(FILE_NOTIFY_INFORMATION) +
  3826. pPath->Length +
  3827. pName->Length +
  3828. (AFP_LONGNAME_LEN+1)*sizeof(WCHAR)+
  3829. sizeof(WCHAR);
  3830. Index = NOTIFY_USIZE_TO_INDEX(pPath->Length+pName->Length+sizeof(WCHAR));
  3831. pVolNotify = afpAllocNotify (Index, TRUE);
  3832. if (pVolNotify != NULL)
  3833. {
  3834. LONG Offset = 0;
  3835. INTERLOCKED_INCREMENT_LONG(&pVolDesc->vds_cPrivateNotifies);
  3836. if (pVolDesc->vds_cPrivateNotifies > pVolDesc->vds_maxPrivateNotifies)
  3837. {
  3838. pVolDesc->vds_maxPrivateNotifies = pVolDesc->vds_cPrivateNotifies;
  3839. }
  3840. pVolNotify->vn_VariableLength = pPath->Length+pName->Length+sizeof(WCHAR);
  3841. pVolNotify->vn_pVolDesc = pVolDesc;
  3842. pVolNotify->vn_Processor = afpProcessPrivateNotify;
  3843. pVolNotify->vn_TimeStamp = AFP_QUEUE_NOTIFY_IMMEDIATELY;
  3844. pVolNotify->vn_ParentId = ParentId;
  3845. pVolNotify->vn_TailLength = pName->Length;
  3846. pVolNotify->vn_StreamId = AFP_STREAM_DATA;
  3847. pNotifyInfo = (PFILE_NOTIFY_INFORMATION)((PBYTE)pVolNotify + sizeof(VOL_NOTIFY));
  3848. pNotifyInfo->NextEntryOffset = 0;
  3849. pNotifyInfo->Action = FILE_ACTION_ADDED | AFP_ACTION_PRIVATE;
  3850. pNotifyInfo->FileNameLength = pName->Length + pPath->Length;
  3851. if (pPath->Length > 0)
  3852. {
  3853. RtlCopyMemory(pNotifyInfo->FileName,
  3854. pPath->Buffer,
  3855. pPath->Length);
  3856. pNotifyInfo->FileName[pPath->Length/sizeof(WCHAR)] = L'\\';
  3857. pNotifyInfo->FileNameLength += sizeof(WCHAR);
  3858. Offset = pPath->Length + sizeof(WCHAR);
  3859. }
  3860. if (pName->Length > 0)
  3861. {
  3862. RtlCopyMemory((PBYTE)pNotifyInfo->FileName + Offset,
  3863. pName->Buffer,
  3864. pName->Length);
  3865. }
  3866. AfpSwmrAcquireExclusive(&AfpVolumeListSwmr);
  3867. InsertTailList(pVirtualNotifyList, &pVolNotify->vn_List);
  3868. AfpSwmrRelease(&AfpVolumeListSwmr);
  3869. }
  3870. else
  3871. {
  3872. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  3873. ("AfpQueuePrivateChangeNotify: Queuing of notify for directory %Z\\%Z failed\n",
  3874. pPath, pName));
  3875. AFPLOG_ERROR(AFPSRVMSG_VOLUME_INIT_FAILED,
  3876. STATUS_INSUFFICIENT_RESOURCES,
  3877. NULL,
  3878. 0,
  3879. &pVolDesc->vds_Name);
  3880. //
  3881. // this will remove all the entries that have been queued so far
  3882. //
  3883. AfpVolumeAbortIndexing(pVolDesc);
  3884. // remove the refcount put above when vol referenced
  3885. AfpVolumeDereference(pVolDesc);
  3886. // remove the creation refcount
  3887. AfpVolumeDereference(pVolDesc);
  3888. }
  3889. }
  3890. else
  3891. {
  3892. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_ERR,
  3893. ("AfpQueuePrivateChangeNotify: Queuing of notify for directory %Z\\%Z failed as Reference not possible\n",
  3894. pPath, pName));
  3895. }
  3896. }
  3897. /*** AfpQueueOurChange
  3898. *
  3899. * LOCKS: vds_VolLock (SPIN)
  3900. */
  3901. VOID
  3902. AfpQueueOurChange(
  3903. IN PVOLDESC pVolDesc,
  3904. IN DWORD Action, // NT FILE_ACTION_XXX (ntioapi.h)
  3905. IN PUNICODE_STRING pPath,
  3906. IN PUNICODE_STRING pParentPath OPTIONAL // queues a ACTION_MODIFIED
  3907. )
  3908. {
  3909. POUR_CHANGE pchange = NULL;
  3910. KIRQL OldIrql;
  3911. #if DBG
  3912. static PBYTE ActionStrings[] =
  3913. { "",
  3914. "ADDED",
  3915. "REMOVED",
  3916. "MODIFIED",
  3917. "RENAMED OLD",
  3918. "RENAMED NEW",
  3919. "STREAM ADDED",
  3920. "STREAM REMOVED",
  3921. "STREAM MODIFIED"
  3922. };
  3923. #endif
  3924. PAGED_CODE( );
  3925. ASSERT(IS_VOLUME_NTFS(pVolDesc) && !EXCLUSIVE_VOLUME(pVolDesc));
  3926. //
  3927. // if the volume is being built, we don't have change-notify posted.
  3928. // Don't queue this change: we are never going to get a change-notify!
  3929. //
  3930. if (pVolDesc->vds_Flags & VOLUME_NEW_FIRST_PASS)
  3931. {
  3932. ASSERT(!(pVolDesc->vds_Flags & VOLUME_NOTIFY_POSTED));
  3933. return;
  3934. }
  3935. pchange = (POUR_CHANGE)AfpAllocNonPagedMemory(sizeof(OUR_CHANGE) + pPath->Length);
  3936. if (pchange != NULL)
  3937. {
  3938. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  3939. ("AfpQueueOurChange: Queueing a %s for %Z\n", ActionStrings[Action], pPath));
  3940. AfpGetCurrentTimeInMacFormat(&pchange->oc_Time);
  3941. AfpInitUnicodeStringWithNonNullTerm(&pchange->oc_Path,
  3942. pPath->Length,
  3943. (PWCHAR)((PBYTE)pchange + sizeof(OUR_CHANGE)));
  3944. RtlCopyMemory(pchange->oc_Path.Buffer,
  3945. pPath->Buffer,
  3946. pPath->Length);
  3947. ExInterlockedInsertTailList(&pVolDesc->vds_OurChangeList[AFP_CHANGE_ACTION(Action)],
  3948. &pchange->oc_Link,
  3949. &(pVolDesc->vds_VolLock.SpinLock));
  3950. }
  3951. if (ARGUMENT_PRESENT(pParentPath))
  3952. {
  3953. pchange = (POUR_CHANGE)AfpAllocNonPagedMemory(sizeof(OUR_CHANGE) + pParentPath->Length);
  3954. if (pchange != NULL)
  3955. {
  3956. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  3957. ("AfpQueueOurChange: Queueing (parent) %s for %Z\n",
  3958. ActionStrings[FILE_ACTION_MODIFIED], pParentPath));
  3959. AfpGetCurrentTimeInMacFormat(&pchange->oc_Time);
  3960. AfpInitUnicodeStringWithNonNullTerm(&pchange->oc_Path,
  3961. pParentPath->Length,
  3962. (PWCHAR)((PBYTE)pchange + sizeof(OUR_CHANGE)));
  3963. RtlCopyMemory(pchange->oc_Path.Buffer,
  3964. pParentPath->Buffer,
  3965. pParentPath->Length);
  3966. ExInterlockedInsertTailList(&pVolDesc->vds_OurChangeList[AFP_CHANGE_ACTION(FILE_ACTION_MODIFIED)],
  3967. &pchange->oc_Link,
  3968. &(pVolDesc->vds_VolLock.SpinLock));
  3969. }
  3970. }
  3971. }
  3972. /*** AfpDequeueOurChange
  3973. *
  3974. * LOCKS: LOCKS: vds_VolLock (SPIN)
  3975. */
  3976. VOID
  3977. AfpDequeueOurChange(
  3978. IN PVOLDESC pVolDesc,
  3979. IN DWORD Action, // NT FILE_ACTION_XXX (ntioapi.h)
  3980. IN PUNICODE_STRING pPath,
  3981. IN PUNICODE_STRING pParentPath OPTIONAL// queues a ACTION_MODIFIED
  3982. )
  3983. {
  3984. POUR_CHANGE pChange;
  3985. PLIST_ENTRY pList, pListHead;
  3986. KIRQL OldIrql;
  3987. #if DBG
  3988. static PBYTE ActionStrings[] =
  3989. { "",
  3990. "ADDED",
  3991. "REMOVED",
  3992. "MODIFIED",
  3993. "RENAMED OLD",
  3994. "RENAMED NEW",
  3995. "STREAM ADDED",
  3996. "STREAM REMOVED",
  3997. "STREAM MODIFIED"
  3998. };
  3999. #endif
  4000. ASSERT(IS_VOLUME_NTFS(pVolDesc) && !EXCLUSIVE_VOLUME(pVolDesc));
  4001. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  4002. // point to the head of the appropriate change action list
  4003. pListHead = &pVolDesc->vds_OurChangeList[AFP_CHANGE_ACTION(Action)];
  4004. for (pList = pListHead->Flink;
  4005. pList != pListHead;
  4006. pList = pList->Flink)
  4007. {
  4008. pChange = CONTAINING_RECORD(pList, OUR_CHANGE, oc_Link);
  4009. // do a case *sensitive* unicode string compare
  4010. if (EQUAL_UNICODE_STRING_CS(pPath, &pChange->oc_Path))
  4011. {
  4012. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  4013. ("AfpDequeueOurChange: Dequeueing a %s for %Z\n",
  4014. ActionStrings[Action], pPath));
  4015. RemoveEntryList(&pChange->oc_Link);
  4016. AfpFreeMemory(pChange);
  4017. break;
  4018. }
  4019. }
  4020. if (ARGUMENT_PRESENT(pParentPath))
  4021. {
  4022. // point to the head of the appropriate change action list
  4023. pListHead = &pVolDesc->vds_OurChangeList[FILE_ACTION_MODIFIED];
  4024. for (pList = pListHead->Flink;
  4025. pList != pListHead;
  4026. pList = pList->Flink)
  4027. {
  4028. pChange = CONTAINING_RECORD(pList, OUR_CHANGE, oc_Link);
  4029. // do a case *sensitive* unicode string compare
  4030. if (EQUAL_UNICODE_STRING_CS(pParentPath, &pChange->oc_Path))
  4031. {
  4032. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  4033. ("AfpDequeueOurChange: Dequeueing (parent) %s for %Z\n",
  4034. ActionStrings[FILE_ACTION_MODIFIED], pParentPath));
  4035. RemoveEntryList(&pChange->oc_Link);
  4036. AfpFreeMemory(pChange);
  4037. break;
  4038. }
  4039. }
  4040. }
  4041. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  4042. }
  4043. /*** AfpOurChangeScavenger
  4044. *
  4045. * This runs in a worker thread context since it takes an swmr.
  4046. *
  4047. * LOCKS: vds_VolLock (SPIN)
  4048. */
  4049. AFPSTATUS FASTCALL
  4050. AfpOurChangeScavenger(
  4051. IN PVOLDESC pVolDesc
  4052. )
  4053. {
  4054. AFPTIME Now;
  4055. KIRQL OldIrql;
  4056. int i;
  4057. BOOLEAN DerefVol = False;
  4058. #if DBG
  4059. static PBYTE Action[] = { "",
  4060. "ADDED",
  4061. "REMOVED",
  4062. "MODIFIED",
  4063. "RENAMED OLD",
  4064. "RENAMED NEW",
  4065. "STREAM ADDED",
  4066. "STREAM REMOVED",
  4067. "STREAM MODIFIED"};
  4068. #endif
  4069. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  4070. ("AfpOurChangeScavenger: OurChange scavenger for volume %Z entered...\n",
  4071. &pVolDesc->vds_Name));
  4072. // If this volume is going away, do not requeue this scavenger routine
  4073. // We don't take the volume lock to check these flags since they are
  4074. // one-way, i.e. once set they are never cleared.
  4075. if (pVolDesc->vds_Flags & (VOLUME_DELETED | VOLUME_STOPPED))
  4076. {
  4077. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  4078. ("AfpOurChangeScavenger: OurChange scavenger for volume %Z: Final run\n",
  4079. &pVolDesc->vds_Name));
  4080. DerefVol = True;
  4081. }
  4082. CleanTurds:
  4083. AfpGetCurrentTimeInMacFormat(&Now);
  4084. ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
  4085. for (i = 0; i < NUM_AFP_CHANGE_ACTION_LISTS; i++)
  4086. {
  4087. PLIST_ENTRY pList, pHead;
  4088. POUR_CHANGE pChange;
  4089. pHead = &pVolDesc->vds_OurChangeList[i];
  4090. while (!IsListEmpty(pHead))
  4091. {
  4092. pList = pHead->Flink;
  4093. pChange = CONTAINING_RECORD(pList, OUR_CHANGE, oc_Link);
  4094. if (((Now - pChange->oc_Time) > OURCHANGE_AGE) || DerefVol)
  4095. {
  4096. RemoveHeadList(pHead);
  4097. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_WARN,
  4098. ("AfpOurChangeScavenger: freeing %Z (%s)\n",
  4099. &pChange->oc_Path, &Action[i]));
  4100. AfpFreeMemory(pChange);
  4101. }
  4102. else
  4103. {
  4104. // All subsequent items in list will have later times so
  4105. // don't bother checking them
  4106. break;
  4107. }
  4108. }
  4109. }
  4110. RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
  4111. // Check again if this volume is going away and if so do not requeue this
  4112. // scavenger routine. Note that while we were running, the volume may
  4113. // have been deleted but this scavenger event could not be killed because
  4114. // it wasn't found on the list. We don't want to requeue this routine again
  4115. // because it will take AFP_OURCHANGE_AGE minutes for the volume to go
  4116. // away otherwise. This closes the window more, but does not totally
  4117. // eliminate it from happening.
  4118. if (!DerefVol)
  4119. {
  4120. if (pVolDesc->vds_Flags & (VOLUME_DELETED | VOLUME_STOPPED))
  4121. {
  4122. DBGPRINT(DBG_COMP_CHGNOTIFY, DBG_LEVEL_INFO,
  4123. ("AfpOurChangeScavenger: OurChanges scavenger for volume %Z: Final Run\n",
  4124. &pVolDesc->vds_Name));
  4125. DerefVol = True;
  4126. goto CleanTurds;
  4127. }
  4128. }
  4129. else
  4130. {
  4131. AfpVolumeDereference(pVolDesc);
  4132. return AFP_ERR_NONE;
  4133. }
  4134. return AFP_ERR_REQUEUE;
  4135. }
  4136. 
  4137.