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.

494 lines
17 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. DfsPath.cpp
  5. Abstract:
  6. This is the implementation file for Dfs Shell path handling modules for the Dfs Shell
  7. Extension object.
  8. Author:
  9. Environment:
  10. NT only.
  11. --*/
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <ntioapi.h>
  15. #include <nturtl.h>
  16. #include <windef.h>
  17. #include <winbase.h>
  18. #include <lmerr.h>
  19. #include <lmcons.h>
  20. #include <lmdfs.h>
  21. #include <lmapibuf.h>
  22. #include <tchar.h>
  23. #include "DfsPath.h"
  24. //--------------------------------------------------------------------------------------------
  25. //
  26. // caller of this function must call free() on *o_ppszRemotePath
  27. //
  28. HRESULT GetRemotePath(
  29. LPCTSTR i_pszPath,
  30. PTSTR *o_ppszRemotePath
  31. )
  32. {
  33. if (!i_pszPath || !*i_pszPath || !o_ppszRemotePath)
  34. return E_INVALIDARG;
  35. if (*o_ppszRemotePath)
  36. free(*o_ppszRemotePath); // to prevent memory leak
  37. UNICODE_STRING unicodePath;
  38. RtlInitUnicodeString(&unicodePath, i_pszPath);
  39. OBJECT_ATTRIBUTES ObjectAttributes;
  40. InitializeObjectAttributes(&ObjectAttributes,
  41. &unicodePath,
  42. OBJ_CASE_INSENSITIVE,
  43. NULL,
  44. NULL);
  45. HANDLE hFile = NULL;
  46. IO_STATUS_BLOCK ioStatusBlock;
  47. NTSTATUS ntStatus = NtOpenFile(&hFile,
  48. SYNCHRONIZE,
  49. &ObjectAttributes,
  50. &ioStatusBlock,
  51. FILE_SHARE_READ,
  52. FILE_DIRECTORY_FILE);
  53. if (!NT_SUCCESS(ntStatus))
  54. return HRESULT_FROM_WIN32(ntStatus);
  55. TCHAR buffer[MAX_PATH + sizeof(FILE_NAME_INFORMATION) + 1] = {0};
  56. PFILE_NAME_INFORMATION pFileNameInfo = (PFILE_NAME_INFORMATION)buffer;
  57. ntStatus = NtQueryInformationFile(hFile,
  58. &ioStatusBlock,
  59. pFileNameInfo,
  60. sizeof(buffer) - sizeof(TCHAR), // leave room for the ending '\0'
  61. FileNameInformation);
  62. NtClose(hFile);
  63. if (!NT_SUCCESS(ntStatus))
  64. {
  65. return HRESULT_FROM_WIN32(ntStatus);
  66. }
  67. UINT uiRequiredLength = (pFileNameInfo->FileNameLength / sizeof(TCHAR)) + 2; // +1 for prefix '\\', the other for the ending NULL
  68. *o_ppszRemotePath = (PTSTR)calloc(uiRequiredLength, sizeof(TCHAR));
  69. if (!*o_ppszRemotePath)
  70. return E_OUTOFMEMORY;
  71. // prepend a "\" as the Api puts only 1 "\" as in \dfsserver\dfsroot
  72. (*o_ppszRemotePath)[0] = _T('\\');
  73. RtlCopyMemory((BYTE*)&((*o_ppszRemotePath)[1]),
  74. pFileNameInfo->FileName,
  75. pFileNameInfo->FileNameLength);
  76. return S_OK;
  77. }
  78. bool IsPathWithDriveLetter(LPCTSTR pszPath)
  79. {
  80. if (!pszPath || lstrlen(pszPath) < 3)
  81. return false;
  82. if (*(pszPath+1) == _T(':') &&
  83. *(pszPath+2) == _T('\\') &&
  84. (*pszPath >= _T('a') && *pszPath <= _T('z') ||
  85. *pszPath >= _T('A') && *pszPath <= _T('Z')))
  86. return true;
  87. return false;
  88. }
  89. HRESULT ResolveDfsPath(
  90. IN LPCTSTR pcszPath, // check if this UNC path is a DFS path
  91. OUT PDFS_INFO_3* ppInfo // will hold the pointer to a DFS_INFO_3
  92. )
  93. /*++
  94. Routine Description:
  95. Given a UNC path, detect whether it is a DFS path.
  96. Return info on the last leg of redirection in *ppInfo.
  97. Return value:
  98. On error or a non-DFS path, *ppInfo will be NULL.
  99. On a DFS path, *ppInfo will contain info on the last leg of redirection.
  100. The caller needs to free it via NetApiBufferFree.
  101. Algorithm:
  102. Since we are bringing up the property page of the given path, DFS should have
  103. brought in all the relevant entries into its PKT cache.
  104. Upon every success call of NetDfsGetClientInfo, we can be sure that
  105. the InputPath must start with the EntryPath in the returned structure.
  106. We can replace the portion of the InputPath with the EntryPath's active target
  107. and feed the new path to the next call of NetDfsGetClientInfo until we
  108. reach the last leg of redirection. The DFS_INFO_3 returned most recently
  109. has the info we want.
  110. Here are several examples:
  111. If (EntryPath == InputPath), the returned DFS_INFO_3 has the info we want.
  112. C:\>dfsapi getclientinfo \\products\Public\boneyard "" "" 3
  113. \products\Public\Boneyard "(null)" OK 1
  114. \\boneyard\boneyard$ active online
  115. If (InputPath > EntryPath), replace the path and continue the loop.
  116. a) If the API call fails, the DFS_INFO_3 returned previously has the info we want.
  117. C:\>dfsapi getclientinfo \\ntdev\public\release\main "" "" 3
  118. \ntdev\public\release "(null)" OK 1
  119. \\ntdev.corp.microsoft.com\release online
  120. C:\>dfsapi getclientinfo \\ntdev.corp.microsoft.com\release\main "" "" 3
  121. \ntdev.corp.microsoft.com\release "(null)" OK 2
  122. \\WINBUILDS\release active online
  123. \\WINBUILDS2\release online
  124. C:\>dfsapi getclientinfo \\WINBUILDS\release\main "" "" 3
  125. c:\public\dfsapi failed: 2662
  126. b) If the API call returns a structure where the EntryPath==ActivePath, stop,
  127. the current structure has the info we want.
  128. C:\>dfsapi getclientinfo \\products\Public\multimedia "" "" 3
  129. \products\Public "(null)" OK 1
  130. \\PRODUCTS\Public active online
  131. --*/
  132. {
  133. if (!pcszPath || !*pcszPath || !ppInfo)
  134. return E_INVALIDARG;
  135. *ppInfo = NULL;
  136. PTSTR pszDfsPath = _tcsdup(pcszPath);
  137. if (!pszDfsPath)
  138. return E_OUTOFMEMORY;
  139. //
  140. // call NetDfsGetClientInfo to find the best entry in PKT cache
  141. //
  142. HRESULT hr = S_OK;
  143. BOOL bOneWhack = TRUE; // true if EntryPath starts with 1 whack
  144. DFS_INFO_3* pDfsInfo3 = NULL;
  145. DFS_INFO_3* pBuffer = NULL;
  146. while (NERR_Success == NetDfsGetClientInfo(pszDfsPath, NULL, NULL, 3, (LPBYTE *)&pBuffer))
  147. {
  148. _ASSERT(pBuffer->EntryPath);
  149. _ASSERT(lstrlen(pBuffer->EntryPath) > 1);
  150. bOneWhack = (_T('\\') == *(pBuffer->EntryPath) &&
  151. _T('\\') != *(pBuffer->EntryPath + 1));
  152. //
  153. // find the active target of this entry, we need it to resolve the rest of path
  154. //
  155. PTSTR pszActiveServerName = NULL;
  156. PTSTR pszActiveShareName = NULL;
  157. if (pBuffer->NumberOfStorages == 1)
  158. {
  159. pszActiveServerName = pBuffer->Storage[0].ServerName;
  160. pszActiveShareName = pBuffer->Storage[0].ShareName;
  161. }
  162. else
  163. {
  164. for (DWORD i = 0; i < pBuffer->NumberOfStorages; i++)
  165. {
  166. if (pBuffer->Storage[i].State & DFS_STORAGE_STATE_ACTIVE)
  167. {
  168. pszActiveServerName = pBuffer->Storage[i].ServerName;
  169. pszActiveShareName = pBuffer->Storage[i].ShareName;
  170. break;
  171. }
  172. }
  173. if (!pszActiveServerName)
  174. {
  175. hr = E_FAIL; // active target is missing, error out
  176. break;
  177. }
  178. }
  179. //
  180. // An entry is found, record its info.
  181. //
  182. if (pDfsInfo3)
  183. NetApiBufferFree(pDfsInfo3);
  184. pDfsInfo3 = pBuffer;
  185. pBuffer = NULL;
  186. //
  187. // When the entry path matches its active target, return the current structure
  188. //
  189. PTSTR pszActiveTarget = (PTSTR)calloc(
  190. (bOneWhack ? 1 : 2) + // prepend 1 or 2 whacks
  191. lstrlen(pszActiveServerName) +
  192. 1 + // '\\'
  193. lstrlen(pszActiveShareName) +
  194. 1, // ending '\0'
  195. sizeof(TCHAR));
  196. if (!pszActiveTarget)
  197. {
  198. hr = E_OUTOFMEMORY;
  199. break;
  200. }
  201. _stprintf(pszActiveTarget,
  202. (bOneWhack ? _T("\\%s\\%s") : _T("\\\\%s\\%s")),
  203. pszActiveServerName,
  204. pszActiveShareName);
  205. BOOL bEntryPathMatchActiveTarget = !lstrcmpi(pszActiveTarget, pDfsInfo3->EntryPath);
  206. free(pszActiveTarget);
  207. if (bEntryPathMatchActiveTarget)
  208. break; // return current pDfsInfo3
  209. //
  210. // pszDfsPath must have begun with pDfsInfo3->EntryPath.
  211. // If no extra chars left in the path, we have found the pDfsInfo3.
  212. //
  213. int nLenDfsPath = lstrlen(pszDfsPath);
  214. int nLenEntryPath = lstrlen(pDfsInfo3->EntryPath) + (bOneWhack ? 1 : 0);
  215. if (nLenDfsPath == nLenEntryPath)
  216. break;
  217. //
  218. // compose a new path which contains the active target and the rest of path.
  219. // continue to call NetDfsGetClientInfo to find the best entry for this new path.
  220. //
  221. PTSTR pszNewPath = (PTSTR)calloc(2 + lstrlen(pszActiveServerName) + 1 + lstrlen(pszActiveShareName) + nLenDfsPath - nLenEntryPath + 1, sizeof(TCHAR));
  222. if (!pszNewPath)
  223. {
  224. hr = E_OUTOFMEMORY;
  225. break;
  226. }
  227. _stprintf(pszNewPath, _T("\\\\%s\\%s%s"), pszActiveServerName, pszActiveShareName, pszDfsPath + nLenEntryPath);
  228. free(pszDfsPath);
  229. pszDfsPath = pszNewPath;
  230. } // end of while
  231. if (pszDfsPath)
  232. free(pszDfsPath);
  233. if (pBuffer)
  234. NetApiBufferFree(pBuffer);
  235. //
  236. // Fill in the output:
  237. // pDfsInfo3 will be NULL on a non-DFS path.
  238. // pDfsInfo3 will contain info on the last leg of redirection on a DFS path.
  239. // The caller needs to free it via NetApiBufferFree.
  240. //
  241. if (SUCCEEDED(hr))
  242. {
  243. *ppInfo = pDfsInfo3;
  244. }
  245. else
  246. {
  247. if (pDfsInfo3)
  248. NetApiBufferFree(pDfsInfo3);
  249. }
  250. return hr;
  251. }
  252. bool
  253. IsDfsPath
  254. (
  255. LPTSTR i_lpszDirPath,
  256. LPTSTR* o_plpszEntryPath,
  257. LPDFS_ALTERNATES** o_pppDfsAlternates
  258. )
  259. /*++
  260. Routine Description:
  261. Checks if the give directory path is a Dfs Path.
  262. If it is then it returns the largest Dfs entry path that matches
  263. this directory.
  264. Arguments:
  265. i_lpszDirPath - The directory path.
  266. o_plpszEntryPath - The largest Dfs entrypath is returned here.
  267. if the dir path is not Dfs path then this is NULL.
  268. o_pppDfsAlternates - If the path is a dfs path, then an array of pointers to the possible alternate
  269. paths are returned here.
  270. Return value:
  271. true if the path is determined to be a Dfs Path
  272. false if otherwise.
  273. --*/
  274. {
  275. if (!i_lpszDirPath || !*i_lpszDirPath || !o_pppDfsAlternates || !o_plpszEntryPath)
  276. {
  277. return(false);
  278. }
  279. *o_pppDfsAlternates = NULL;
  280. *o_plpszEntryPath = NULL;
  281. //
  282. // Convert a path to UNC format:
  283. // Local path (C:\foo) is not a DFS path, return false.
  284. // Remote path (X:\foo) needs to be converted to UNC format via NtQueryInformationFile.
  285. // Remote path already in UNC format needs no further conversion.
  286. //
  287. PTSTR lpszSharePath = NULL; // this variable will hold the path in UNC format
  288. // Is the dir path of type d:\* or \\server\share\*?
  289. if (_T('\\') == i_lpszDirPath[0])
  290. {
  291. //
  292. // This path is already in UNC format.
  293. //
  294. lpszSharePath = _tcsdup(i_lpszDirPath);
  295. if (!lpszSharePath)
  296. return false; // out of memory
  297. }
  298. else if (!IsPathWithDriveLetter(i_lpszDirPath))
  299. {
  300. return false; // unknown path format
  301. }
  302. else
  303. {
  304. //
  305. // This path starts with a drive letter. Check if it is local.
  306. //
  307. TCHAR lpszDirPath[] = _T("\\??\\C:\\");
  308. PTSTR lpszDrive = lpszDirPath + 4;
  309. // Copy the drive letter,
  310. *lpszDrive = *i_lpszDirPath;
  311. // See if it is a remote drive. If not return false.
  312. if (DRIVE_REMOTE != GetDriveType(lpszDrive))
  313. return false;
  314. //
  315. // Find UNC path behind this drive letter.
  316. //
  317. PTSTR pszRemotePath = NULL;
  318. if (FAILED(GetRemotePath(lpszDirPath, &pszRemotePath)))
  319. return false;
  320. //
  321. // Construct the full path in UNC.
  322. //
  323. lpszSharePath = (PTSTR)calloc(lstrlen(pszRemotePath) + lstrlen(i_lpszDirPath), sizeof(TCHAR));
  324. if (!lpszSharePath)
  325. {
  326. free(pszRemotePath);
  327. return false; // out of memory
  328. }
  329. _stprintf(lpszSharePath, _T("%s%s"), pszRemotePath,
  330. (pszRemotePath[lstrlen(pszRemotePath) - 1] == _T('\\') ? i_lpszDirPath + 3 : i_lpszDirPath + 2));
  331. free(pszRemotePath);
  332. }
  333. //
  334. // Check if this UNC is a DFS path. If it is, pDfsInfo3 will contain info of
  335. // the last leg of redirection.
  336. //
  337. bool bIsDfsPath = false;
  338. DFS_INFO_3* pDfsInfo3 = NULL;
  339. HRESULT hr = ResolveDfsPath(lpszSharePath, &pDfsInfo3);
  340. if (SUCCEEDED(hr) && pDfsInfo3)
  341. {
  342. _ASSERT(pDfsInfo3->EntryPath);
  343. _ASSERT(lstrlen(pDfsInfo3->EntryPath) > 1);
  344. BOOL bOneWhack = (_T('\\') == *(pDfsInfo3->EntryPath) &&
  345. _T('\\') != *(pDfsInfo3->EntryPath + 1));
  346. do
  347. {
  348. //
  349. // This is a DFS path, output the entry path.
  350. //
  351. *o_plpszEntryPath = new TCHAR [(bOneWhack ? 1 : 0) + // prepend an extra whack
  352. _tcslen(pDfsInfo3->EntryPath) +
  353. 1]; // ending '\0'
  354. if (!*o_plpszEntryPath)
  355. {
  356. break;
  357. }
  358. _stprintf(*o_plpszEntryPath,
  359. (bOneWhack ? _T("\\%s") : _T("%s")),
  360. pDfsInfo3->EntryPath);
  361. // Allocate null terminated array for alternate pointers.
  362. *o_pppDfsAlternates = new LPDFS_ALTERNATES[pDfsInfo3->NumberOfStorages + 1];
  363. if (!*o_pppDfsAlternates)
  364. {
  365. delete[] *o_plpszEntryPath;
  366. *o_plpszEntryPath = NULL;
  367. break;
  368. }
  369. (*o_pppDfsAlternates)[pDfsInfo3->NumberOfStorages] = NULL;
  370. // Allocate space for each alternate.
  371. DWORD i = 0;
  372. for (i = 0; i < pDfsInfo3->NumberOfStorages; i++)
  373. {
  374. (*o_pppDfsAlternates)[i] = new DFS_ALTERNATES;
  375. if (NULL == (*o_pppDfsAlternates)[i])
  376. {
  377. for(int j = i-1; j >= 0; j--)
  378. delete (*o_pppDfsAlternates)[j];
  379. delete[] *o_pppDfsAlternates;
  380. *o_pppDfsAlternates = NULL;
  381. delete[] *o_plpszEntryPath;
  382. *o_plpszEntryPath = NULL;
  383. break;
  384. }
  385. }
  386. if (i < pDfsInfo3->NumberOfStorages)
  387. break;
  388. // Copy alternate paths.
  389. for (i = 0; i < pDfsInfo3->NumberOfStorages; i++)
  390. {
  391. (*o_pppDfsAlternates)[i]->bstrServer = (pDfsInfo3->Storage[i]).ServerName;
  392. (*o_pppDfsAlternates)[i]->bstrShare = (pDfsInfo3->Storage[i]).ShareName;
  393. (*o_pppDfsAlternates)[i]->bstrAlternatePath = _T("\\\\");
  394. (*o_pppDfsAlternates)[i]->bstrAlternatePath += (pDfsInfo3->Storage[i]).ServerName;
  395. (*o_pppDfsAlternates)[i]->bstrAlternatePath += _T("\\");
  396. (*o_pppDfsAlternates)[i]->bstrAlternatePath += (pDfsInfo3->Storage[i]).ShareName;
  397. // Set replica state.
  398. if ((pDfsInfo3->Storage[i]).State & DFS_STORAGE_STATE_ACTIVE)
  399. {
  400. (*o_pppDfsAlternates)[i]->ReplicaState = SHL_DFS_REPLICA_STATE_ACTIVE_UNKNOWN;
  401. }
  402. }
  403. bIsDfsPath = true;
  404. } while(false);
  405. NetApiBufferFree(pDfsInfo3);
  406. }
  407. if (lpszSharePath)
  408. free(lpszSharePath);
  409. return bIsDfsPath;
  410. }
  411. //----------------------------------------------------------------------------------