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.

1158 lines
36 KiB

  1. //
  2. // History: 1-Mar-95 BillMo Created.
  3. // ...
  4. // 01-Dec-96 MikeHill Converted to new NT5 implementation.
  5. #include "shellprv.h"
  6. #pragma hdrstop
  7. #define LINKDATA_AS_CLASS
  8. #include <linkdata.hxx>
  9. #include "shelllnk.h"
  10. // NTRAID95363-2000-03-19: These four inlines are copied from private\net\svcdlls\trksvcs\common\trklib.hxx
  11. // They should be moved to linkdata.hxx
  12. inline
  13. CDomainRelativeObjId::operator == (const CDomainRelativeObjId &Other) const
  14. {
  15. return(_volume == Other._volume && _object == Other._object);
  16. }
  17. inline
  18. CDomainRelativeObjId::operator != (const CDomainRelativeObjId &Other) const
  19. {
  20. return !(*this == Other);
  21. }
  22. inline
  23. CVolumeId:: operator == (const CVolumeId & Other) const
  24. {
  25. return(0 == memcmp(&_volume, &Other._volume, sizeof(_volume)));
  26. }
  27. inline
  28. CVolumeId:: operator != (const CVolumeId & Other) const
  29. {
  30. return ! (Other == *this);
  31. }
  32. //+----------------------------------------------------------------------------
  33. //
  34. // Function: RPC free/alloc routines
  35. //
  36. // Synopsis: CTracker uses MIDL-generated code to call an RPC server,
  37. // and MIDL-generated code assumes that the following routines
  38. // be provided.
  39. //
  40. //+----------------------------------------------------------------------------
  41. void __RPC_USER MIDL_user_free(void __RPC_FAR *pv)
  42. {
  43. LocalFree(pv);
  44. }
  45. void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t s)
  46. {
  47. return (void __RPC_FAR *) LocalAlloc(LMEM_FIXED, s);
  48. }
  49. //+----------------------------------------------------------------------------
  50. //
  51. // Method: IUnknown methods
  52. //
  53. // Synopsis: IUnknown methods for the ISLTracker interface.
  54. //
  55. //+----------------------------------------------------------------------------
  56. STDMETHODIMP CTracker::QueryInterface(REFIID riid, void **ppvObj)
  57. {
  58. return _psl->QueryInterface(riid, ppvObj);
  59. }
  60. STDMETHODIMP_(ULONG) CTracker::AddRef()
  61. {
  62. return _psl->AddRef();
  63. }
  64. STDMETHODIMP_(ULONG) CTracker::Release()
  65. {
  66. return _psl->Release();
  67. }
  68. //+----------------------------------------------------------------------------
  69. //
  70. // Method: ISLTracker custom methods
  71. //
  72. // Synopsis: This interface is private and is only used for testing.
  73. // This provides test programs the ability to specify the
  74. // TrackerRestrictions (from the TrkMendRestrictions enum)
  75. // and the ability to get the internal IDs.
  76. //
  77. //+----------------------------------------------------------------------------
  78. HRESULT CTracker::Resolve(HWND hwnd, DWORD dwResolveFlags, DWORD dwTracker)
  79. {
  80. return _psl->_Resolve(hwnd, dwResolveFlags, dwTracker);
  81. }
  82. HRESULT CTracker::GetIDs(CDomainRelativeObjId *pdroidBirth, CDomainRelativeObjId *pdroidLast, CMachineId *pmcid)
  83. {
  84. if (!_fLoaded)
  85. return E_UNEXPECTED;
  86. *pdroidBirth = _droidBirth;
  87. *pdroidLast = _droidLast;
  88. *pmcid = _mcidLast;
  89. return S_OK;
  90. }
  91. //+----------------------------------------------------------------------------
  92. // Synopsis: Initializes the data members used for RPC. This should be
  93. // called either by InitNew or Load.
  94. //
  95. // Arguments: None
  96. //
  97. // Returns: [HRESULT]
  98. //
  99. //+----------------------------------------------------------------------------
  100. HRESULT CTracker::InitRPC()
  101. {
  102. HRESULT hr = S_OK;
  103. if (!_fCritsecInitialized)
  104. {
  105. if (!InitializeCriticalSectionAndSpinCount(&_cs, 0))
  106. {
  107. hr = E_FAIL;
  108. goto Exit;
  109. }
  110. _fCritsecInitialized = TRUE;
  111. }
  112. if (NULL == _pRpcAsyncState)
  113. {
  114. _pRpcAsyncState = new RPC_ASYNC_STATE;
  115. if (NULL == _pRpcAsyncState)
  116. {
  117. hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
  118. goto Exit;
  119. }
  120. }
  121. if (NULL == _hEvent)
  122. {
  123. _hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto-reset, not initially signaled
  124. if (NULL == _hEvent)
  125. {
  126. hr = HRESULT_FROM_WIN32(GetLastError());
  127. goto Exit;
  128. }
  129. }
  130. Exit:
  131. return hr;
  132. }
  133. //+----------------------------------------------------------------------------
  134. //
  135. // Synopsis: Initializes the CTracker object. This method may be called
  136. // repeatedly, i.e. it may be called to clear/reinit the object.
  137. // This method need not be called before calling the Load method.
  138. //
  139. // Arguments: None
  140. //
  141. // Returns: [HRESULT]
  142. //
  143. //+----------------------------------------------------------------------------
  144. HRESULT CTracker::InitNew()
  145. {
  146. HRESULT hr = InitRPC();
  147. if (SUCCEEDED(hr))
  148. {
  149. _mcidLast = CMachineId();
  150. _droidLast = CDomainRelativeObjId();
  151. _droidBirth = CDomainRelativeObjId();
  152. _fDirty = FALSE;
  153. _fLoaded = FALSE;
  154. _fMendInProgress = FALSE;
  155. _fUserCancelled = FALSE;
  156. }
  157. return hr;
  158. } // CTracker::InitNew()
  159. //+----------------------------------------------------------------------------
  160. //
  161. // Synopsis: Get tracking state from the given file handle. Note that this
  162. // is expected to fail if the referrent file isn't on an
  163. // NTFS5 volume.
  164. //
  165. //
  166. // Arguments: [hFile]
  167. // The file to track
  168. // [ptszFile]
  169. // The name of the file
  170. //
  171. // Returns: [HRESULT]
  172. //
  173. //-----------------------------------------------------------------------------
  174. HRESULT CTracker::InitFromHandle(const HANDLE hFile, const TCHAR* ptszFile)
  175. {
  176. NTSTATUS status = STATUS_SUCCESS;
  177. FILE_OBJECTID_BUFFER fobOID = {0};
  178. DWORD cbReturned;
  179. CDomainRelativeObjId droidLast;
  180. CDomainRelativeObjId droidBirth;
  181. CMachineId mcidLast;
  182. // Initialize the RPC members
  183. HRESULT hr = InitRPC();
  184. if (FAILED(hr))
  185. goto Exit;
  186. // -----------------------------------
  187. // Get the Object ID Buffer (64 bytes)
  188. // -----------------------------------
  189. // Use the file handle to get the file's Object ID. Tell the filesystem to give us the
  190. // existing object ID if the file already has one, or to create a new one otherwise.
  191. if (!DeviceIoControl(hFile, FSCTL_CREATE_OR_GET_OBJECT_ID,
  192. NULL, 0, // No input buffer
  193. &fobOID, sizeof(fobOID), // Output buffer
  194. &cbReturned, NULL))
  195. {
  196. hr = HRESULT_FROM_WIN32(GetLastError());
  197. goto Exit;
  198. }
  199. // ----------------------
  200. // Load the Droids & MCID
  201. // ----------------------
  202. status = droidLast.InitFromFile(hFile, fobOID);
  203. if (!NT_SUCCESS(status))
  204. {
  205. hr = HRESULT_FROM_WIN32(RtlNtStatusToDosError(status));
  206. goto Exit;
  207. }
  208. droidBirth.InitFromFOB(fobOID);
  209. droidBirth.GetVolumeId().Normalize();
  210. if (FAILED(mcidLast.InitFromPath(ptszFile, hFile)))
  211. mcidLast = CMachineId();
  212. // ----
  213. // Exit
  214. // ----
  215. if (_mcidLast != mcidLast
  216. ||
  217. _droidLast != droidLast
  218. ||
  219. _droidBirth != droidBirth
  220. )
  221. {
  222. _mcidLast = mcidLast;
  223. _droidLast = droidLast;
  224. _droidBirth = droidBirth;
  225. _fDirty = TRUE;
  226. }
  227. _fLoaded = TRUE; // Cleared in InitNew
  228. _fLoadedAtLeastOnce = TRUE; // Not cleared in InitNew
  229. hr = S_OK;
  230. Exit:
  231. return hr;
  232. }
  233. //+-------------------------------------------------------------------
  234. //
  235. // Synopsis: Load the tracker from the memory buffer. The InitNew
  236. // method need not be called before calling this method.
  237. //
  238. // Arguments: [pb] -- buffer to load from
  239. // [cb] -- size of pb buffer
  240. //
  241. // Returns: [HRESULT]
  242. //
  243. //--------------------------------------------------------------------
  244. #define CTRACKER_VERSION 0
  245. HRESULT CTracker::Load(BYTE *pb, ULONG cb)
  246. {
  247. DWORD dwLength;
  248. // Initialize RPC if it hasn't been already.
  249. HRESULT hr = InitRPC();
  250. if (FAILED(hr))
  251. goto Exit;
  252. // Check the length
  253. dwLength = *reinterpret_cast<DWORD*>(pb);
  254. if (dwLength < GetSize())
  255. {
  256. hr = E_INVALIDARG;
  257. goto Exit;
  258. }
  259. pb += sizeof(dwLength);
  260. // Check the version number
  261. if (CTRACKER_VERSION != *reinterpret_cast<DWORD*>(pb))
  262. {
  263. hr = HRESULT_FROM_WIN32(ERROR_REVISION_MISMATCH);
  264. goto Exit;
  265. }
  266. pb += sizeof(DWORD); // Skip past the version
  267. // Get the machine ID & droids
  268. _mcidLast = *reinterpret_cast<CMachineId*>(pb);
  269. pb += sizeof(_mcidLast);
  270. _droidLast = *reinterpret_cast<CDomainRelativeObjId*>(pb);
  271. pb += sizeof(_droidLast);
  272. _droidBirth = *reinterpret_cast<CDomainRelativeObjId*>(pb);
  273. pb += sizeof(_droidBirth);
  274. _fLoaded = TRUE; // Cleared in InitNew
  275. _fLoadedAtLeastOnce = TRUE; // Not cleared in InitNew
  276. hr = S_OK;
  277. Exit:
  278. return hr;
  279. }
  280. //+-------------------------------------------------------------------
  281. //
  282. // Member: CTracker::Save
  283. //
  284. // Synopsis: Save tracker to the given buffer.
  285. //
  286. // Arguments: [pb] -- buffer for tracker.
  287. // [cbSize] -- size of buffer in pb
  288. //
  289. // Returns: None
  290. //
  291. //--------------------------------------------------------------------
  292. VOID CTracker::Save(BYTE *pb, ULONG cbSize)
  293. {
  294. // Save the length
  295. *reinterpret_cast<DWORD*>(pb) = GetSize();
  296. pb += sizeof(DWORD);
  297. // Save a version number
  298. *reinterpret_cast<DWORD*>(pb) = CTRACKER_VERSION;
  299. pb += sizeof(DWORD);
  300. // Save the machine & DROIDs
  301. *reinterpret_cast<CMachineId*>(pb) = _mcidLast;
  302. pb += sizeof(_mcidLast);
  303. *reinterpret_cast<CDomainRelativeObjId*>(pb) = _droidLast;
  304. pb += sizeof(_droidLast);
  305. *reinterpret_cast<CDomainRelativeObjId*>(pb) = _droidBirth;
  306. pb += sizeof(_droidBirth);
  307. _fDirty = FALSE;
  308. } // CTracker::Save()
  309. //+-------------------------------------------------------------------
  310. //
  311. // Synopsis: Search for the object referred to by the tracker.
  312. //
  313. // Arguments: [dwTickCountDeadline] -- absolute tick count for deadline
  314. // [pfdIn] -- may not be NULL
  315. // [pfdOut] -- may not be NULL
  316. // will contain updated data on success
  317. // [uShlinkFlags] -- SLR_ flags
  318. // [TrackerRestrictions] -- TrkMendRestrictions enumeration
  319. //
  320. // Returns: [HRESULT]
  321. // S_OK
  322. // found (pfdOut contains new info)
  323. // E_UNEXPECTED
  324. // CTracker::InitNew hasn't bee called.
  325. // HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)
  326. // Restrictions (set in registry) are set such that
  327. // this operation isn't to be performed.
  328. //
  329. //--------------------------------------------------------------------
  330. HRESULT CTracker::Search(const DWORD dwTickCountDeadline,
  331. const WIN32_FIND_DATA *pfdIn,
  332. WIN32_FIND_DATA *pfdOut,
  333. UINT uShlinkFlags,
  334. DWORD TrackerRestrictions)
  335. {
  336. HRESULT hr = S_OK;
  337. TCHAR ptszError = NULL;
  338. WIN32_FILE_ATTRIBUTE_DATA fadNew;
  339. WIN32_FIND_DATA fdNew = *pfdIn;
  340. DWORD cbFileName;
  341. BOOL fPotentialFileFound = FALSE;
  342. BOOL fLocked = FALSE;
  343. DWORD dwCurrentTickCount = 0;
  344. RPC_TCHAR *ptszStringBinding = NULL;
  345. RPC_BINDING_HANDLE BindingHandle;
  346. RPC_STATUS rpcstatus;
  347. CDomainRelativeObjId droidBirth, droidLast, droidCurrent;
  348. CMachineId mcidCurrent;
  349. // Initialize the output
  350. ZeroMemory(pfdOut, sizeof(*pfdOut));
  351. // Abort if restrictions don't allow this operation
  352. if (SHRestricted(REST_NORESOLVETRACK) ||
  353. (SLR_NOTRACK & uShlinkFlags))
  354. {
  355. hr = HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED);
  356. goto Exit;
  357. }
  358. // Ensure that we've been loaded first
  359. else if (!_fLoaded)
  360. {
  361. hr = E_UNEXPECTED;
  362. goto Exit;
  363. }
  364. // Capture the current tick count
  365. dwCurrentTickCount = GetTickCount();
  366. if ((long) dwTickCountDeadline <= (long) dwCurrentTickCount)
  367. {
  368. hr = HRESULT_FROM_WIN32(ERROR_SERVICE_REQUEST_TIMEOUT);
  369. goto Exit;
  370. }
  371. //
  372. // Create an RPC binding
  373. //
  374. rpcstatus = RpcStringBindingCompose(NULL,
  375. TEXT("ncalrpc"),
  376. NULL,
  377. TRKWKS_LRPC_ENDPOINT_NAME,
  378. NULL,
  379. &ptszStringBinding);
  380. if (RPC_S_OK == rpcstatus)
  381. rpcstatus = RpcBindingFromStringBinding(ptszStringBinding, &BindingHandle);
  382. if (RPC_S_OK != rpcstatus)
  383. {
  384. hr = HRESULT_FROM_WIN32(rpcstatus);
  385. goto Exit;
  386. }
  387. //
  388. // Initialize an RPC Async handle
  389. //
  390. // Take the lock
  391. EnterCriticalSection(&_cs);
  392. fLocked = TRUE;
  393. // Verify that we were initialized properly
  394. if (NULL == _hEvent || NULL == _pRpcAsyncState)
  395. {
  396. hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
  397. goto Exit;
  398. }
  399. rpcstatus = RpcAsyncInitializeHandle(_pRpcAsyncState, RPC_ASYNC_VERSION_1_0);
  400. if (RPC_S_OK != rpcstatus)
  401. {
  402. hr = HRESULT_FROM_WIN32(rpcstatus);
  403. goto Exit;
  404. }
  405. _pRpcAsyncState->NotificationType = RpcNotificationTypeEvent;
  406. _pRpcAsyncState->u.hEvent = _hEvent;
  407. _pRpcAsyncState->UserInfo = NULL;
  408. //
  409. // Call the tracking service to find the file
  410. //
  411. __try
  412. {
  413. SYSTEMTIME stNow;
  414. FILETIME ftDeadline;
  415. DWORD dwDeltaMillisecToDeadline;
  416. // NOTE: The following four assignments used to be above the
  417. // __try. But that appears to trigger a compiler problem, where
  418. // some of the assignments do not make it to the .obj in an optimized
  419. // build (bug 265255).
  420. droidLast = _droidLast;
  421. droidBirth = _droidBirth;
  422. mcidCurrent = _mcidLast;
  423. cbFileName = sizeof(fdNew.cFileName);
  424. // Convert the tick-count deadline into a UTC filetime.
  425. dwDeltaMillisecToDeadline = (DWORD)((long)dwTickCountDeadline - (long)dwCurrentTickCount);
  426. GetSystemTime(&stNow);
  427. SystemTimeToFileTime(&stNow, &ftDeadline);
  428. *reinterpret_cast<LONGLONG*>(&ftDeadline) += (dwDeltaMillisecToDeadline * 10*1000);
  429. // Start the async RPC call to the tracking service
  430. _fMendInProgress = TRUE;
  431. LnkMendLink(_pRpcAsyncState,
  432. BindingHandle,
  433. ftDeadline,
  434. TrackerRestrictions,
  435. const_cast<CDomainRelativeObjId*>(&droidBirth),
  436. const_cast<CDomainRelativeObjId*>(&droidLast),
  437. const_cast<CMachineId*>(&_mcidLast),
  438. &droidCurrent,
  439. &mcidCurrent,
  440. &cbFileName,
  441. fdNew.cFileName);
  442. // Wait for the call to return. Release the lock first, though, so that
  443. // the UI thread can come in and cancel.
  444. LeaveCriticalSection(&_cs);
  445. fLocked = FALSE;
  446. DWORD dwWaitReturn = WaitForSingleObject(_hEvent, dwDeltaMillisecToDeadline);
  447. // Now take the lock back and see what happenned.
  448. EnterCriticalSection(&_cs); fLocked = TRUE;
  449. _fMendInProgress = FALSE;
  450. if ((WAIT_TIMEOUT == dwWaitReturn) || _fUserCancelled)
  451. {
  452. // We timed out waiting for a response. Cancel the call.
  453. // If the call should complete between the time
  454. // we exited the WaitForSingleObject and the cancel call below,
  455. // then the cancel will be ignored by RPC.
  456. rpcstatus = RpcAsyncCancelCall(_pRpcAsyncState, TRUE); // fAbort
  457. if (_fUserCancelled)
  458. {
  459. _fUserCancelled = FALSE;
  460. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  461. __leave;
  462. }
  463. else if (RPC_S_OK != rpcstatus)
  464. {
  465. hr = HRESULT_FROM_WIN32(rpcstatus);
  466. __leave;
  467. }
  468. }
  469. else if (WAIT_OBJECT_0 != dwWaitReturn)
  470. {
  471. // There was an error of some kind.
  472. hr = HRESULT_FROM_WIN32(GetLastError());
  473. __leave;
  474. }
  475. // Now we find out how the LnkMendLink call completed. If we get
  476. // RPC_S_OK, then it completed normally, and the result is
  477. // in hr.
  478. rpcstatus = RpcAsyncCompleteCall(_pRpcAsyncState, &hr);
  479. if (RPC_S_OK != rpcstatus)
  480. {
  481. // The call either failed or was cancelled (the reason for the
  482. // cancel would be that the UI thread called CTracker::CancelSearch,
  483. // or because we timed out above and called RpcAsyncCancelCall).
  484. hr = HRESULT_FROM_WIN32(rpcstatus);
  485. __leave;
  486. }
  487. }
  488. __except(EXCEPTION_EXECUTE_HANDLER)
  489. {
  490. _fMendInProgress = FALSE;
  491. _fUserCancelled = FALSE;
  492. hr = HRESULT_FROM_WIN32(RpcExceptionCode());
  493. }
  494. // free the binding
  495. RpcBindingFree(&BindingHandle);
  496. if (HRESULT_FROM_WIN32(ERROR_POTENTIAL_FILE_FOUND) == hr)
  497. {
  498. fPotentialFileFound = TRUE;
  499. hr = S_OK;
  500. }
  501. if (FAILED(hr)) goto Exit;
  502. //
  503. // See if this is in the recycle bin
  504. //
  505. if (IsFileInBitBucket(fdNew.cFileName))
  506. {
  507. hr = E_FAIL;
  508. goto Exit;
  509. }
  510. //
  511. // Now that we know what the new filename is, let's get all
  512. // the FindData info.
  513. //
  514. if (!GetFileAttributesEx(fdNew.cFileName, GetFileExInfoStandard, &fadNew))
  515. {
  516. hr = HRESULT_FROM_WIN32(GetLastError());
  517. goto Exit;
  518. }
  519. // Ensure that the file we found has the same "directory-ness"
  520. // as the last known link source (either they're both a directory
  521. // or they're both a file). Also ensure that the file we found
  522. // isn't itself a link client (a shell shortcut).
  523. if (((fadNew.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ^ (pfdIn->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  524. || PathIsLnk(fdNew.cFileName))
  525. {
  526. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  527. goto Exit;
  528. }
  529. // Copy the file attributes into the WIN32_FIND_DATA structure.
  530. fdNew.dwFileAttributes = fadNew.dwFileAttributes;
  531. fdNew.ftCreationTime = fadNew.ftCreationTime;
  532. fdNew.ftLastAccessTime = fadNew.ftLastAccessTime;
  533. fdNew.ftLastWriteTime = fadNew.ftLastWriteTime;
  534. fdNew.nFileSizeLow = fadNew.nFileSizeLow;
  535. // Return the new finddata to the caller.
  536. *pfdOut = fdNew;
  537. // Update our local state
  538. if ((_droidLast != droidCurrent) || (_droidBirth != droidBirth) || (_mcidLast != mcidCurrent))
  539. {
  540. _droidLast = droidCurrent;
  541. _droidBirth = droidBirth;
  542. _mcidLast = mcidCurrent;
  543. _fDirty = TRUE;
  544. }
  545. Exit:
  546. if (fLocked)
  547. LeaveCriticalSection(&_cs);
  548. if (ptszStringBinding)
  549. RpcStringFree(&ptszStringBinding);
  550. if (FAILED(hr))
  551. DebugMsg(DM_TRACE, TEXT("CTracker::Search failed (hr=0x%08X)"), hr);
  552. else if (fPotentialFileFound)
  553. hr = HRESULT_FROM_WIN32(ERROR_POTENTIAL_FILE_FOUND);
  554. return(hr);
  555. } // CTracker::Search()
  556. //+----------------------------------------------------------------------------
  557. //
  558. // Synopsis: This method is called on a thread signal another thread
  559. // which is in CTracker::Search to abort the LnkMendLink
  560. // call.
  561. //
  562. // Returns: [HRESULT]
  563. //
  564. //-----------------------------------------------------------------------------
  565. STDMETHODIMP CTracker::CancelSearch()
  566. {
  567. EnterCriticalSection(&_cs);
  568. // If a search is in progress, cancel it.
  569. if (_fMendInProgress && NULL != _pRpcAsyncState)
  570. {
  571. _fUserCancelled = TRUE;
  572. SetEvent(_hEvent); // SetEvent so as to unblock the Tracker Worker thread.
  573. }
  574. LeaveCriticalSection(&_cs);
  575. return S_OK;
  576. }
  577. //+----------------------------------------------------------------------------
  578. //
  579. // Look at a path and determine the computer name of the host machine.
  580. // In the future, we should remove this code, and add the capbility to query
  581. // handles for their computer name.
  582. //
  583. // GetServerComputer name uses ScanForComputerName and ConvertDfsPath
  584. // as helper functions.
  585. //
  586. // The name can only be obtained for NetBios paths - if the path is IP or DNS
  587. // an error is returned. (If the NetBios name has a "." in it, it will
  588. // cause an error because it will be misinterpreted as a DNS path. This case
  589. // becomes less and less likely as the NT5 UI doesn't allow such computer names.)
  590. // For DFS paths, the leaf server's name is returned, as long as it wasn't
  591. // joined to its parent with an IP or DNS path name.
  592. //
  593. //+----------------------------------------------------------------------------
  594. const UNICODE_STRING NtUncPathNamePrefix = { 16, 18, L"\\??\\UNC\\"};
  595. #define cchNtUncPathNamePrefix 8
  596. const UNICODE_STRING NtDrivePathNamePrefix = { 8, 10, L"\\??\\" };
  597. #define cchNtDrivePathNamePrefix 4
  598. const WCHAR RedirectorMappingPrefix[] = { L"\\Device\\LanmanRedirector\\;" };
  599. const WCHAR LocalVolumeMappingPrefix[] = { L"\\Device\\Volume" };
  600. const WCHAR CDRomMappingPrefix[] = { L"\\Device\\CDRom" };
  601. const WCHAR FloppyMappingPrefix[] = { L"\\Device\\Floppy" };
  602. const WCHAR DfsMappingPrefix[] = { L"\\Device\\WinDfs\\" };
  603. //
  604. // ScanForComputerName:
  605. //
  606. // Scan the path in ServerFileName (which is a UNICODE_STRING with
  607. // a full NT path name), searching for the computer name. If it's
  608. // found, point to it with UnicodeComputerName.Buffer, and set
  609. // *AvailableLength to show how much readable memory is after that
  610. // point.
  611. //
  612. HRESULT ScanForComputerName(HANDLE hFile, const UNICODE_STRING &ServerFileName,
  613. UNICODE_STRING *UnicodeComputerName, ULONG *AvailableLength,
  614. WCHAR *DosDeviceMapping, ULONG cchDosDeviceMapping,
  615. PFILE_NAME_INFORMATION FileNameInfo, ULONG cbFileNameInfo,
  616. BOOL *CheckForDfs)
  617. {
  618. HRESULT hr = S_OK;
  619. // Is this a UNC path?
  620. if (RtlPrefixString((PSTRING)&NtUncPathNamePrefix, (PSTRING)&ServerFileName, TRUE))
  621. {
  622. // Make sure there's some more to this path than just the prefix
  623. if (ServerFileName.Length <= NtUncPathNamePrefix.Length)
  624. {
  625. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  626. goto Exit;
  627. }
  628. // It appears to be a valid UNC path. Point to the beginning of the computer
  629. // name, and calculate how much room is left in ServerFileName after that.
  630. UnicodeComputerName->Buffer = &ServerFileName.Buffer[ NtUncPathNamePrefix.Length/sizeof(WCHAR) ];
  631. *AvailableLength = ServerFileName.Length - NtUncPathNamePrefix.Length;
  632. }
  633. else if (RtlPrefixString((PSTRING)&NtDrivePathNamePrefix, (PSTRING)&ServerFileName, TRUE)
  634. &&
  635. ServerFileName.Buffer[ cchNtDrivePathNamePrefix + 1 ] == L':')
  636. {
  637. // Get the correct, upper-cased, drive letter into DosDevice.
  638. WCHAR DosDevice[3] = { L"A:" };
  639. DosDevice[0] = ServerFileName.Buffer[ cchNtDrivePathNamePrefix ];
  640. if (L'a' <= DosDevice[0] && DosDevice[0] <= L'z')
  641. DosDevice[0] = L'A' + (DosDevice[0] - L'a');
  642. // Map the drive letter to its symbolic link under \??. E.g., say D: & R:
  643. // are DFS/rdr drives, you would then see something like:
  644. //
  645. // D: => \Device\WinDfs\G
  646. // R: => \Device\LanmanRedirector\;R:0\scratch\scratch
  647. if (!QueryDosDevice(DosDevice, DosDeviceMapping, cchDosDeviceMapping))
  648. {
  649. hr = HRESULT_FROM_WIN32(GetLastError());
  650. goto Exit;
  651. }
  652. // Now that we have the DosDeviceMapping, we can check ... Is this a rdr drive?
  653. if (// Does it begin with "\Device\LanmanRedirector\;" ?
  654. 0 == wcsncmp(DosDeviceMapping, RedirectorMappingPrefix, lstrlenW(RedirectorMappingPrefix))
  655. &&
  656. // Are the next letters the correct drive letter, a colon, and a whack?
  657. (DosDevice[0] == DosDeviceMapping[ sizeof(RedirectorMappingPrefix)/sizeof(WCHAR) - 1 ]
  658. &&
  659. L':' == DosDeviceMapping[ sizeof(RedirectorMappingPrefix)/sizeof(WCHAR) ]
  660. &&
  661. (UnicodeComputerName->Buffer = StrChrW(&DosDeviceMapping[ sizeof(RedirectorMappingPrefix)/sizeof(WCHAR) + 1 ], L'\\'))
  662. ))
  663. {
  664. // We have a valid rdr drive. Point to the beginning of the computer
  665. // name, and calculate how much room is availble in DosDeviceMapping after that.
  666. UnicodeComputerName->Buffer += 1;
  667. *AvailableLength = sizeof(DosDeviceMapping) - sizeof(DosDeviceMapping[0]) * (ULONG)(UnicodeComputerName->Buffer - DosDeviceMapping);
  668. // We know now that it's not a DFS path
  669. *CheckForDfs = FALSE;
  670. }
  671. else if (0 == wcsncmp(DosDeviceMapping, DfsMappingPrefix, lstrlenW(DfsMappingPrefix)))
  672. {
  673. // Get the full UNC name of this DFS path. Later, we'll call the DFS
  674. // driver to find out what the actual server name is.
  675. IO_STATUS_BLOCK IoStatusBlock;
  676. NTSTATUS NtStatus = NtQueryInformationFile(hFile,
  677. &IoStatusBlock, FileNameInfo, cbFileNameInfo, FileNameInformation);
  678. if (!NT_SUCCESS(NtStatus))
  679. {
  680. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  681. goto Exit;
  682. }
  683. UnicodeComputerName->Buffer = FileNameInfo->FileName + 1;
  684. *AvailableLength = FileNameInfo->FileNameLength;
  685. }
  686. else
  687. {
  688. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  689. goto Exit;
  690. }
  691. } // else if (RtlPrefixString((PSTRING)&NtDrivePathNamePrefix, (PSTRING)&ServerFileName, TRUE) ...
  692. else
  693. {
  694. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  695. goto Exit;
  696. }
  697. Exit:
  698. return hr;
  699. }
  700. //
  701. // Try to convert the path name pointed to by UnicodeComputerName.Buffer
  702. // into a DFS path name. The caller provides DfsServerPathName as a buffer
  703. // for the converted name. If it's a DFS path, then update UnicodeComputerName.Buffer
  704. // to point to the conversion, otherwise leave it unchanged.
  705. //
  706. HRESULT ConvertDfsPath(HANDLE hFile, UNICODE_STRING *UnicodeComputerName,
  707. ULONG *AvailableLength, WCHAR *DfsServerPathName, ULONG cbDfsServerPathName)
  708. {
  709. HRESULT hr = S_OK;
  710. HANDLE hDFS = INVALID_HANDLE_VALUE;
  711. UNICODE_STRING DfsDriverName;
  712. NTSTATUS NtStatus;
  713. IO_STATUS_BLOCK IoStatusBlock;
  714. OBJECT_ATTRIBUTES ObjectAttributes;
  715. WCHAR *DfsPathName = UnicodeComputerName->Buffer - 1; // Back up to the whack
  716. ULONG DfsPathNameLength = *AvailableLength + sizeof(WCHAR);
  717. // Open the DFS driver
  718. RtlInitUnicodeString(&DfsDriverName, L"\\Dfs");
  719. InitializeObjectAttributes(&ObjectAttributes,
  720. &DfsDriverName,
  721. OBJ_CASE_INSENSITIVE,
  722. NULL,
  723. NULL
  724. );
  725. NtStatus = NtCreateFile(
  726. &hDFS,
  727. SYNCHRONIZE,
  728. &ObjectAttributes,
  729. &IoStatusBlock,
  730. NULL,
  731. FILE_ATTRIBUTE_NORMAL,
  732. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  733. FILE_OPEN_IF,
  734. FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
  735. NULL,
  736. 0
  737. );
  738. if (!NT_SUCCESS(NtStatus))
  739. {
  740. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  741. goto Exit;
  742. }
  743. // Query DFS's cache for the server name. The name is guaranteed to
  744. // remain in the cache as long as the file is open.
  745. if (L'\\' != DfsPathName[0])
  746. {
  747. NtClose(hDFS);
  748. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  749. goto Exit;
  750. }
  751. NtStatus = NtFsControlFile(
  752. hDFS,
  753. NULL, // Event,
  754. NULL, // ApcRoutine,
  755. NULL, // ApcContext,
  756. &IoStatusBlock,
  757. FSCTL_DFS_GET_SERVER_NAME,
  758. DfsPathName,
  759. DfsPathNameLength,
  760. DfsServerPathName,
  761. cbDfsServerPathName);
  762. NtClose(hDFS);
  763. // STATUS_OBJECT_NAME_NOT_FOUND means that it's not a DFS path
  764. if (!NT_SUCCESS(NtStatus))
  765. {
  766. if (STATUS_OBJECT_NAME_NOT_FOUND != NtStatus )
  767. {
  768. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  769. goto Exit;
  770. }
  771. }
  772. else if (L'\0' != DfsServerPathName[0])
  773. {
  774. // The previous DFS call returns the server-specific path to the file in UNC form.
  775. // Point UnicodeComputerName to just past the two whacks.
  776. *AvailableLength = lstrlenW(DfsServerPathName) * sizeof(WCHAR);
  777. if (3*sizeof(WCHAR) > *AvailableLength
  778. ||
  779. L'\\' != DfsServerPathName[0]
  780. ||
  781. L'\\' != DfsServerPathName[1])
  782. {
  783. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  784. goto Exit;
  785. }
  786. UnicodeComputerName->Buffer = DfsServerPathName + 2;
  787. *AvailableLength -= 2 * sizeof(WCHAR);
  788. }
  789. Exit:
  790. return hr;
  791. }
  792. // Take pwszFile, which is a path to a remote machine, and get the
  793. // server machine's computer name.
  794. HRESULT GetRemoteServerComputerName(LPCWSTR pwszFile, HANDLE hFile, WCHAR *pwszComputer)
  795. {
  796. HRESULT hr = S_OK;
  797. ULONG cbComputer = 0;
  798. ULONG AvailableLength = 0;
  799. PWCHAR PathCharacter = NULL;
  800. BOOL CheckForDfs = TRUE;
  801. NTSTATUS NtStatus = STATUS_SUCCESS;
  802. WCHAR FileNameInfoBuffer[MAX_PATH+sizeof(FILE_NAME_INFORMATION)];
  803. PFILE_NAME_INFORMATION FileNameInfo = (PFILE_NAME_INFORMATION)FileNameInfoBuffer;
  804. WCHAR DfsServerPathName[ MAX_PATH + 1 ];
  805. WCHAR DosDeviceMapping[ MAX_PATH + 1 ];
  806. UNICODE_STRING UnicodeComputerName;
  807. UNICODE_STRING ServerFileName;
  808. // Canonicalize the file name into the NT object directory namespace.
  809. RtlInitUnicodeString(&UnicodeComputerName, NULL);
  810. RtlInitUnicodeString(&ServerFileName, NULL);
  811. if (!RtlDosPathNameToNtPathName_U(pwszFile, &ServerFileName, NULL, NULL))
  812. {
  813. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  814. goto Exit;
  815. }
  816. // Point UnicodeComputerName.Buffer at the beginning of the computer name.
  817. hr = ScanForComputerName(hFile, ServerFileName, &UnicodeComputerName, &AvailableLength,
  818. DosDeviceMapping, ARRAYSIZE(DosDeviceMapping),
  819. FileNameInfo, sizeof(FileNameInfoBuffer), &CheckForDfs);
  820. if (FAILED(hr))
  821. goto Exit;
  822. // If there was no error but we don't have a computer name, then the file is on
  823. // the local computer.
  824. if (NULL == UnicodeComputerName.Buffer)
  825. {
  826. DWORD cchName = MAX_COMPUTERNAME_LENGTH + 1;
  827. hr = S_OK;
  828. if (!GetComputerNameW(pwszComputer, &cchName))
  829. hr = HRESULT_FROM_WIN32(GetLastError());
  830. goto Exit;
  831. }
  832. // If we couldn't determine above whether or not this is a DFS path, let the
  833. // DFS driver decide now.
  834. if (CheckForDfs && INVALID_HANDLE_VALUE != hFile)
  835. {
  836. // On return, UnicodeComputerName.Buffer points to the leaf machine's
  837. // UNC name if it's a DFS path. If it's not a DFS path,
  838. // .Buffer is left unchanged.
  839. hr = ConvertDfsPath(hFile, &UnicodeComputerName, &AvailableLength,
  840. DfsServerPathName, sizeof(DfsServerPathName));
  841. if (FAILED(hr))
  842. goto Exit;
  843. }
  844. // If we get here, then the computer name\share is pointed to by UnicodeComputerName.Buffer.
  845. // But the Length is currently zero, so we search for the whack that separates
  846. // the computer name from the share, and set the Length to include just the computer name.
  847. PathCharacter = UnicodeComputerName.Buffer;
  848. while(((ULONG) ((PCHAR)PathCharacter - (PCHAR)UnicodeComputerName.Buffer) < AvailableLength)
  849. &&
  850. *PathCharacter != L'\\')
  851. {
  852. // If we found a '.', we fail because this is probably a DNS or IP name.
  853. if (L'.' == *PathCharacter)
  854. {
  855. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  856. goto Exit;
  857. }
  858. PathCharacter++;
  859. }
  860. // Set the computer name length
  861. UnicodeComputerName.Length = UnicodeComputerName.MaximumLength
  862. = (USHORT) ((PCHAR)PathCharacter - (PCHAR)UnicodeComputerName.Buffer);
  863. // Fail if the computer name exceeded the length of the input ServerFileName,
  864. // or if the length exceeds that allowed.
  865. if (UnicodeComputerName.Length >= AvailableLength
  866. ||
  867. UnicodeComputerName.Length > MAX_COMPUTERNAME_LENGTH*sizeof(WCHAR))
  868. {
  869. goto Exit;
  870. }
  871. // Copy the computer name into the caller's buffer, as long as there's enough
  872. // room for the name & a terminating '\0'.
  873. if (UnicodeComputerName.Length + sizeof(WCHAR) > (MAX_COMPUTERNAME_LENGTH+1)*sizeof(WCHAR))
  874. {
  875. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  876. goto Exit;
  877. }
  878. CopyMemory(pwszComputer, UnicodeComputerName.Buffer, UnicodeComputerName.Length);
  879. pwszComputer[UnicodeComputerName.Length / sizeof(WCHAR)] = L'\0';
  880. hr = S_OK;
  881. Exit:
  882. RtlFreeHeap(RtlProcessHeap(), 0, ServerFileName.Buffer);
  883. return hr;
  884. }
  885. // Give a file's path & handle, determine the computer name of the server
  886. // on which that file resides (which could just be this machine).
  887. HRESULT GetServerComputerName(LPCWSTR pwszFile, HANDLE hFile, WCHAR *pwszComputer)
  888. {
  889. // pwszFile may be a local path name. Convert it into an absolute name.
  890. HRESULT hr;
  891. WCHAR wszAbsoluteName[ MAX_PATH + 1 ], *pwszFilePart;
  892. if (GetFullPathName(pwszFile, ARRAYSIZE(wszAbsoluteName), wszAbsoluteName, &pwszFilePart))
  893. {
  894. if (pwszFilePart)
  895. *pwszFilePart = 0;
  896. // Check to see if this points to a local or remote drive. Terminate
  897. // the path at the beginning of the file name, so that the path ends in
  898. // a whack. This allows GetDriveType to determine the type without being
  899. // give a root path.
  900. UINT DriveType = GetDriveType(wszAbsoluteName);
  901. if (DRIVE_REMOTE == DriveType)
  902. {
  903. // We have a remote drive (could be a UNC path or a redirected drive).
  904. hr = GetRemoteServerComputerName(wszAbsoluteName, hFile, pwszComputer);
  905. }
  906. else if (DRIVE_UNKNOWN == DriveType ||
  907. DRIVE_NO_ROOT_DIR == DriveType)
  908. {
  909. // We have an unsupported type
  910. hr = HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME);
  911. }
  912. else
  913. {
  914. // We have a path to the local machine.
  915. DWORD cchName = MAX_COMPUTERNAME_LENGTH + 1;
  916. if (!GetComputerNameW(pwszComputer, &cchName))
  917. hr = HRESULT_FROM_WIN32(GetLastError());
  918. else
  919. hr = S_OK;
  920. }
  921. }
  922. else
  923. {
  924. hr = HRESULT_FROM_WIN32(GetLastError());
  925. }
  926. return hr;
  927. }