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.

461 lines
13 KiB

  1. /*
  2. * canon.c - Canonical path manipulation module.
  3. */
  4. /* Headers
  5. **********/
  6. #include "project.h"
  7. #pragma hdrstop
  8. /***************************** Private Functions *****************************/
  9. /* Module Prototypes
  10. ********************/
  11. PRIVATE_CODE BOOL GetCNRInfoForDevice(LPTSTR, LPTSTR, PDWORD, PDWORD);
  12. PRIVATE_CODE BOOL GetDrivePathInfo(LPTSTR, PDWORD, LPTSTR, LPTSTR *);
  13. PRIVATE_CODE BOOL GetRemotePathInfo(LPTSTR, PDWORD, LPTSTR, LPTSTR *);
  14. PRIVATE_CODE void CanonicalizeTrailingSlash(LPTSTR);
  15. #ifdef DEBUG
  16. PRIVATE_CODE BOOL CheckFullPathInfo(LPCTSTR, PDWORD, LPCTSTR, LPCTSTR *);
  17. #endif
  18. /*
  19. ** GetCNRInfoForDevice()
  20. **
  21. **
  22. **
  23. ** Arguments:
  24. **
  25. ** Returns: BOOL
  26. **
  27. ** Side Effects: none
  28. */
  29. PRIVATE_CODE BOOL GetCNRInfoForDevice(LPTSTR pszDeviceName, LPTSTR pszNameBuf,
  30. PDWORD pdwcbLen, PDWORD pdwOutFlags)
  31. {
  32. DWORD dwNetResult;
  33. BOOL bResult;
  34. /* "X:" + null terminator */
  35. TCHAR rgchDrive[2 + 1];
  36. ASSERT(IS_VALID_STRING_PTR(pszDeviceName, CSTR));
  37. ASSERT(IS_VALID_WRITE_PTR(pdwcbLen, DWORD));
  38. ASSERT(*pdwcbLen > 0);
  39. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszNameBuf, TCHAR, (UINT)(*pdwcbLen)));
  40. ASSERT(IS_VALID_WRITE_PTR(pdwOutFlags, DWORD));
  41. /* WNetGetConnection requires the device name to have no trailing
  42. ** backslash.
  43. */
  44. MyLStrCpyN(rgchDrive, pszDeviceName, ARRAYSIZE(rgchDrive));
  45. dwNetResult = WNetGetConnection(rgchDrive, pszNameBuf, pdwcbLen);
  46. switch (dwNetResult)
  47. {
  48. case NO_ERROR:
  49. *pdwOutFlags = GCPI_OFL_REMOTE;
  50. bResult = TRUE;
  51. TRACE_OUT((TEXT("GetCNRInfoForDevice(): %s is redirected to net resource \"%s\"."),
  52. pszDeviceName,
  53. pszNameBuf));
  54. break;
  55. case ERROR_NOT_CONNECTED:
  56. *pdwOutFlags = 0;
  57. bResult = TRUE;
  58. TRACE_OUT((TEXT("GetCNRInfoForDevice(): %s is not redirected."),
  59. pszDeviceName));
  60. break;
  61. default:
  62. WARNING_OUT((TEXT("GetCNRInfoForDevice(): WNetGetConnection() on %s returned %lu."),
  63. pszDeviceName,
  64. dwNetResult));
  65. bResult = FALSE;
  66. break;
  67. }
  68. ASSERT(! bResult ||
  69. FLAGS_ARE_VALID(*pdwOutFlags, ALL_GCPI_OFLAGS) &&
  70. (IS_FLAG_CLEAR(*pdwOutFlags, GCPI_OFL_REMOTE) ||
  71. IsValidCNRName(pszNameBuf)));
  72. return(bResult);
  73. }
  74. /*
  75. ** GetDrivePathInfo()
  76. **
  77. **
  78. **
  79. ** Arguments:
  80. **
  81. ** Returns:
  82. **
  83. ** Side Effects: none
  84. */
  85. PRIVATE_CODE BOOL GetDrivePathInfo(LPTSTR pszDrivePath, PDWORD pdwOutFlags,
  86. LPTSTR pszNetResourceNameBuf,
  87. LPTSTR *ppszRootPathSuffix)
  88. {
  89. BOOL bResult;
  90. /* "X:\" + null terminator. */
  91. TCHAR rgchDriveRootPath[3 + 1];
  92. ASSERT(IsDrivePath(pszDrivePath));
  93. ASSERT(IS_VALID_WRITE_PTR(pdwOutFlags, DWORD));
  94. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszNetResourceNameBuf, STR, MAX_PATH_LEN));
  95. ASSERT(IS_VALID_WRITE_PTR(ppszRootPathSuffix, LPTSTR));
  96. ASSERT(lstrlen(pszDrivePath) >= 3);
  97. *pdwOutFlags = 0;
  98. MyLStrCpyN(rgchDriveRootPath, pszDrivePath, ARRAYSIZE(rgchDriveRootPath));
  99. ASSERT(IsDriveRootPath(rgchDriveRootPath));
  100. /* Do we need to get the CNR name for this drive path? */
  101. if (GetDriveType(rgchDriveRootPath) != DRIVE_REMOTE)
  102. /* No. */
  103. bResult = TRUE;
  104. else
  105. {
  106. DWORD dwcbBufLen = MAX_PATH_LEN;
  107. /* Yes. */
  108. bResult = GetCNRInfoForDevice(rgchDriveRootPath, pszNetResourceNameBuf,
  109. &dwcbBufLen, pdwOutFlags);
  110. }
  111. *ppszRootPathSuffix = pszDrivePath + 3;
  112. ASSERT(! bResult ||
  113. CheckFullPathInfo(pszDrivePath, pdwOutFlags, pszNetResourceNameBuf,
  114. ppszRootPathSuffix));
  115. return(bResult);
  116. }
  117. /*
  118. ** GetRemotePathInfo()
  119. **
  120. **
  121. **
  122. ** Arguments:
  123. **
  124. ** Returns:
  125. **
  126. ** Side Effects: none
  127. */
  128. PRIVATE_CODE BOOL GetRemotePathInfo(LPTSTR pszRemotePath, PDWORD pdwOutFlags,
  129. LPTSTR pszNetResourceNameBuf,
  130. LPTSTR *ppszRootPathSuffix)
  131. {
  132. BOOL bResult;
  133. ASSERT(IsFullPath(pszRemotePath));
  134. ASSERT(IS_VALID_WRITE_PTR(pdwOutFlags, DWORD));
  135. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszNetResourceNameBuf, STR, MAX_PATH_LEN));
  136. ASSERT(IS_VALID_WRITE_PTR(ppszRootPathSuffix, LPTSTR));
  137. /* Is this a "\\server\share" name? */
  138. bResult = IsUNCPath(pszRemotePath);
  139. if (bResult)
  140. {
  141. LPTSTR psz;
  142. *pdwOutFlags = 0;
  143. /*
  144. * Yes. Skip two leading slashes and look for end of \\server\share
  145. * specification.
  146. */
  147. /* Assume (as above) that a slash cannot be a DBCS lead byte. */
  148. for (psz = pszRemotePath + 2; ! IS_SLASH(*psz); psz = CharNext(psz))
  149. ASSERT(*psz);
  150. ASSERT(IS_SLASH(*psz));
  151. /*
  152. * Found first slash after double slash. Find end of string or next
  153. * slash as end of root specification.
  154. */
  155. for (psz = CharNext(psz); *psz; psz = CharNext(psz))
  156. {
  157. if (IS_SLASH(*psz))
  158. break;
  159. }
  160. ASSERT(psz >= pszRemotePath);
  161. /* Add trailing slash for UNC root path. */
  162. if (! *psz)
  163. {
  164. *psz = SLASH;
  165. *(psz + 1) = TEXT('\0');
  166. }
  167. *ppszRootPathSuffix = (LPTSTR)psz + 1;
  168. ASSERT(! IS_SLASH(**ppszRootPathSuffix));
  169. /* (+ 1) for null terminator. */
  170. MyLStrCpyN(pszNetResourceNameBuf, pszRemotePath, (int)(psz - pszRemotePath + 1));
  171. CharUpper(pszNetResourceNameBuf);
  172. SET_FLAG(*pdwOutFlags, GCPI_OFL_REMOTE);
  173. bResult = TRUE;
  174. }
  175. else
  176. /* Not a UNC path. */
  177. SetLastError(ERROR_BAD_PATHNAME);
  178. ASSERT(! bResult ||
  179. CheckFullPathInfo(pszRemotePath, pdwOutFlags, pszNetResourceNameBuf,
  180. ppszRootPathSuffix));
  181. return(bResult);
  182. }
  183. /*
  184. ** CanonicalizeTrailingSlash()
  185. **
  186. **
  187. **
  188. ** Arguments:
  189. **
  190. ** Returns:
  191. **
  192. ** Side Effects: none
  193. */
  194. PRIVATE_CODE void CanonicalizeTrailingSlash(LPTSTR pszRootPathSuffix)
  195. {
  196. LPTSTR pszLast;
  197. ASSERT(IS_VALID_STRING_PTR(pszRootPathSuffix, STR));
  198. ASSERT(! IS_SLASH(*pszRootPathSuffix));
  199. /* No path suffix should end in a slash. */
  200. pszLast = CharPrev(pszRootPathSuffix,
  201. pszRootPathSuffix + lstrlen(pszRootPathSuffix));
  202. if (IS_SLASH(*pszLast))
  203. *pszLast = TEXT('\0');
  204. ASSERT(IsValidPathSuffix(pszRootPathSuffix));
  205. return;
  206. }
  207. #ifdef DEBUG
  208. /*
  209. ** CheckFullPathInfo()
  210. **
  211. **
  212. **
  213. ** Arguments:
  214. **
  215. ** Returns:
  216. **
  217. ** Side Effects: none
  218. */
  219. PRIVATE_CODE BOOL CheckFullPathInfo(LPCTSTR pcszFullPath,
  220. PDWORD pdwOutFlags,
  221. LPCTSTR pcszNetResourceName,
  222. LPCTSTR *ppcszRootPathSuffix)
  223. {
  224. return(EVAL(IsFullPath(pcszFullPath)) &&
  225. FLAGS_ARE_VALID(*pdwOutFlags, ALL_GCPI_OFLAGS) &&
  226. (IS_FLAG_CLEAR(*pdwOutFlags, GCPI_OFL_REMOTE) ||
  227. (EVAL(IsValidCNRName(pcszNetResourceName)) &&
  228. EVAL(lstrlen(pcszNetResourceName) < MAX_PATH_LEN))) &&
  229. (IS_FLAG_SET(*pdwOutFlags, GCPI_OFL_REMOTE) ||
  230. EVAL(IsLocalDrivePath(pcszFullPath))) &&
  231. IS_VALID_STRING_PTR(*ppcszRootPathSuffix, CSTR) &&
  232. EVAL(IsStringContained(pcszFullPath, *ppcszRootPathSuffix)));
  233. }
  234. #endif
  235. /***************************** Exported Functions ****************************/
  236. /******************************************************************************
  237. @doc LINKINFOAPI
  238. @func BOOL | GetCanonicalPathInfo | Retrieves information about the canonical
  239. form of a path.
  240. @parm PCSTR | pcszPath | A pointer to the path string whose canonical form
  241. information is to be retrieved.
  242. @parm PSTR | pszCanonicalBuf | A pointer to a buffer to be filled in with the
  243. full canonical form of the path. This buffer must be at least MAX_PATH_LEN
  244. bytes long.
  245. @parm PDWORD | pdwOutFlags | A pointer to a DWORD bit mask of flags to be
  246. filled in with flags from the <t GETCANONICALPATHINFOOUTFLAGS> enumeration.
  247. @parm PSTR | pszNetResourceNameBuf | A pointer to a buffer to be filled in with
  248. the name of the net resource parent of the path. This buffer must be at least
  249. MAX_PATH_LEN bytes long. This buffer is only filled in if GCPI_OFL_REMOTE is
  250. set in *pdwOutFlags.
  251. @parm PSTR * | ppszRootPathSuffix | A pointer to a PSTR to be filled in with a
  252. pointer to the file system root path suffix, not including the leading slash,
  253. of the canonical path in pszCanonicalBuf's buffer.
  254. @rdesc If the function completed successfully, TRUE is returned. Otherwise,
  255. FALSE is returned. The reason for failure may be determined by calling
  256. GetLastError().
  257. ******************************************************************************/
  258. LINKINFOAPI BOOL WINAPI GetCanonicalPathInfo(LPCTSTR pcszPath,
  259. LPTSTR pszCanonicalBuf,
  260. PDWORD pdwOutFlags,
  261. LPTSTR pszNetResourceNameBuf,
  262. LPTSTR *ppszRootPathSuffix)
  263. {
  264. BOOL bResult;
  265. LPTSTR pszFileName;
  266. DWORD dwPathLen;
  267. ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
  268. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszCanonicalBuf, STR, MAX_PATH_LEN));
  269. ASSERT(IS_VALID_WRITE_PTR(pdwOutFlags, DWORD));
  270. ASSERT(IS_VALID_WRITE_PTR(ppszRootPathSuffix, LPTSTR));
  271. dwPathLen = GetFullPathName(pcszPath, MAX_PATH_LEN, pszCanonicalBuf,
  272. &pszFileName);
  273. if (dwPathLen > 0 && dwPathLen < MAX_PATH_LEN)
  274. {
  275. /*
  276. * Assume that GetFullPathName() changed all back slashes ('/') to
  277. * forward slashes ('\\').
  278. */
  279. ASSERT(! MyStrChr(pszCanonicalBuf, TEXT('/'), NULL));
  280. if (IsDrivePath(pszCanonicalBuf))
  281. bResult = GetDrivePathInfo(pszCanonicalBuf, pdwOutFlags,
  282. pszNetResourceNameBuf,
  283. ppszRootPathSuffix);
  284. else
  285. bResult = GetRemotePathInfo(pszCanonicalBuf, pdwOutFlags,
  286. pszNetResourceNameBuf,
  287. ppszRootPathSuffix);
  288. if (bResult)
  289. CanonicalizeTrailingSlash(*ppszRootPathSuffix);
  290. }
  291. else
  292. {
  293. // BOGUS ASSERT: We can also get here if the resulting full path
  294. // is bigger than MAX_PATH_LEN.
  295. // ASSERT(! dwPathLen);
  296. WARNING_OUT((TEXT("GetFullPathName() failed on path %s, returning %lu."),
  297. pcszPath,
  298. dwPathLen));
  299. bResult = FALSE;
  300. }
  301. ASSERT(! bResult ||
  302. (CheckFullPathInfo(pszCanonicalBuf, pdwOutFlags,
  303. pszNetResourceNameBuf, ppszRootPathSuffix) &&
  304. IsValidPathSuffix(*ppszRootPathSuffix)));
  305. return(bResult);
  306. }
  307. #ifdef UNICODE
  308. LINKINFOAPI BOOL WINAPI GetCanonicalPathInfoA(LPCSTR pcszPath,
  309. LPSTR pszCanonicalBuf,
  310. PDWORD pdwOutFlags,
  311. LPSTR pszNetResourceNameBuf,
  312. LPSTR *ppszRootPathSuffix)
  313. {
  314. LPWSTR pcszWidePath;
  315. UINT cchPath;
  316. WCHAR szWideCanonicalBuf[MAX_PATH];
  317. WCHAR szWideNetResourceNameBuf[MAX_PATH];
  318. LPWSTR pszWideRootPathSuffix;
  319. UINT_PTR chOffset;
  320. BOOL fCanonical;
  321. cchPath = lstrlenA(pcszPath) + 1;
  322. pcszWidePath = (LPWSTR)_alloca(cchPath*SIZEOF(WCHAR));
  323. if ( MultiByteToWideChar( CP_ACP, 0,
  324. pcszPath, cchPath,
  325. pcszWidePath, cchPath) == 0)
  326. {
  327. return FALSE;
  328. }
  329. fCanonical = GetCanonicalPathInfo( pcszWidePath,
  330. szWideCanonicalBuf,
  331. pdwOutFlags,
  332. szWideNetResourceNameBuf,
  333. &pszWideRootPathSuffix );
  334. if ( fCanonical )
  335. {
  336. if ( WideCharToMultiByte( CP_ACP, 0,
  337. szWideCanonicalBuf, -1,
  338. pszCanonicalBuf, MAX_PATH,
  339. NULL, NULL ) == 0)
  340. {
  341. return FALSE;
  342. }
  343. if ( *pdwOutFlags & GCPI_OFL_REMOTE )
  344. {
  345. if ( WideCharToMultiByte( CP_ACP, 0,
  346. szWideNetResourceNameBuf, -1,
  347. pszNetResourceNameBuf, MAX_PATH,
  348. NULL, NULL ) == 0)
  349. {
  350. return FALSE;
  351. }
  352. }
  353. chOffset = pszWideRootPathSuffix - szWideCanonicalBuf;
  354. *ppszRootPathSuffix = pszCanonicalBuf;
  355. while ( chOffset-- )
  356. {
  357. *ppszRootPathSuffix = CharNextA(*ppszRootPathSuffix);
  358. }
  359. }
  360. return(fCanonical);
  361. }
  362. #endif