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

1282 lines
36 KiB

  1. //
  2. // Copyright (C) 2000, Microsoft Corporation
  3. //
  4. // File: DfsReparseSupport.cxx
  5. //
  6. // Contents: This handles all reparse point work.
  7. //
  8. //
  9. // History: April 2002, Author: SupW
  10. //
  11. //-----------------------------------------------------------------------------
  12. #include <stdlib.h>
  13. #include <time.h>
  14. #include <nt.h>
  15. #include <ntrtl.h>
  16. #include <nturtl.h>
  17. #include <ntstatus.h>
  18. #include <ntioapi.h>
  19. #include <windows.h>
  20. #include <shlwapi.h>
  21. #include <strsafe.h>
  22. #include <dfsgeneric.hxx>
  23. #include <dfsheader.h>
  24. #include <DfsInit.hxx>
  25. #include <DfsRootFolder.hxx>
  26. #include <DfsReparse.hxx>
  27. #include "DfsReparseSupport.tmh"
  28. // Local support routines
  29. DFSSTATUS
  30. DfsGetVolumePathName(
  31. IN PUNICODE_STRING pDirectoryName,
  32. OUT PUNICODE_STRING ppVolumePath);
  33. VOID
  34. DfsFreeVolumePathName(
  35. PUNICODE_STRING VolumeName);
  36. DFSSTATUS
  37. DfsInsertInReparseVolList(
  38. LPWSTR VolumeName);
  39. DFSSTATUS
  40. DfsOpenReparseIndex(
  41. IN PUNICODE_STRING pVolume,
  42. OUT HANDLE *pHandle);
  43. DFSSTATUS
  44. DfsGetNextReparseRecord(
  45. HANDLE hIndex,
  46. PFILE_REPARSE_POINT_INFORMATION pReparseInfo,
  47. PBOOLEAN pDone);
  48. DFSSTATUS
  49. DfsRemoveReparseIfOrphaned(
  50. IN HANDLE VolumeHandle,
  51. IN PUNICODE_STRING pVolumeName,
  52. IN LONGLONG FileReference,
  53. IN FILETIME ServiceStartupTime);
  54. DFSSTATUS
  55. DfsDeleteReparseDirectory(
  56. PUNICODE_STRING pVolumeName,
  57. LPWSTR pDfsDirectory);
  58. DFSSTATUS
  59. DfsIsReparseOrphaned(
  60. IN HANDLE Handle,
  61. IN FILETIME ServiceStartupTime,
  62. OUT PBOOLEAN pOrphaned);
  63. DFSSTATUS
  64. DfsOpenReparseByID(
  65. IN HANDLE VolumeHandle,
  66. IN LONGLONG FileReference,
  67. OUT PHANDLE pReparseHandle);
  68. DFSSTATUS
  69. DfsGetVolumeHandleByName(
  70. IN PUNICODE_STRING pVolume,
  71. OUT PHANDLE pVolumeHandle);
  72. VOID
  73. DfsGetReparseVolumeToScan(
  74. PDFS_REPARSE_VOLUME_INFO *ppVolumeInfo );
  75. NTSTATUS
  76. DfsIsDirectoryReparsePoint(
  77. HANDLE DirHandle,
  78. PBOOLEAN pReparsePoint,
  79. PBOOLEAN pDfsReparsePoint );
  80. NTSTATUS
  81. DfsClearDfsReparsePoint(
  82. IN HANDLE DirHandle );
  83. NTSTATUS
  84. DfsDeleteLinkDirectories(
  85. PUNICODE_STRING pLinkName,
  86. HANDLE RelativeHandle,
  87. BOOLEAN bRemoveParentDirs);
  88. BOOLEAN
  89. DfsIsEmptyDirectory(
  90. HANDLE DirectoryHandle,
  91. PVOID pDirectoryBuffer,
  92. ULONG DirectoryBufferSize );
  93. NTSTATUS
  94. DfsOpenDirectory(
  95. PUNICODE_STRING pDirectoryName,
  96. ULONG ShareMode,
  97. HANDLE RelativeHandle,
  98. PHANDLE pOpenedHandle,
  99. PBOOLEAN pIsNewlyCreated )
  100. {
  101. NTSTATUS NtStatus;
  102. OBJECT_ATTRIBUTES ObjectAttributes;
  103. ACCESS_MASK DesiredAccess;
  104. PLARGE_INTEGER AllocationSize;
  105. ULONG FileAttributes;
  106. ULONG CreateDisposition;
  107. ULONG CreateOptions;
  108. IO_STATUS_BLOCK IoStatusBlock;
  109. AllocationSize = NULL;
  110. FileAttributes = FILE_ATTRIBUTE_NORMAL;
  111. CreateDisposition = FILE_OPEN_IF;
  112. CreateOptions = FILE_DIRECTORY_FILE |
  113. FILE_OPEN_REPARSE_POINT |
  114. FILE_SYNCHRONOUS_IO_NONALERT |
  115. FILE_OPEN_FOR_BACKUP_INTENT;
  116. DesiredAccess = FILE_READ_DATA |
  117. FILE_WRITE_DATA |
  118. FILE_READ_ATTRIBUTES |
  119. FILE_WRITE_ATTRIBUTES |
  120. SYNCHRONIZE;
  121. InitializeObjectAttributes (
  122. &ObjectAttributes,
  123. pDirectoryName, //Object Name
  124. OBJ_CASE_INSENSITIVE, //Attributes
  125. RelativeHandle, //Root handle
  126. NULL); //Security descriptor.
  127. NtStatus = NtCreateFile(pOpenedHandle,
  128. DesiredAccess,
  129. &ObjectAttributes,
  130. &IoStatusBlock,
  131. AllocationSize,
  132. FileAttributes,
  133. ShareMode,
  134. CreateDisposition,
  135. CreateOptions,
  136. NULL, // EaBuffer
  137. 0 ); // EaLength
  138. DFSLOG("Open on %wZ: Status %x\n", pDirectoryName, NtStatus);
  139. if ( (NtStatus == STATUS_SUCCESS) && (pIsNewlyCreated != NULL) )
  140. {
  141. *pIsNewlyCreated = (IoStatusBlock.Information == FILE_CREATED)? TRUE : FALSE;
  142. }
  143. return NtStatus;
  144. }
  145. VOID
  146. DfsCloseDirectory(
  147. HANDLE DirHandle )
  148. {
  149. NtClose( DirHandle );
  150. }
  151. //+-------------------------------------------------------------------------
  152. //
  153. // Function: ClearDfsReparsePoint
  154. //
  155. // Arguments: DirHandle - handle on open directory
  156. //
  157. // Returns: SUCCESS or error
  158. //
  159. // Description: This routine takes a handle to an open directory and
  160. // makes that directory a reparse point with the DFS tag
  161. //
  162. //--------------------------------------------------------------------------
  163. NTSTATUS
  164. DfsClearDfsReparsePoint(
  165. IN HANDLE DirHandle )
  166. {
  167. NTSTATUS NtStatus;
  168. REPARSE_DATA_BUFFER ReparseDataBuffer;
  169. IO_STATUS_BLOCK IoStatusBlock;
  170. //
  171. // Attempt to set a reparse point on the directory
  172. //
  173. RtlZeroMemory( &ReparseDataBuffer, sizeof(ReparseDataBuffer) );
  174. ReparseDataBuffer.ReparseTag = IO_REPARSE_TAG_DFS;
  175. ReparseDataBuffer.ReparseDataLength = 0;
  176. NtStatus = NtFsControlFile( DirHandle,
  177. NULL,
  178. NULL,
  179. NULL,
  180. &IoStatusBlock,
  181. FSCTL_DELETE_REPARSE_POINT,
  182. &ReparseDataBuffer,
  183. REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataBuffer.ReparseDataLength,
  184. NULL,
  185. 0 );
  186. return NtStatus;
  187. }
  188. NTSTATUS
  189. DfsDeleteLinkDirectories(
  190. PUNICODE_STRING pLinkName,
  191. HANDLE RelativeHandle,
  192. BOOLEAN bRemoveParentDirs)
  193. {
  194. UNICODE_STRING DirectoryToDelete = *pLinkName;
  195. NTSTATUS NtStatus = STATUS_SUCCESS;
  196. OBJECT_ATTRIBUTES ObjectAttributes;
  197. HANDLE CurrentDirectory = NULL;
  198. ULONG ShareMode = 0;
  199. ShareMode = FILE_SHARE_READ;
  200. //
  201. // dfsdev: fix this fixed size limit. it will hurt us in the future.
  202. //
  203. ULONG DirectoryBufferSize = 4096;
  204. PBYTE pDirectoryBuffer = new BYTE [DirectoryBufferSize];
  205. if (pDirectoryBuffer == NULL)
  206. {
  207. NtStatus = STATUS_INSUFFICIENT_RESOURCES;
  208. }
  209. while ( (NtStatus == STATUS_SUCCESS) && (DirectoryToDelete.Length != 0) )
  210. {
  211. NtStatus = DfsOpenDirectory( &DirectoryToDelete,
  212. ShareMode,
  213. RelativeHandle,
  214. &CurrentDirectory,
  215. NULL );
  216. if (NtStatus == ERROR_SUCCESS)
  217. {
  218. if (DfsIsEmptyDirectory(CurrentDirectory,
  219. pDirectoryBuffer,
  220. DirectoryBufferSize) == FALSE)
  221. {
  222. NtClose( CurrentDirectory );
  223. break;
  224. }
  225. NtClose( CurrentDirectory );
  226. InitializeObjectAttributes (
  227. &ObjectAttributes,
  228. &DirectoryToDelete,
  229. OBJ_CASE_INSENSITIVE,
  230. RelativeHandle,
  231. NULL);
  232. NtStatus = NtDeleteFile( &ObjectAttributes );
  233. //
  234. // When the worker thread is trying to clean up orphaned
  235. // reparse points, don't try to iterate and remove all parent
  236. // dirs all the way to the root. All we want to do is to
  237. // remove the reparse dir.
  238. // BUG 701594.
  239. //
  240. if (!bRemoveParentDirs)
  241. {
  242. break;
  243. }
  244. StripLastPathComponent( &DirectoryToDelete );
  245. }
  246. }
  247. if (pDirectoryBuffer != NULL)
  248. {
  249. delete [] pDirectoryBuffer;
  250. }
  251. return NtStatus;
  252. }
  253. BOOLEAN
  254. DfsIsEmptyDirectory(
  255. HANDLE DirectoryHandle,
  256. PVOID pDirectoryBuffer,
  257. ULONG DirectoryBufferSize )
  258. {
  259. NTSTATUS NtStatus;
  260. FILE_NAMES_INFORMATION *pFileInfo;
  261. ULONG NumberOfFiles = 1;
  262. BOOLEAN ReturnValue = FALSE;
  263. IO_STATUS_BLOCK IoStatus;
  264. NtStatus = NtQueryDirectoryFile ( DirectoryHandle,
  265. NULL, // no event
  266. NULL, // no apc routine
  267. NULL, // no apc context
  268. &IoStatus,
  269. pDirectoryBuffer,
  270. DirectoryBufferSize,
  271. FileNamesInformation,
  272. FALSE, // return single entry = false
  273. NULL, // filename
  274. FALSE ); // restart scan = false
  275. if (NtStatus == ERROR_SUCCESS)
  276. {
  277. pFileInfo = (FILE_NAMES_INFORMATION *)pDirectoryBuffer;
  278. while (pFileInfo->NextEntryOffset) {
  279. NumberOfFiles++;
  280. if (NumberOfFiles > 3)
  281. {
  282. break;
  283. }
  284. pFileInfo = (FILE_NAMES_INFORMATION *)((ULONG_PTR)(pFileInfo) +
  285. pFileInfo->NextEntryOffset);
  286. }
  287. if (NumberOfFiles <= 2)
  288. {
  289. ReturnValue = TRUE;
  290. }
  291. }
  292. return ReturnValue;
  293. }
  294. //+-------------------------------------------------------------------------
  295. //
  296. // Function: IsDirectoryReparsePoint
  297. //
  298. // Arguments: DirHandle - handle to open directory.
  299. // pReparsePoint - returned boolean: true if this directory is
  300. // a reparse point
  301. // pDfsReparsePoint - returned boolean: true if this
  302. // directory is a dfs reparse point
  303. //
  304. //
  305. // Returns: SUCCESS or error
  306. //
  307. // Description: This routine takes a handle to an open directory and
  308. // sets 2 booleans to indicate if this directory is a
  309. // reparse point, and if so, if this directory is a dfs
  310. // reparse point. The booleans are initialized if this
  311. // function returns success.
  312. //
  313. //--------------------------------------------------------------------------
  314. NTSTATUS
  315. DfsIsDirectoryReparsePoint(
  316. IN HANDLE DirHandle,
  317. OUT PBOOLEAN pReparsePoint,
  318. OUT PBOOLEAN pDfsReparsePoint )
  319. {
  320. NTSTATUS NtStatus;
  321. FILE_BASIC_INFORMATION BasicInfo;
  322. IO_STATUS_BLOCK IoStatusBlock;
  323. //
  324. //we assume these are not reparse points.
  325. //
  326. *pReparsePoint = FALSE;
  327. *pDfsReparsePoint = FALSE;
  328. //
  329. // Query for the basic information, which has the attributes.
  330. //
  331. NtStatus = NtQueryInformationFile( DirHandle,
  332. &IoStatusBlock,
  333. (PVOID)&BasicInfo,
  334. sizeof(BasicInfo),
  335. FileBasicInformation );
  336. if (NtStatus == STATUS_SUCCESS)
  337. {
  338. //
  339. // If the attributes indicate reparse point, we have a reparse
  340. // point directory on our hands.
  341. //
  342. if ( BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT )
  343. {
  344. FILE_ATTRIBUTE_TAG_INFORMATION FileTagInformation;
  345. *pReparsePoint = TRUE;
  346. NtStatus = NtQueryInformationFile( DirHandle,
  347. &IoStatusBlock,
  348. (PVOID)&FileTagInformation,
  349. sizeof(FileTagInformation),
  350. FileAttributeTagInformation );
  351. if (NtStatus == STATUS_SUCCESS)
  352. {
  353. //
  354. // Checkif the tag indicates its a DFS reparse point,
  355. // and setup the return accordingly.
  356. //
  357. if (FileTagInformation.ReparseTag == IO_REPARSE_TAG_DFS)
  358. {
  359. *pDfsReparsePoint = TRUE;
  360. }
  361. }
  362. }
  363. }
  364. return NtStatus;
  365. }
  366. NTSTATUS
  367. DfsDeleteLinkReparsePoint(
  368. PUNICODE_STRING pDirectoryName,
  369. HANDLE ParentHandle,
  370. BOOLEAN bRemoveParentDirs)
  371. {
  372. NTSTATUS NtStatus;
  373. HANDLE LinkDirectoryHandle;
  374. BOOLEAN IsReparsePoint, IsDfsReparsePoint;
  375. NtStatus = DfsOpenDirectory( pDirectoryName,
  376. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  377. ParentHandle,
  378. &LinkDirectoryHandle,
  379. NULL );
  380. if (NtStatus == STATUS_SUCCESS)
  381. {
  382. NtStatus = DfsIsDirectoryReparsePoint( LinkDirectoryHandle,
  383. &IsReparsePoint,
  384. &IsDfsReparsePoint );
  385. if ((NtStatus == STATUS_SUCCESS) &&
  386. (IsDfsReparsePoint == TRUE) )
  387. {
  388. NtStatus = DfsClearDfsReparsePoint( LinkDirectoryHandle );
  389. DFS_TRACE_NORM( REFERRAL_SERVER, "ClearDfsReparsePoint: %wZ, NtStatus 0x%x\n",
  390. pDirectoryName, NtStatus );
  391. }
  392. NtClose( LinkDirectoryHandle );
  393. }
  394. if (NtStatus == STATUS_SUCCESS)
  395. {
  396. NtStatus = DfsDeleteLinkDirectories( pDirectoryName,
  397. ParentHandle,
  398. bRemoveParentDirs );
  399. DFS_TRACE_NORM( REFERRAL_SERVER, "DfsDeleteLinkDirectories: %wZ, NtStatus 0x%x\n",
  400. pDirectoryName, NtStatus );
  401. }
  402. return NtStatus;
  403. }
  404. //
  405. // Given a directory name, return the volume it is on.
  406. //
  407. DFSSTATUS
  408. DfsGetVolumePathName(
  409. IN PUNICODE_STRING pDirectoryName,
  410. OUT PUNICODE_STRING pVolumePath)
  411. {
  412. DWORD BufferSize = MAX_PATH;
  413. PWSTR pName = NULL;
  414. BOOL bResult = FALSE;
  415. DFSSTATUS Status = ERROR_SUCCESS;
  416. do
  417. {
  418. // Buffersize is just a rough guess. We adjust it later.
  419. pName = new WCHAR[BufferSize];
  420. if(pName == NULL)
  421. {
  422. Status = ERROR_NOT_ENOUGH_MEMORY;
  423. break;
  424. }
  425. // Now get the volume path
  426. bResult = GetVolumePathName(
  427. pDirectoryName->Buffer,
  428. pName,
  429. BufferSize);
  430. // If we failed, see if it's because we needed a longer buffer.
  431. if (!bResult)
  432. {
  433. delete [] pName;
  434. pName = NULL;
  435. Status = GetLastError();
  436. //
  437. // We assume a well behaved GetVolumePathName
  438. // that returns OVERFLOW finite number of times.
  439. //
  440. if (Status == ERROR_BUFFER_OVERFLOW)
  441. {
  442. BufferSize *= 2;
  443. }
  444. else
  445. {
  446. break;
  447. }
  448. }
  449. } while (!bResult);
  450. Status = DfsRtlInitUnicodeStringEx( pVolumePath, pName );
  451. if (Status != ERROR_SUCCESS && pName != NULL)
  452. {
  453. delete [] pName;
  454. pName = NULL;
  455. }
  456. return Status;
  457. }
  458. VOID
  459. DfsFreeVolumePathName(
  460. PUNICODE_STRING pVolumeName)
  461. {
  462. if (pVolumeName != NULL)
  463. {
  464. delete [] pVolumeName->Buffer;
  465. pVolumeName->Buffer = NULL;
  466. }
  467. }
  468. DFSSTATUS
  469. DfsInsertInReparseVolList(
  470. LPWSTR VolumeName)
  471. {
  472. DFSSTATUS Status = ERROR_SUCCESS;
  473. PDFS_REPARSE_VOLUME_INFO pNewReparseEntry = NULL;
  474. pNewReparseEntry = new DFS_REPARSE_VOLUME_INFO;
  475. if (pNewReparseEntry != NULL)
  476. {
  477. //
  478. // We create a new null terminated string to keep. The input may be unnecessarily longer.
  479. //
  480. Status = DfsCreateUnicodeStringFromString( &pNewReparseEntry->VolumeName, VolumeName );
  481. if (Status == ERROR_SUCCESS)
  482. {
  483. //
  484. // Add to the global reparse volume list. The caller knows that the entry isn't there,
  485. // and has held the datalock through out.
  486. //
  487. InsertTailList( &DfsServerGlobalData.ReparseVolumeList, &pNewReparseEntry->ListEntry);
  488. DFS_TRACE_NORM( REFERRAL_SERVER, "[%!FUNC! Added %ws to ReparseVolumeList\n",
  489. VolumeName );
  490. }
  491. }
  492. else
  493. {
  494. Status = ERROR_NOT_ENOUGH_MEMORY;
  495. }
  496. // Error path
  497. if (Status != ERROR_SUCCESS)
  498. {
  499. if (pNewReparseEntry != NULL)
  500. {
  501. delete pNewReparseEntry;
  502. pNewReparseEntry = NULL;
  503. }
  504. }
  505. DFS_TRACE_ERROR_HIGH( Status, REFERRAL_SERVER,
  506. "[%!FUNC!- Level %!LEVEL!] OUT OF RESOURCES adding %ws to ReparseVolumeList\n",
  507. VolumeName );
  508. return Status;
  509. }
  510. DFSSTATUS
  511. DfsGetNextReparseRecord(
  512. HANDLE hIndex,
  513. PFILE_REPARSE_POINT_INFORMATION pReparseInfo,
  514. PBOOLEAN pDone)
  515. {
  516. BOOLEAN bResult = FALSE;
  517. DFSSTATUS Status = ERROR_SUCCESS;
  518. IO_STATUS_BLOCK IoStatus;
  519. NTSTATUS NtStatus = STATUS_SUCCESS;
  520. NtStatus = NtQueryDirectoryFile(hIndex,
  521. NULL,
  522. NULL,
  523. NULL,
  524. &IoStatus,
  525. pReparseInfo,
  526. sizeof(FILE_REPARSE_POINT_INFORMATION),
  527. FileReparsePointInformation,
  528. TRUE,
  529. NULL,
  530. FALSE);
  531. if (!NT_SUCCESS(NtStatus))
  532. {
  533. Status = RtlNtStatusToDosError(NtStatus);
  534. if (Status == ERROR_NO_MORE_FILES)
  535. {
  536. Status = ERROR_SUCCESS;
  537. }
  538. *pDone = TRUE;
  539. }
  540. return Status;
  541. }
  542. DFSSTATUS
  543. DfsOpenReparseIndex(
  544. IN PUNICODE_STRING pVolume,
  545. OUT HANDLE *pHandle)
  546. {
  547. HANDLE ReparseHandle = INVALID_HANDLE_VALUE;
  548. LPWSTR pReparsePathName = NULL;
  549. DFSSTATUS Status = ERROR_SUCCESS;
  550. size_t CchPathLen;
  551. HRESULT Hr = S_OK;
  552. LPWSTR pIndexAllocPath = REPARSE_INDEX_PATH;
  553. *pHandle = INVALID_HANDLE_VALUE;
  554. do {
  555. CchPathLen = pVolume->Length;
  556. //
  557. // Extra space is to concat "\$Extend\\$Reparse:$R:$INDEX_ALLOCATION"
  558. //
  559. CchPathLen += REPARSE_INDEX_PATH_LEN;
  560. pReparsePathName = new WCHAR[ CchPathLen ];
  561. if (pReparsePathName == NULL)
  562. {
  563. Status = ERROR_NOT_ENOUGH_MEMORY;
  564. break;
  565. }
  566. // The volume name.
  567. Hr = StringCchCopy( pReparsePathName, CchPathLen, pVolume->Buffer );
  568. if (!SUCCEEDED(Hr))
  569. {
  570. Status = HRESULT_CODE(Hr);
  571. break;
  572. }
  573. (VOID)PathAddBackslash( pReparsePathName );
  574. //
  575. // $Extend\\$Reparse:$R:$INDEX_ALLOCATION
  576. //
  577. Hr = StringCchCat( pReparsePathName, CchPathLen, pIndexAllocPath );
  578. if (!SUCCEEDED(Hr))
  579. {
  580. Status = HRESULT_CODE(Hr);
  581. break;
  582. }
  583. ReparseHandle = CreateFile(
  584. pReparsePathName,
  585. GENERIC_READ,
  586. FILE_SHARE_READ,
  587. NULL,
  588. OPEN_EXISTING,
  589. FILE_FLAG_BACKUP_SEMANTICS | SECURITY_IMPERSONATION,
  590. NULL);
  591. Status = GetLastError();
  592. // paranoia
  593. if (Status == ERROR_SUCCESS)
  594. {
  595. *pHandle = ReparseHandle;
  596. }
  597. } while (FALSE);
  598. //
  599. // Clean up
  600. //
  601. if (pReparsePathName != NULL)
  602. {
  603. delete [] pReparsePathName;
  604. pReparsePathName = NULL;
  605. }
  606. return Status;
  607. }
  608. //
  609. // Given the volume name, return a handle to it.
  610. //
  611. DFSSTATUS
  612. DfsGetVolumeHandleByName(
  613. IN PUNICODE_STRING pVolume,
  614. OUT PHANDLE pVolumeHandle)
  615. {
  616. LPWSTR VolumeName = NULL;
  617. DFSSTATUS Status = ERROR_SUCCESS;
  618. BOOL bResult = FALSE;
  619. *pVolumeHandle = INVALID_HANDLE_VALUE;
  620. VolumeName = new WCHAR[ MAX_PATH ];
  621. if (VolumeName == NULL)
  622. {
  623. Status = ERROR_NOT_ENOUGH_MEMORY;
  624. return Status;
  625. }
  626. bResult = GetVolumeNameForVolumeMountPoint(
  627. pVolume->Buffer,
  628. VolumeName,
  629. MAX_PATH);
  630. if (bResult)
  631. {
  632. *pVolumeHandle = CreateFile(
  633. VolumeName,
  634. GENERIC_READ,
  635. FILE_SHARE_READ,
  636. NULL,
  637. OPEN_EXISTING,
  638. FILE_FLAG_BACKUP_SEMANTICS | SECURITY_IMPERSONATION,
  639. NULL);
  640. }
  641. Status = GetLastError();
  642. delete [] VolumeName;
  643. return Status;
  644. }
  645. DFSSTATUS
  646. DfsOpenReparseByID(
  647. IN HANDLE VolumeHandle,
  648. IN LONGLONG FileReference,
  649. OUT PHANDLE pReparseHandle)
  650. {
  651. DFSSTATUS Status = ERROR_SUCCESS;
  652. NTSTATUS NtStatus = STATUS_SUCCESS;
  653. UNICODE_STRING FileIdString;
  654. OBJECT_ATTRIBUTES ObjectAttributes;
  655. IO_STATUS_BLOCK IoStatusBlock;
  656. *pReparseHandle = INVALID_HANDLE_VALUE;
  657. //
  658. // Open the file by its file reference.
  659. //
  660. FileIdString.Length = sizeof(LONGLONG);
  661. FileIdString.MaximumLength = sizeof(LONGLONG);
  662. FileIdString.Buffer = (PWCHAR)&FileReference;
  663. InitializeObjectAttributes(
  664. &ObjectAttributes,
  665. &FileIdString,
  666. OBJ_CASE_INSENSITIVE,
  667. VolumeHandle,
  668. NULL); // security descriptor
  669. NtStatus = NtCreateFile(
  670. pReparseHandle,
  671. FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  672. &ObjectAttributes,
  673. &IoStatusBlock,
  674. NULL, // allocation size
  675. FILE_ATTRIBUTE_NORMAL,
  676. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  677. FILE_OPEN,
  678. FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT | FILE_OPEN_BY_FILE_ID,
  679. NULL, // EA buffer
  680. 0); // EA length
  681. Status = RtlNtStatusToDosError( NtStatus );
  682. return Status;
  683. }
  684. DFSSTATUS
  685. DfsIsReparseOrphaned(
  686. IN HANDLE Handle,
  687. IN FILETIME ServiceStartupTime,
  688. OUT PBOOLEAN pOrphaned)
  689. {
  690. DFSSTATUS Status = ERROR_SUCCESS;
  691. NTSTATUS NtStatus = STATUS_SUCCESS;
  692. IO_STATUS_BLOCK IoStatusBlock;
  693. FILE_BASIC_INFORMATION FileBasicInfo;
  694. FILETIME ReparseTimeStamp;
  695. *pOrphaned = FALSE;
  696. //
  697. // Query the LastWriteTime to see if this reparse point is orphaned.
  698. //
  699. ZeroMemory( &FileBasicInfo, sizeof( FileBasicInfo ));
  700. NtStatus = NtQueryInformationFile(
  701. Handle,
  702. &IoStatusBlock,
  703. &FileBasicInfo,
  704. sizeof(FileBasicInfo),
  705. FileBasicInformation);
  706. Status = RtlNtStatusToDosError( NtStatus );
  707. if (Status == ERROR_SUCCESS)
  708. {
  709. //
  710. // Since we would've re-written all reparse points
  711. // when the service had started up, a good reparse point
  712. // can't have an older timestamp.
  713. //
  714. LARGE_INTEGER_TO_FILETIME( &ReparseTimeStamp, &FileBasicInfo.ChangeTime );
  715. if (CompareFileTime( &ReparseTimeStamp, &ServiceStartupTime ) == -1)
  716. {
  717. *pOrphaned = TRUE;
  718. }
  719. }
  720. return Status;
  721. }
  722. DFSSTATUS
  723. DfsDeleteReparseDirectory(
  724. PUNICODE_STRING pVolumeName,
  725. LPWSTR pDfsDirectory,
  726. ULONG CbDirLength)
  727. {
  728. DFSSTATUS Status = ERROR_SUCCESS;
  729. NTSTATUS NtStatus;
  730. DWORD BuffLen = 0;
  731. LPWSTR FullFileName = NULL;
  732. UNICODE_STRING UnicodeFileName;
  733. ULONG CbCurrentPos;
  734. BuffLen = WHACKWHACKQQ_SIZE;
  735. BuffLen += (pVolumeName->Length);
  736. BuffLen += CbDirLength;
  737. BuffLen += sizeof(UNICODE_NULL);
  738. //
  739. // Unicodes can't handle paths longer than MAXUSHORT.
  740. //
  741. if (BuffLen >= MAXUSHORT)
  742. {
  743. Status = ERROR_INVALID_PARAMETER;
  744. return Status;
  745. }
  746. FullFileName = new WCHAR[ BuffLen/sizeof(WCHAR) ];
  747. if (FullFileName == NULL)
  748. {
  749. Status = ERROR_NOT_ENOUGH_MEMORY;
  750. return Status;
  751. }
  752. CbCurrentPos = 0;
  753. // First the \??\ portion.
  754. RtlCopyMemory( FullFileName,
  755. WHACKWHACKQQ,
  756. WHACKWHACKQQ_SIZE );
  757. CbCurrentPos += WHACKWHACKQQ_SIZE;
  758. // Volume name goes next.
  759. RtlCopyMemory( &FullFileName[ CbCurrentPos / sizeof(WCHAR) ],
  760. pVolumeName->Buffer,
  761. pVolumeName->Length );
  762. CbCurrentPos += pVolumeName->Length;
  763. // The reparse path itself.
  764. RtlCopyMemory( &FullFileName[ CbCurrentPos / sizeof(WCHAR) ],
  765. pDfsDirectory,
  766. CbDirLength);
  767. CbCurrentPos += CbDirLength;
  768. FullFileName[ CbCurrentPos / sizeof(WCHAR) ] = UNICODE_NULL;
  769. UnicodeFileName.Buffer = FullFileName;
  770. UnicodeFileName.Length = (USHORT)CbCurrentPos;
  771. CbCurrentPos += sizeof(UNICODE_NULL);
  772. ASSERT( BuffLen == CbCurrentPos );
  773. UnicodeFileName.MaximumLength = (USHORT)CbCurrentPos;
  774. //
  775. // Finally get rid of this reparse point directory.
  776. // We delete only the reparse directory, not anything above it.
  777. // We don't want it to go all the way up and delete the root directory, for example.
  778. // BUG 701594
  779. //
  780. NtStatus = DfsDeleteLinkReparsePointDir( &UnicodeFileName, NULL );
  781. if (NtStatus != STATUS_SUCCESS)
  782. {
  783. Status = RtlNtStatusToDosError(NtStatus);
  784. }
  785. delete [] FullFileName;
  786. return Status;
  787. }
  788. DFSSTATUS
  789. DfsRemoveReparseIfOrphaned(
  790. IN HANDLE VolumeHandle,
  791. IN PUNICODE_STRING pVolumeName,
  792. IN LONGLONG FileReference,
  793. IN FILETIME ServiceStartupTime)
  794. {
  795. NTSTATUS NtStatus = STATUS_SUCCESS;
  796. DFSSTATUS Status = ERROR_SUCCESS;
  797. IO_STATUS_BLOCK IoStatusBlock;
  798. HANDLE ReparseDirectoryHandle = INVALID_HANDLE_VALUE;
  799. BOOLEAN Orphaned = FALSE;
  800. BOOLEAN ReparseOpened = FALSE;
  801. PFILE_NAME_INFORMATION pFileInfo = NULL;
  802. ULONG CbPathLen = MAX_PATH * sizeof(WCHAR);
  803. ULONG CbBufSize = 0;
  804. do {
  805. //
  806. // First get a handle to the reparse directory. We have its FileID.
  807. //
  808. Status = DfsOpenReparseByID( VolumeHandle,
  809. FileReference,
  810. &ReparseDirectoryHandle );
  811. if (Status != ERROR_SUCCESS)
  812. break;
  813. ReparseOpened = TRUE;
  814. Status = DfsIsReparseOrphaned( ReparseDirectoryHandle,
  815. ServiceStartupTime,
  816. &Orphaned);
  817. //
  818. // If this reparse point is active, we are done.
  819. //
  820. if (Status != ERROR_SUCCESS || Orphaned == FALSE)
  821. {
  822. break;
  823. }
  824. //
  825. // Query the name of the file.
  826. //
  827. do {
  828. CbBufSize = sizeof( FILE_NAME_INFORMATION ) + CbPathLen;
  829. pFileInfo = (PFILE_NAME_INFORMATION) new BYTE[CbBufSize];
  830. if (pFileInfo == NULL)
  831. {
  832. NtStatus = STATUS_INSUFFICIENT_RESOURCES;
  833. break;
  834. }
  835. //
  836. // Zero the buffer before querying the filename.
  837. //
  838. ZeroMemory( pFileInfo, CbBufSize );
  839. NtStatus = NtQueryInformationFile(
  840. ReparseDirectoryHandle,
  841. &IoStatusBlock,
  842. pFileInfo,
  843. CbBufSize,
  844. FileNameInformation);
  845. //
  846. // If we need to resize the buffer, do it in
  847. // multiples of two, but cap it at ULONGMAX.
  848. //
  849. if (NtStatus == STATUS_BUFFER_OVERFLOW)
  850. {
  851. delete [] pFileInfo;
  852. pFileInfo = NULL;
  853. if (CbPathLen >= (ULONG_MAX / 2))
  854. {
  855. // this isn't the ideal error message, but...
  856. NtStatus = STATUS_INVALID_PARAMETER;
  857. break;
  858. }
  859. CbPathLen *= 2;
  860. }
  861. } while (NtStatus == STATUS_BUFFER_OVERFLOW);
  862. Status = RtlNtStatusToDosError( NtStatus );
  863. if (Status != ERROR_SUCCESS ||
  864. pFileInfo == NULL) // To keep PREFAST happy
  865. {
  866. break;
  867. }
  868. CloseHandle( ReparseDirectoryHandle );
  869. ReparseOpened = FALSE;
  870. //
  871. // Now do the actual deletion
  872. //
  873. Status = DfsDeleteReparseDirectory( pVolumeName, pFileInfo->FileName, pFileInfo->FileNameLength );
  874. } while (FALSE);
  875. //
  876. // If we still haven't closed the reparse handle, do so before we get out.
  877. //
  878. if (ReparseOpened)
  879. {
  880. CloseHandle( ReparseDirectoryHandle );
  881. ReparseOpened = FALSE;
  882. }
  883. if (pFileInfo != NULL)
  884. {
  885. delete [] pFileInfo;
  886. pFileInfo = NULL;
  887. }
  888. return Status;
  889. }
  890. VOID
  891. DfsGetReparseVolumeToScan(
  892. PDFS_REPARSE_VOLUME_INFO *ppVolumeInfo )
  893. {
  894. PLIST_ENTRY pNext = NULL;
  895. PDFS_REPARSE_VOLUME_INFO pVolInfo = NULL;
  896. *ppVolumeInfo = NULL;
  897. //
  898. // this needs to be optimized to return a subset or LRU entries.
  899. //
  900. DfsAcquireGlobalDataLock();
  901. if (!IsListEmpty( &DfsServerGlobalData.ReparseVolumeList ))
  902. {
  903. pNext = RemoveHeadList( &DfsServerGlobalData.ReparseVolumeList );
  904. ASSERT( pNext != NULL);
  905. pVolInfo = CONTAINING_RECORD( pNext,
  906. DFS_REPARSE_VOLUME_INFO,
  907. ListEntry );
  908. if (!IsEmptyUnicodeString( &pVolInfo->VolumeName ))
  909. {
  910. *ppVolumeInfo = pVolInfo;
  911. }
  912. }
  913. DfsReleaseGlobalDataLock();
  914. }
  915. DFSSTATUS
  916. DfsRemoveOrphanedReparsePoints(
  917. IN PUNICODE_STRING pVolumeName,
  918. IN FILETIME ServiceStartupTime)
  919. {
  920. DFSSTATUS Status = ERROR_SUCCESS;
  921. HANDLE ReparseIndexHandle = INVALID_HANDLE_VALUE;
  922. HANDLE ReparseDirectoryHandle = INVALID_HANDLE_VALUE;
  923. HANDLE VolumeHandle = INVALID_HANDLE_VALUE;
  924. FILE_REPARSE_POINT_INFORMATION ReparseInfo;
  925. BOOLEAN Done = FALSE;
  926. do {
  927. //
  928. // Open the $Reparse index of the volume.
  929. //
  930. Status = DfsOpenReparseIndex( pVolumeName, &ReparseIndexHandle );
  931. if (Status != ERROR_SUCCESS)
  932. {
  933. break;
  934. }
  935. //
  936. // First get a handle to this volume.
  937. //
  938. Status = DfsGetVolumeHandleByName( pVolumeName, &VolumeHandle );
  939. if (Status != ERROR_SUCCESS)
  940. {
  941. break;
  942. }
  943. //
  944. // Go through all the reparse points in the index.
  945. //
  946. Done = FALSE;
  947. Status = DfsGetNextReparseRecord( ReparseIndexHandle,
  948. &ReparseInfo,
  949. &Done );
  950. while (!Done && Status == ERROR_SUCCESS)
  951. {
  952. //
  953. // If we find a DFS reparse point...
  954. //
  955. if (ReparseInfo.Tag == IO_REPARSE_TAG_DFS)
  956. {
  957. DFSSTATUS TempStatus;
  958. TempStatus = DfsRemoveReparseIfOrphaned( VolumeHandle,
  959. pVolumeName,
  960. ReparseInfo.FileReference,
  961. ServiceStartupTime );
  962. //
  963. // Ignore and move on if we hit an error. This will get retried when the
  964. // service starts up next.
  965. //
  966. DFS_TRACE_ERROR_NORM( Status, REFERRAL_SERVER,
  967. "[%!FUNC!] Status 0x%x in ReparseIfOrphaned for Volume %wZ, FileRef 0x%x\n",
  968. TempStatus, pVolumeName, (ULONG)ReparseInfo.FileReference);
  969. }
  970. //
  971. // Iterate to the next reparse record.
  972. //
  973. Status = DfsGetNextReparseRecord( ReparseIndexHandle,
  974. &ReparseInfo,
  975. &Done );
  976. }
  977. } while (FALSE);
  978. if (ReparseIndexHandle != INVALID_HANDLE_VALUE)
  979. {
  980. CloseHandle( ReparseIndexHandle );
  981. ReparseIndexHandle = INVALID_HANDLE_VALUE;
  982. }
  983. if (VolumeHandle != INVALID_HANDLE_VALUE)
  984. {
  985. CloseHandle( VolumeHandle );
  986. VolumeHandle = INVALID_HANDLE_VALUE;
  987. }
  988. return Status;
  989. }
  990. //
  991. // Given a path to a reparse point, this adds the volume that it resides in
  992. // to our list of volumes to scan (for orphaned reparse points) later.
  993. //
  994. DFSSTATUS
  995. DfsAddReparseVolumeToList (
  996. IN PUNICODE_STRING pDirectoryName)
  997. {
  998. DFSSTATUS Status = ERROR_SUCCESS;
  999. UNICODE_STRING VolumeName;
  1000. PLIST_ENTRY pNext = NULL;
  1001. BOOLEAN Found = FALSE;
  1002. PDFS_REPARSE_VOLUME_INFO pReparseVolInfo = NULL;
  1003. BOOLEAN VolumeAdded = FALSE;
  1004. do {
  1005. //
  1006. // First find the volume this path belongs in.
  1007. //
  1008. Status = DfsGetVolumePathName( pDirectoryName, &VolumeName );
  1009. if (Status != ERROR_SUCCESS)
  1010. {
  1011. break;
  1012. }
  1013. DfsAcquireGlobalDataLock();
  1014. {
  1015. //
  1016. // See if the volume is already on the list
  1017. //
  1018. pNext = DfsServerGlobalData.ReparseVolumeList.Flink;
  1019. while (pNext != &DfsServerGlobalData.ReparseVolumeList)
  1020. {
  1021. pReparseVolInfo = CONTAINING_RECORD( pNext,
  1022. DFS_REPARSE_VOLUME_INFO,
  1023. ListEntry );
  1024. if (RtlCompareUnicodeString(&pReparseVolInfo->VolumeName,
  1025. &VolumeName,
  1026. TRUE) == 0) // Case insensitive
  1027. {
  1028. Found = TRUE;
  1029. break;
  1030. }
  1031. pNext = pNext->Flink;
  1032. }
  1033. //
  1034. // Insert this volume only if it isn't already there.
  1035. //
  1036. if (!Found)
  1037. {
  1038. Status = DfsInsertInReparseVolList( VolumeName.Buffer );
  1039. }
  1040. }
  1041. DfsReleaseGlobalDataLock();
  1042. } while (FALSE);
  1043. //
  1044. // We've made a copy of the volume name, so we are ok to free it.
  1045. //
  1046. if (VolumeName.Buffer != NULL)
  1047. {
  1048. DfsFreeVolumePathName( &VolumeName );
  1049. }
  1050. return Status;
  1051. }
  1052. //
  1053. // This is the entry point for cleaning up reparse points.
  1054. // It'll iterate through all local volumes that are known to have DFS roots on them
  1055. // (and therefore reparse points) and inspect their respective $Reparse indices.
  1056. // This assumes that all good reparse points will have been written to when the service
  1057. // started up.
  1058. //
  1059. VOID
  1060. DfsRemoveOrphanedReparsePoints(
  1061. IN FILETIME ServiceStartupTime)
  1062. {
  1063. PDFS_REPARSE_VOLUME_INFO pVolInfo = NULL;
  1064. DfsGetReparseVolumeToScan( &pVolInfo );
  1065. while (pVolInfo != NULL)
  1066. {
  1067. DFS_TRACE_NORM( REFERRAL_SERVER, "[%!FUNC!] Starting ReparsePt cleanup on volume %wZ\n",
  1068. &pVolInfo->VolumeName);
  1069. // We have no choice but to ignore errors and keep going
  1070. (VOID)DfsRemoveOrphanedReparsePoints( &pVolInfo->VolumeName,
  1071. ServiceStartupTime );
  1072. pVolInfo = NULL; // paranoia
  1073. // Get the next volume if any.
  1074. DfsGetReparseVolumeToScan( &pVolInfo );
  1075. }
  1076. DFS_TRACE_NORM( REFERRAL_SERVER, "[%!FUNC!] Done reparse cleanup\n");
  1077. return;
  1078. }