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.

436 lines
13 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. Win9xPath.cpp
  5. Abstract:
  6. Munge a path the same was as Win9x.
  7. Much of this code was copied from Win9x:
  8. \\redrum\slm\proj\win\src\CORE\win32\KERNEL\dirutil.c
  9. \\redrum\slm\proj\win\src\CORE\win32\KERNEL\fileopcc.c
  10. Path changes:
  11. 1. Translate all / to \
  12. 2. Remove all . and .. from the path, also removes some spaces
  13. (This is really bad Win9x code)
  14. 3. Remove all spaces before a \, except spaces following a .
  15. ( "abc \xyz" -> "abc\xyz" or ". \xyz" -> ". \xyz")
  16. Notes:
  17. None
  18. History:
  19. 10/05/2000 robkenny Created
  20. 08/14/2001 robkenny Moved code inside the ShimLib namespace.
  21. --*/
  22. #include "ShimLib.h"
  23. #include "Win9xPath.h"
  24. namespace ShimLib
  25. {
  26. #define WHACK L'\\'
  27. #define SPACE L' '
  28. #define DOT L'.'
  29. #define QUESTION L'?'
  30. #define EOS L'\0'
  31. #define chNetIni L'\\'
  32. #define chDirSep L'\\'
  33. #define chDirSep2 L'/'
  34. #define chRelDir L'.'
  35. #define IsWhackWhack( lpstr ) (lpstr[0] == WHACK && lpstr[1] == WHACK)
  36. #define IsWhackWhackDotWhack( lpstr ) (lpstr[0] == WHACK && lpstr[1] == WHACK && lpstr[2] == DOT && lpstr[3] == WHACK)
  37. #define IsWhackWhackQuestionWhack( lpstr ) (lpstr[0] == WHACK && lpstr[1] == WHACK && lpstr[2] == QUESTION && lpstr[3] == WHACK)
  38. #define CopySz OverlapCpy // Must be safe for overlapping strings
  39. // Home grown version of wcscpy that works if src and dst overlap
  40. void OverlapCpy(WCHAR * dst, const WCHAR * src)
  41. {
  42. while (*dst++ = *src++)
  43. {
  44. ;
  45. }
  46. }
  47. /*** PchGetNetDir - Validates a net drive spcification and returns
  48. ** a pointer to directory portion.
  49. **
  50. ** Synopsis
  51. ** WCHAR * = PchGetNetDir (pchNetName)
  52. **
  53. ** Input:
  54. ** pchNetName - pointer to a string previously validated as
  55. ** the start of a net name (begins with \\)
  56. **
  57. ** Output:
  58. ** returns pointer to the start of the directory portion of a net path
  59. **
  60. ** Errors:
  61. ** returns NULL if the net name is invalid
  62. **
  63. ** Description:
  64. ** This function takes a name starting with \\ and confirms that
  65. ** it has one following \. It returns the position of the directory
  66. ** portion. For the string
  67. **
  68. ** \\server\share[\path[\]]
  69. **
  70. ** it returns
  71. **
  72. ** [\path[\]]
  73. */
  74. const WCHAR * PchGetNetDir (const WCHAR * pchNetName)
  75. {
  76. register const WCHAR * pch = pchNetName;
  77. // Skip starting slashes
  78. pch +=2;
  79. // Skip to first backslash
  80. for (;*pch != chNetIni; pch++) {
  81. if (*pch == EOS) {
  82. // No code required.
  83. return (NULL);
  84. }
  85. }
  86. pch++; // skip past 1st backslash
  87. // Skip to second backslash
  88. for (;(*pch != chDirSep) && (*pch != chDirSep2); pch++) {
  89. if (*pch == EOS) {
  90. // ok if share with no following \path
  91. return ((*(pch-1)==chNetIni) ? NULL : pch);
  92. }
  93. }
  94. return (pch);
  95. }
  96. /*** DwRemoveDots - Remove any dots from a path name
  97. **
  98. ** Synopsis
  99. ** DWORD DwRemoveDots (pchPath)
  100. **
  101. ** Input:
  102. ** pchPath - A path string
  103. **
  104. **
  105. ** Output:
  106. ** returns the number of double dot levels removed from front
  107. **
  108. ** Errors:
  109. ** returns dwInvalid if invalid path
  110. **
  111. ** Description:
  112. ** Removes ..\ and .\ sequences from a path string. The path
  113. ** string should not include the root drive or net name portion.
  114. ** The return value of is the number of levels removed from the
  115. ** start of the string. Levels removed from inside the string
  116. ** will not be returned. For example:
  117. **
  118. ** String Result Return
  119. **
  120. ** ..\..\dir1 dir1 2
  121. ** dir1\..\dir2 dir2 0
  122. ** dir1\..\..\dir2 dir2 1
  123. ** .\dir1 dir1 0
  124. ** dir1\.\dir2 dir1\dir2 0
  125. **
  126. ** A backslash at the start of the string will be ignored.
  127. */
  128. DWORD DwRemoveDots (WCHAR * pchPath)
  129. {
  130. BOOL fInside = FALSE;
  131. DWORD cLevel = 0;
  132. DWORD cBackup;
  133. register WCHAR * pchR;
  134. register WCHAR * pchL;
  135. // Check for invalid characters
  136. // if (!FFixPathChars(pchPath)) {
  137. // // No code required.
  138. // return dwInvalid;
  139. // }
  140. //
  141. // Skip slashes
  142. for (; *pchPath == chDirSep; pchPath++)
  143. ;
  144. pchL = pchR = pchPath;
  145. // Loop through handling each directory part
  146. while (*pchR) {
  147. // This part starts with dot. Is it one or more?
  148. if (*pchR++ == chRelDir) {
  149. for (cBackup = 0; *pchR == chRelDir; cBackup++, pchR++)
  150. ;
  151. if (cBackup) {
  152. // More than one dot. Back up the left pointer.
  153. if ((*pchR != chDirSep) && (*pchR != EOS)) {
  154. // we got a [.]+X (X != '\') might be an LFN
  155. // process this as a name
  156. goto name_processing;
  157. }
  158. // Doesn't advance for ending ..
  159. for (; *pchR == chDirSep; pchR++)
  160. ;
  161. if (fInside) {
  162. for (; cBackup; cBackup--) {
  163. if (pchL <= pchPath) {
  164. cLevel += cBackup;
  165. fInside = FALSE;
  166. break;
  167. }
  168. // Remove the previous part
  169. for (pchL -= 2; *pchL != chDirSep; pchL--) {
  170. if (pchL <= pchPath) {
  171. fInside = FALSE;
  172. pchL--;
  173. break;
  174. }
  175. }
  176. pchL++;
  177. }
  178. } else {
  179. cLevel += cBackup;
  180. }
  181. // Subtract ending backslash if not root
  182. if ((*pchR == EOS) && (pchL != pchPath))
  183. pchL--;
  184. CopySz(pchL, pchR);
  185. pchR = pchL;
  186. } else {
  187. // This part starts with one dot. Throw it away.
  188. if (*pchR != chDirSep) {
  189. // Special case "\." by converting it to ""
  190. // unless it is a root, when it becomes "\".
  191. if (*pchR == EOS) {
  192. if (pchL == pchPath)
  193. *(pchR-1) = EOS; // root
  194. else
  195. *(pchR-2) = EOS; // not root
  196. return cLevel;
  197. }
  198. // we started with a '.' and then there was no '\'
  199. // might be an LFN name
  200. goto name_processing;
  201. }
  202. pchR++;
  203. CopySz(pchL, pchR);
  204. pchR = pchL;
  205. }
  206. } else {
  207. name_processing:
  208. // This part is a name. Skip it.
  209. fInside = TRUE;
  210. for (; TRUE; pchR++) {
  211. if (*pchR == chDirSep) {
  212. if (*(pchR-1) == chRelDir) {
  213. // This name has one or more dots at the end.
  214. // Remove the last dot (NT3.5 does this).
  215. pchL = pchR-1;
  216. CopySz(pchL, pchR);
  217. pchR = pchL; // point to chDirSep again
  218. }
  219. for (; *pchR == chDirSep; pchR++)
  220. ;
  221. break;
  222. } else if (*pchR == EOS) {
  223. // Remove trailing dots.
  224. // NB Can't fall off the beginning since the first WCHAR
  225. // of the current path element was not chRelDir.
  226. for (; *(pchR-1) == chRelDir; pchR--)
  227. ;
  228. // Overstore the first trailing dot, if there is one.
  229. *pchR = EOS;
  230. break;
  231. }
  232. }
  233. pchL = pchR;
  234. }
  235. }
  236. return cLevel;
  237. }
  238. // Get the Drive portion of this path,
  239. // Either C: or \\server\disk format.
  240. const WCHAR * GetDrivePortion(const WCHAR * uncorrected)
  241. {
  242. if (uncorrected && uncorrected[0])
  243. {
  244. // Look for DOS style
  245. if (uncorrected[1] == ':')
  246. {
  247. uncorrected += 2;
  248. }
  249. // Look for UNC
  250. else if (IsWhackWhack(uncorrected))
  251. {
  252. const WCHAR * pchDir = PchGetNetDir(uncorrected);
  253. if (pchDir == NULL)
  254. {
  255. if (IsWhackWhackDotWhack(uncorrected) || IsWhackWhackQuestionWhack(uncorrected))
  256. {
  257. uncorrected += 4;
  258. }
  259. }
  260. else
  261. {
  262. uncorrected = pchDir;
  263. }
  264. }
  265. }
  266. return uncorrected;
  267. }
  268. // Remove blank directory names "abc\ \def" -> "abc\def"
  269. void RemovePreceedingBlanks(WCHAR * directoryPortion)
  270. {
  271. if (directoryPortion == NULL || directoryPortion[0] == 0)
  272. {
  273. return;
  274. }
  275. WCHAR * blank = wcschr(directoryPortion, SPACE);
  276. while (blank != NULL)
  277. {
  278. // Find the end of the spaces
  279. WCHAR * blankEnd = blank;
  280. while (*blankEnd == SPACE && *blankEnd != WHACK)
  281. {
  282. ++blankEnd;
  283. }
  284. // Do not remove spaces *after* a period
  285. BOOL bPrevCharDot = (blank > directoryPortion) && (blank[-1] == DOT);
  286. if (bPrevCharDot)
  287. {
  288. blank = blankEnd;
  289. continue;
  290. }
  291. // If the the blank is a \ then we simply move the string down
  292. if (*blankEnd == WHACK)
  293. {
  294. BOOL bPrevCharWhack = blank[-1] == WHACK;
  295. // If the previous WCHAR is a \
  296. // we remove the \ at the end of the spaces as well
  297. if (bPrevCharWhack)
  298. blankEnd += 1;
  299. CopySz(blank, blankEnd);
  300. // Note: we don't change the value of blank,
  301. // since we moved all the data to it!
  302. }
  303. else
  304. {
  305. blank = blankEnd + 1;
  306. }
  307. // Keep on truckin'
  308. blank = wcschr(blank, SPACE);
  309. }
  310. }
  311. // Win9x performs some special process on path names,
  312. // particularly they remove spaces before slashes.
  313. WCHAR * W9xPathMassageW(const WCHAR * uncorrect)
  314. {
  315. if (uncorrect == NULL)
  316. return NULL;
  317. // Make a buffer large enough for the resulting string
  318. //
  319. // We are okay using a buffer that is exactly the same size as the original,
  320. // since all changes made *reduce* the size of the string.
  321. //
  322. WCHAR * correctBuffer = StringDuplicateW(uncorrect);
  323. if (!correctBuffer)
  324. return NULL;
  325. // Convert all '/' to '\'
  326. // Win9x allows //robkenny/d as a valid UNC name
  327. for (WCHAR * whack = correctBuffer; *whack; ++whack)
  328. {
  329. if (*whack == chDirSep2)
  330. *whack = chDirSep;
  331. }
  332. // We need to skip past the drive portion of the path
  333. WCHAR * directoryPortion = (WCHAR *)GetDrivePortion(correctBuffer);
  334. // Remove blank directory names "abc\ \def" -> "abc\def"
  335. // These are remove entirely rather than just removing the spaces,
  336. // because we could end up changing "\ \abc" -> "\\abc"
  337. RemovePreceedingBlanks(directoryPortion);
  338. // DwRemoveDots is used to remove all .\ and any ..\ in the middle of a path.
  339. DWORD dwUpDirs = DwRemoveDots(directoryPortion);
  340. if (dwUpDirs > 0)
  341. {
  342. // We need to add some ..\ to the front of the directoryPortion string
  343. // This is sorta wierd, removing the dots and adding them back again.
  344. // But the DwRemoveDots routine was copied strait from Win9x, and I
  345. // didn't want to change it in any way, as to preserve all peculiarities.
  346. // So we have to add back the leading parent directories that were removed.
  347. DWORD dwLen = (dwUpDirs * 3) + wcslen(correctBuffer) + 1;
  348. WCHAR * moreCorrectBuffer = (WCHAR*)malloc(dwLen * sizeof(WCHAR));
  349. if (moreCorrectBuffer)
  350. {
  351. moreCorrectBuffer[0] = 0;
  352. // Copy any drive portion
  353. wcsncpy(moreCorrectBuffer, correctBuffer, directoryPortion - correctBuffer);
  354. // add as many "..\" as were removed by DwRemoveDots
  355. while (dwUpDirs-- > 0)
  356. {
  357. wcscat(moreCorrectBuffer, L"..\\");
  358. }
  359. // finally the remainder of the string
  360. wcscat(moreCorrectBuffer, directoryPortion);
  361. delete correctBuffer;
  362. correctBuffer = moreCorrectBuffer;
  363. }
  364. else
  365. {
  366. delete correctBuffer;
  367. correctBuffer = NULL;
  368. }
  369. }
  370. return correctBuffer;
  371. }
  372. }; // end of namespace ShimLib