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.

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