Changeset 51250 in vbox
- Timestamp:
- May 14, 2014 8:11:22 AM (11 years ago)
- svn:sync-xref-src-repo-rev:
- 93688
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/VBox/shflsvc.h
r51015 r51250 205 205 206 206 /** 207 * Validates a HGCM string parameter.207 * Validates a HGCM string output parameter. 208 208 * 209 209 * @returns true if valid, false if not. … … 212 212 * @param cbBuf The buffer size from the parameter. 213 213 */ 214 DECLINLINE(bool) ShflStringIsValid (PCSHFLSTRING pString, uint32_t cbBuf)214 DECLINLINE(bool) ShflStringIsValidOut(PCSHFLSTRING pString, uint32_t cbBuf) 215 215 { 216 216 if (RT_UNLIKELY(cbBuf <= RT_UOFFSETOF(SHFLSTRING, String))) … … 220 220 if (RT_UNLIKELY(pString->u16Length >= pString->u16Size)) 221 221 return false; 222 /** @todo r=bird: Check that u16Length is a multiple of two if UTF-16 input? */223 /** @todo r=bird: Do we require the string to have a NUL terminator char, if224 * so check for it!! (Just had a problem with too small (/2) u16Length225 * and code behaving incorrectly because it worked up to the terminator226 * instead of the length.) */227 /** @todo r=bird: Who checks for valid UTF-8 encoding of strings? */228 222 return true; 229 223 } 230 224 231 225 /** 232 * Validates an optional HGCM string parameter. 226 * Validates a HGCM string input parameter. 227 * 228 * @returns true if valid, false if not. 229 * 230 * @param pString The string buffer pointer. 231 * @param cbBuf The buffer size from the parameter. 232 * @param fUtf8Not16 Set if UTF-8 encoding, clear if UTF-16 encoding. 233 */ 234 DECLINLINE(bool) ShflStringIsValidIn(PCSHFLSTRING pString, uint32_t cbBuf, bool fUtf8Not16) 235 { 236 int rc; 237 if (RT_UNLIKELY(cbBuf <= RT_UOFFSETOF(SHFLSTRING, String))) 238 return false; 239 if (RT_UNLIKELY((uint32_t)pString->u16Size + RT_UOFFSETOF(SHFLSTRING, String) > cbBuf)) 240 return false; 241 if (fUtf8Not16) 242 { 243 /* UTF-8: */ 244 if (RT_UNLIKELY(pString->u16Length >= pString->u16Size)) 245 return false; 246 rc = RTStrValidateEncodingEx((const char *)&pString->String.utf8[0], pString->u16Length + 1, 247 RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED); 248 } 249 else 250 { 251 /* UTF-16: */ 252 if (RT_UNLIKELY(pString->u16Length & 1)) 253 return false; 254 if (RT_UNLIKELY((uint32_t)sizeof(RTUTF16) + pString->u16Length > pString->u16Size)) 255 return false; 256 rc = RTUtf16ValidateEncodingEx(&pString->String.ucs2[0], pString->u16Length / 2 + 1, 257 RTSTR_VALIDATE_ENCODING_EXACT_LENGTH | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED); 258 } 259 if (RT_FAILURE(rc)) 260 return false; 261 return true; 262 } 263 264 /** 265 * Validates an optional HGCM string input parameter. 233 266 * 234 267 * @returns true if valid, false if not. … … 236 269 * @param pString The string buffer pointer. Can be NULL. 237 270 * @param cbBuf The buffer size from the parameter. 238 */ 239 DECLINLINE(bool) ShflStringIsValidOrNull(PCSHFLSTRING pString, uint32_t cbBuf) 271 * @param fUtf8Not16 Set if UTF-8 encoding, clear if UTF-16 encoding. 272 */ 273 DECLINLINE(bool) ShflStringIsValidOrNullIn(PCSHFLSTRING pString, uint32_t cbBuf, bool fUtf8Not16) 240 274 { 241 275 if (pString) 242 return ShflStringIsValid (pString, cbBuf);276 return ShflStringIsValidIn(pString, cbBuf, fUtf8Not16); 243 277 if (RT_UNLIKELY(cbBuf > 0)) 244 278 return false; -
trunk/src/VBox/HostServices/SharedFolders/mappings.cpp
r44528 r51250 535 535 #endif 536 536 int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName, 537 RTUTF16 pwszDelimiter, bool fCaseSensitive, SHFLROOT *pRoot)537 RTUTF16 wcDelimiter, bool fCaseSensitive, SHFLROOT *pRoot) 538 538 { 539 539 MAPPING *pFolderMapping = NULL; … … 548 548 } 549 549 550 AssertMsgReturn(wcDelimiter == '/' || wcDelimiter == '\\', 551 ("Invalid path delimiter: %#x\n", wcDelimiter), 552 VERR_INVALID_PARAMETER); 550 553 if (pClient->PathDelimiter == 0) 551 554 { 552 pClient->PathDelimiter = pwszDelimiter;555 pClient->PathDelimiter = wcDelimiter; 553 556 } 554 557 else 555 558 { 556 Assert(pwszDelimiter == pClient->PathDelimiter); 559 AssertMsgReturn(wcDelimiter == pClient->PathDelimiter, 560 ("wcDelimiter=%#x PathDelimiter=%#x", wcDelimiter, pClient->PathDelimiter), 561 VERR_INVALID_PARAMETER); 557 562 } 558 563 -
trunk/src/VBox/HostServices/SharedFolders/service.cpp
r44528 r51250 385 385 386 386 /* Verify parameters values. */ 387 if (!ShflStringIsValid (pString, paParms[1].u.pointer.size))387 if (!ShflStringIsValidOut(pString, paParms[1].u.pointer.size)) 388 388 { 389 389 rc = VERR_INVALID_PARAMETER; … … 431 431 432 432 /* Verify parameters values. */ 433 if ( !ShflStringIsValid (pPath, cbPath)433 if ( !ShflStringIsValidIn(pPath, cbPath, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) 434 434 || (cbParms != sizeof (SHFLCREATEPARMS)) 435 435 ) … … 442 442 { 443 443 /* Execute the function. */ 444 445 444 rc = vbsfCreate (pClient, root, pPath, cbPath, pParms); 446 445 … … 490 489 { 491 490 /* Execute the function. */ 492 493 491 rc = vbsfClose (pClient, root, Handle); 494 492 … … 762 760 if ( (length < sizeof (SHFLDIRINFO)) 763 761 || length > paParms[5].u.pointer.size 764 || !ShflStringIsValidOrNull (pPath, paParms[4].u.pointer.size)762 || !ShflStringIsValidOrNullIn(pPath, paParms[4].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) 765 763 ) 766 764 { … … 830 828 831 829 /* Verify parameters values. */ 832 if (!ShflStringIsValidOrNull (pPath, paParms[1].u.pointer.size))830 if (!ShflStringIsValidOrNullIn(pPath, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))) 833 831 { 834 832 rc = VERR_INVALID_PARAMETER; … … 875 873 876 874 /* Verify parameters values. */ 877 if (!ShflStringIsValid (pszMapName, paParms[0].u.pointer.size))875 if (!ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))) 878 876 { 879 877 rc = VERR_INVALID_PARAMETER; … … 926 924 927 925 /* Verify parameters values. */ 928 if (!ShflStringIsValid(pszMapName, paParms[0].u.pointer.size)) 929 { 930 rc = VERR_INVALID_PARAMETER; 931 } 932 else 933 { 934 935 /* Execute the function. */ 926 if (ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))) 927 { 928 rc = VINF_SUCCESS; 929 } 930 else 931 { 932 rc = VERR_INVALID_PARAMETER; 933 934 /* Fudge for windows GAs getting the length wrong by one char. */ 935 if ( !(pClient->fu32Flags & SHFL_CF_UTF8) 936 && paParms[0].u.pointer.size >= sizeof(SHFLSTRING) 937 && pszMapName->u16Length >= 2 938 && pszMapName->String.ucs2[pszMapName->u16Length / 2 - 1] == 0x0000) 939 { 940 pszMapName->u16Length -= 2; 941 if (ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, false /*fUtf8Not16*/)) 942 rc = VINF_SUCCESS; 943 else 944 pszMapName->u16Length += 2; 945 } 946 } 947 948 /* Execute the function. */ 949 if (RT_SUCCESS(rc)) 936 950 rc = vbsfMapFolder (pClient, pszMapName, delimiter, fCaseSensitive, &root); 937 951 938 if (RT_SUCCESS(rc)) 939 { 940 /* Update parameters.*/ 941 paParms[1].u.uint32 = root; 942 } 952 if (RT_SUCCESS(rc)) 953 { 954 /* Update parameters.*/ 955 paParms[1].u.uint32 = root; 943 956 } 944 957 } … … 1065 1078 1066 1079 /* Verify parameters values. */ 1067 if (!ShflStringIsValid (pPath, cbPath))1080 if (!ShflStringIsValidIn(pPath, cbPath, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))) 1068 1081 { 1069 1082 rc = VERR_INVALID_PARAMETER; … … 1109 1122 1110 1123 /* Verify parameters values. */ 1111 if ( !ShflStringIsValid (pSrc, paParms[1].u.pointer.size)1112 || !ShflStringIsValid (pDest, paParms[2].u.pointer.size)1124 if ( !ShflStringIsValidIn(pSrc, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) 1125 || !ShflStringIsValidIn(pDest, paParms[2].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) 1113 1126 ) 1114 1127 { … … 1209 1222 1210 1223 /* Verify parameters values. */ 1211 if ( !ShflStringIsValid (pNewPath, paParms[1].u.pointer.size)1212 || !ShflStringIsValid (pOldPath, paParms[2].u.pointer.size)1224 if ( !ShflStringIsValidIn(pNewPath, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) 1225 || !ShflStringIsValidIn(pOldPath, paParms[2].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) 1213 1226 || (cbInfo != sizeof(SHFLFSOBJINFO)) 1214 1227 ) … … 1306 1319 1307 1320 /* Verify parameters values. */ 1308 if ( !ShflStringIsValid (pFolderName, paParms[0].u.pointer.size)1309 || !ShflStringIsValid (pMapName, paParms[1].u.pointer.size)1321 if ( !ShflStringIsValidIn(pFolderName, paParms[0].u.pointer.size, false /*fUtf8Not16*/) 1322 || !ShflStringIsValidIn(pMapName, paParms[1].u.pointer.size, false /*fUtf8Not16*/) 1310 1323 ) 1311 1324 { … … 1362 1375 1363 1376 /* Verify parameters values. */ 1364 if (!ShflStringIsValid (pString, paParms[0].u.pointer.size))1377 if (!ShflStringIsValidIn(pString, paParms[0].u.pointer.size, false /*fUtf8Not16*/)) 1365 1378 { 1366 1379 rc = VERR_INVALID_PARAMETER; -
trunk/src/VBox/HostServices/SharedFolders/vbsf.cpp
r44528 r51250 107 107 } 108 108 109 /** 110 * Corrects the casing of the final component 111 * 112 * @returns 113 * @param pClient . 114 * @param pszFullPath . 115 * @param pszStartComponent . 116 */ 109 117 static int vbsfCorrectCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, char *pszStartComponent) 110 118 { 111 PRTDIRENTRYEX pDirEntry = NULL;112 uint32_t cbDirEntry, cbComponent;113 int rc = VERR_FILE_NOT_FOUND;114 PRTDIR hSearch = 0;115 char szWildCard[4];116 117 119 Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent)); 118 120 119 cbComponent = (uint32_t) strlen(pszStartComponent); 120 121 cbDirEntry = 4096; 122 pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry); 123 if (pDirEntry == 0) 124 { 125 AssertFailed(); 121 AssertReturn((uintptr_t)pszFullPath < (uintptr_t)pszStartComponent - 1U, VERR_INTERNAL_ERROR_2); 122 AssertReturn(pszStartComponent[-1] == RTPATH_DELIMITER, VERR_INTERNAL_ERROR_5); 123 124 /* 125 * Allocate a buffer that can hold really long file name entries as well as 126 * the initial search pattern. 127 */ 128 size_t cchComponent = strlen(pszStartComponent); 129 size_t cchParentDir = pszStartComponent - pszFullPath; 130 size_t cchFullPath = cchParentDir + cchComponent; 131 Assert(strlen(pszFullPath) == cchFullPath); 132 133 size_t cbDirEntry = 4096; 134 if (cchFullPath + 4 > cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName)) 135 cbDirEntry = RT_OFFSETOF(RTDIRENTRYEX, szName) + cchFullPath + 4; 136 137 PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry); 138 if (pDirEntry == NULL) 126 139 return VERR_NO_MEMORY; 127 } 128 129 /** @todo this is quite inefficient, especially for directories with many files */ 130 Assert(pszFullPath < pszStartComponent-1); 131 Assert(*(pszStartComponent-1) == RTPATH_DELIMITER); 132 *(pszStartComponent-1) = 0; 133 strcpy(pDirEntry->szName, pszFullPath); 134 szWildCard[0] = RTPATH_DELIMITER; 135 szWildCard[1] = '*'; 136 szWildCard[2] = 0; 137 strcat(pDirEntry->szName, szWildCard); 138 139 rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0); 140 *(pszStartComponent-1) = RTPATH_DELIMITER; 140 141 /* 142 * Construct the search criteria in the szName member of pDirEntry. 143 */ 144 /** @todo This is quite inefficient, especially for directories with many 145 * files. If any of the typically case sensitive host systems start 146 * supporting opendir wildcard filters, it would make sense to build 147 * one here with '?' for case foldable charaters. */ 148 /** @todo Use RTDirOpen here and drop the whole uncessary path copying? */ 149 int rc = RTPathJoinEx(pDirEntry->szName, cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName), 150 pszFullPath, cchParentDir, 151 RT_STR_TUPLE("*")); 152 AssertRC(rc); 153 if (RT_SUCCESS(rc)) 154 { 155 PRTDIR hSearch = NULL; 156 rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0); 157 if (RT_SUCCESS(rc)) 158 { 159 for (;;) 160 { 161 size_t cbDirEntrySize = cbDirEntry; 162 163 rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); 164 if (rc == VERR_NO_MORE_FILES) 165 break; 166 167 if ( rc != VINF_SUCCESS 168 && rc != VWRN_NO_DIRENT_INFO) 169 { 170 AssertFailed(); 171 if ( rc == VERR_NO_TRANSLATION 172 || rc == VERR_INVALID_UTF8_ENCODING) 173 continue; 174 break; 175 } 176 177 Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0])); 178 if ( pDirEntry->cbName == cchComponent 179 && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0])) 180 { 181 Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent)); 182 strcpy(pszStartComponent, &pDirEntry->szName[0]); 183 rc = VINF_SUCCESS; 184 break; 185 } 186 } 187 188 RTDirClose(hSearch); 189 } 190 } 191 141 192 if (RT_FAILURE(rc)) 142 goto end; 143 144 for (;;) 145 { 146 size_t cbDirEntrySize = cbDirEntry; 147 148 rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); 149 if (rc == VERR_NO_MORE_FILES) 150 break; 151 152 if ( rc != VINF_SUCCESS 153 && rc != VWRN_NO_DIRENT_INFO) 154 { 155 AssertFailed(); 156 if ( rc == VERR_NO_TRANSLATION 157 || rc == VERR_INVALID_UTF8_ENCODING) 158 continue; 159 break; 160 } 161 162 Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0])); 163 if ( pDirEntry->cbName == cbComponent 164 && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0])) 165 { 166 Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent)); 167 strcpy(pszStartComponent, &pDirEntry->szName[0]); 168 rc = VINF_SUCCESS; 169 break; 170 } 171 } 172 173 end: 174 if (RT_FAILURE(rc)) 175 Log(("vbsfCorrectCasing %s failed with %d\n", pszStartComponent, rc)); 176 177 if (pDirEntry) 178 RTMemFree(pDirEntry); 179 180 if (hSearch) 181 RTDirClose(hSearch); 193 Log(("vbsfCorrectCasing %s failed with %Rrc\n", pszStartComponent, rc)); 194 195 RTMemFree(pDirEntry); 196 182 197 return rc; 183 198 } 199 200 /* Temporary stand-in for RTPathExistEx. */ 201 static int vbsfQueryExistsEx(const char *pszPath, uint32_t fFlags) 202 { 203 #if 0 /** @todo Fix the symlink issue on windows! */ 204 return RTPathExistsEx(pszPath, fFlags); 205 #else 206 RTFSOBJINFO IgnInfo; 207 return RTPathQueryInfoEx(pszPath, &IgnInfo, RTFSOBJATTRADD_NOTHING, fFlags); 208 #endif 209 } 210 211 /** 212 * Helper for vbsfBuildFullPath that performs case corrections on the path 213 * that's being build. 214 * 215 * @returns VINF_SUCCESS at the moment. 216 * @param pClient The client data. 217 * @param pszFullPath Pointer to the full path. This is the path 218 * which may need case corrections. The 219 * corrections will be applied in place. 220 * @param cchFullPath The length of the full path. 221 * @param fWildCard Whether the last component may contain 222 * wildcards and thus might require exclusion 223 * from the case correction. 224 * @param fPreserveLastComponent Always exclude the last component from case 225 * correction if set. 226 */ 227 static int vbsfCorrectPathCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, size_t cchFullPath, 228 bool fWildCard, bool fPreserveLastComponent) 229 { 230 /* 231 * Hide the last path component if it needs preserving. This is required 232 * in the following cases: 233 * - Contains the wildcard(s). 234 * - Is a 'rename' target. 235 */ 236 char *pszLastComponent = NULL; 237 if (fWildCard || fPreserveLastComponent) 238 { 239 char *pszSrc = pszFullPath + cchFullPath - 1; 240 Assert(strchr(pszFullPath, '\0') == pszSrc + 1); 241 while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath) 242 { 243 if (*pszSrc == RTPATH_DELIMITER) 244 break; 245 pszSrc--; 246 } 247 if (*pszSrc == RTPATH_DELIMITER) 248 { 249 if ( fPreserveLastComponent 250 /* Or does it really have wildcards? */ 251 || strchr(pszSrc + 1, '*') != NULL 252 || strchr(pszSrc + 1, '?') != NULL 253 || strchr(pszSrc + 1, '>') != NULL 254 || strchr(pszSrc + 1, '<') != NULL 255 || strchr(pszSrc + 1, '"') != NULL ) 256 { 257 pszLastComponent = pszSrc; 258 *pszLastComponent = '\0'; 259 } 260 } 261 } 262 263 /* 264 * If the path/file doesn't exist, we need to attempt case correcting it. 265 */ 266 /** @todo Don't check when creating files or directories; waste of time. */ 267 int rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient)); 268 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) 269 { 270 Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath)); 271 272 /* 273 * Work from the end of the path to find a partial path that's valid. 274 */ 275 char *pszSrc = pszLastComponent ? pszLastComponent - 1 : pszFullPath + cchFullPath - 1; 276 Assert(strchr(pszFullPath, '\0') == pszSrc + 1); 277 278 while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath) 279 { 280 if (*pszSrc == RTPATH_DELIMITER) 281 { 282 *pszSrc = '\0'; 283 rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient)); 284 *pszSrc = RTPATH_DELIMITER; 285 if (RT_SUCCESS(rc)) 286 { 287 #ifdef DEBUG 288 *pszSrc = '\0'; 289 Log(("Found valid partial path %s\n", pszFullPath)); 290 *pszSrc = RTPATH_DELIMITER; 291 #endif 292 break; 293 } 294 } 295 296 pszSrc--; 297 } 298 Assert(*pszSrc == RTPATH_DELIMITER && RT_SUCCESS(rc)); 299 if ( *pszSrc == RTPATH_DELIMITER 300 && RT_SUCCESS(rc)) 301 { 302 /* 303 * Turn around and work the other way case correcting the components. 304 */ 305 pszSrc++; 306 for (;;) 307 { 308 bool fEndOfString = true; 309 310 /* Find the end of the component. */ 311 char *pszEnd = pszSrc; 312 while (*pszEnd) 313 { 314 if (*pszEnd == RTPATH_DELIMITER) 315 break; 316 pszEnd++; 317 } 318 319 if (*pszEnd == RTPATH_DELIMITER) 320 { 321 fEndOfString = false; 322 *pszEnd = '\0'; 323 #if 0 /** @todo Please, double check this. The original code is in the #if 0, what I hold as correct is in the #else. */ 324 rc = RTPathQueryInfoEx(pszSrc, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); 325 #else 326 rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient)); 327 #endif 328 Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND); 329 } 330 else if (pszEnd == pszSrc) 331 rc = VINF_SUCCESS; /* trailing delimiter */ 332 else 333 rc = VERR_FILE_NOT_FOUND; 334 335 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) 336 { 337 /* Path component is invalid; try to correct the casing. */ 338 rc = vbsfCorrectCasing(pClient, pszFullPath, pszSrc); 339 if (RT_FAILURE(rc)) 340 { 341 /* Failed, so don't bother trying any further components. */ 342 if (!fEndOfString) 343 *pszEnd = RTPATH_DELIMITER; /* Restore the original full path. */ 344 break; 345 } 346 } 347 348 /* Next (if any). */ 349 if (fEndOfString) 350 break; 351 352 *pszEnd = RTPATH_DELIMITER; 353 pszSrc = pszEnd + 1; 354 } 355 if (RT_FAILURE(rc)) 356 Log(("Unable to find suitable component rc=%d\n", rc)); 357 } 358 else 359 rc = VERR_FILE_NOT_FOUND; 360 361 } 362 363 /* Restore the final component if it was dropped. */ 364 if (pszLastComponent) 365 *pszLastComponent = RTPATH_DELIMITER; 366 367 /* might be a new file so don't fail here! */ 368 return VINF_SUCCESS; 369 } 370 371 372 /** @def VBSF_IS_PATH_SLASH 373 * Checks if @a a_ch is a path delimiter (slash, not volume spearator) for the 374 * guest or the host. 375 * @param a_pClient Pointer to the client data (SHFLCLIENTDATA). 376 * @param a_ch The character to inspect. 377 */ 378 #if 1 379 # define VBSF_IS_PATH_SLASH(a_pClient, a_ch) ( (a_ch) == '\\' || (a_ch) == '/' || RTPATH_IS_SLASH(a_ch) ) 380 #else 381 # define VBSF_IS_PATH_SLASH(a_pClient, a_ch) ( (RTUTF16)(a_ch) == (a_pClient)->PathDelimiter || RTPATH_IS_SLASH(a_ch) ) 382 #endif 383 184 384 185 385 /** … … 193 393 * @retval VERR_INVALID_NAME 194 394 * 195 * @param pUtf8Path The path to check. 196 * @param cchPath The length of the path in chars (not code points, but 197 * the C type) excluding the string terminator. 395 * @param pszPath The (UTF-8) path to check. Slashes has been convert 396 * to host slashes by this time. 198 397 * 199 398 * @remarks This function assumes that the path will be appended to the root … … 201 400 * checking absolute paths! 202 401 */ 203 static int vbsfPathCheck(const char *pUtf8Path, size_t cchPath) 204 { 205 int rc = VINF_SUCCESS; 206 207 size_t i = 0; 402 static int vbsfPathCheck(const char *pszPath) 403 { 404 /* 405 * Walk the path, component by component and check for escapes. 406 */ 407 208 408 int cComponents = 0; /* How many normal path components. */ 209 409 int cParentDirs = 0; /* How many '..' components. */ … … 211 411 for (;;) 212 412 { 413 char ch; 414 213 415 /* Skip leading path delimiters. */ 214 while ( i < cchPath 215 && (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/')) 216 i++; 217 218 if (i >= cchPath) 219 break; 416 do 417 ch = *pszPath++; 418 while (RTPATH_IS_SLASH(ch)); 419 if (ch == '\0') 420 return VINF_SUCCESS; 220 421 221 422 /* Check if that is a dot component. */ 222 423 int cDots = 0; 223 while ( i < cchPath && pUtf8Path[i]== '.')424 while (ch == '.') 224 425 { 225 426 cDots++; 226 i++; 227 } 228 229 if ( cDots >= 2 /* Consider all multidots sequences as a 'parent dir'. */ 230 && (i >= cchPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/'))) 231 { 232 cParentDirs++; 233 } 234 else if ( cDots == 1 235 && (i >= cchPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/'))) 236 { 237 /* Single dot, nothing changes. */ 427 ch = *pszPath++; 428 } 429 430 if ( cDots >= 1 431 && (ch == '\0' || RTPATH_IS_SLASH(ch)) ) 432 { 433 if (cDots >= 2) /* Consider all multidots sequences as a 'parent dir'. */ 434 { 435 cParentDirs++; 436 437 /* Escaping? */ 438 if (cParentDirs > cComponents) 439 return VERR_INVALID_NAME; 440 } 441 /* else: Single dot, nothing changes. */ 238 442 } 239 443 else 240 444 { 241 /* Skip this component. */ 242 while ( i < cchPath 243 && (pUtf8Path[i] != '\\' && pUtf8Path[i] != '/')) 244 i++; 245 445 /* Not a dot component, skip to the end of it. */ 446 while (ch != '\0' && !RTPATH_IS_SLASH(ch)) 447 ch = *pszPath++; 246 448 cComponents++; 247 449 } 248 249 Assert(i >= cchPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/')); 250 251 /* Verify counters for every component. */ 252 if (cParentDirs > cComponents) 253 { 254 rc = VERR_INVALID_NAME; 255 break; 256 } 257 } 258 259 return rc; 450 Assert(cComponents >= cParentDirs); 451 452 /* The end? */ 453 Assert(ch == '\0' || RTPATH_IS_SLASH(ch)); 454 if (ch == '\0') 455 return VINF_SUCCESS; 456 } 260 457 } 261 458 … … 264 461 bool fWildCard = false, bool fPreserveLastComponent = false) 265 462 { 266 int rc = VINF_SUCCESS; 267 char *pszFullPath = NULL; 268 size_t cbRoot; 463 /* Resolve the root prefix into a string. */ 269 464 const char *pszRoot = vbsfMappingsQueryHostRoot(root); 270 271 465 if ( !pszRoot 272 || ! (cbRoot = strlen(pszRoot)))466 || !*pszRoot) 273 467 { 274 468 Log(("vbsfBuildFullPath: invalid root!\n")); 275 469 return VERR_INVALID_PARAMETER; 276 470 } 277 471 uint32_t cchRoot = (uint32_t)strlen(pszRoot); 472 #if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS 473 Assert(!strchr(pszRoot, '/')); 474 #endif 475 476 /* 477 * Combine the root prefix with the guest path into a full UTF-8 path in a 478 * buffer pointed to by pszFullPath. cchRoot may be adjusted in the process. 479 */ 480 int rc; 481 size_t cchFullPath; 482 char *pszFullPath = NULL; 278 483 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) 279 484 { 280 /* Verify that the path is under the root directory. */ 281 rc = vbsfPathCheck((const char *)&pPath->String.utf8[0], pPath->u16Length); 282 if (RT_SUCCESS(rc)) 283 { 284 size_t cbUtf8FullPath = cbRoot + 1 + pPath->u16Length + 1; 285 char *utf8FullPath = (char *) RTMemAllocZ(cbUtf8FullPath); 286 287 if (!utf8FullPath) 288 { 289 rc = VERR_NO_MEMORY; 290 *ppszFullPath = NULL; 291 Log(("RTMemAllocZ %x failed!!\n", cbUtf8FullPath)); 292 } 293 else 294 { 295 /** @todo r-bird: Pardon me for asking, but who validates the UTF-8 encoding?*/ 296 memcpy(utf8FullPath, pszRoot, cbRoot); 297 utf8FullPath[cbRoot] = '/'; 298 memcpy(utf8FullPath + cbRoot + 1, &pPath->String.utf8[0], pPath->u16Length); 299 utf8FullPath[cbUtf8FullPath - 1] = 0; 300 pszFullPath = utf8FullPath; 301 302 if (pcbFullPathRoot) 303 *pcbFullPathRoot = (uint32_t)cbRoot; /* Must index the path delimiter. */ 304 } 485 /* Strip leading slashes from the path the guest specified. */ 486 uint32_t cchSrc = pPath->u16Length; 487 const char *pszSrc = (char *)&pPath->String.utf8[0]; 488 Log(("Root %s path %.*s\n", pszRoot, cchSrc, pszSrc)); 489 490 while ( cchSrc > 0 491 && VBSF_IS_PATH_SLASH(pClient, *pszSrc)) 492 { 493 pszSrc++; 494 cchSrc--; 495 } 496 497 /* Allocate a buffer that will be able to contain the root prefix and 498 the path specified by the guest. */ 499 cchFullPath = cchRoot + cchSrc; 500 pszFullPath = (char *)RTMemAlloc(cchFullPath + 1 + 1); 501 if (RT_LIKELY(pszFullPath != NULL)) 502 { 503 memcpy(pszFullPath, pszRoot, cchRoot); 504 if (!RTPATH_IS_SLASH(pszRoot[-1])) 505 { 506 pszFullPath[cchRoot++] = RTPATH_DELIMITER; 507 cchFullPath++; 508 } 509 510 if (cchSrc) 511 memcpy(&pszFullPath[cchRoot], pszSrc, cchSrc); 512 513 /* Terminate the string. */ 514 pszFullPath[cchRoot + cchSrc] = '\0'; 515 rc = VINF_SUCCESS; 305 516 } 306 517 else 307 518 { 308 Log(("vbsfBuildFullPath: vbsfPathCheck failed with %Rrc\n", rc)); 309 } 310 } 311 else 312 { 313 #ifdef RT_OS_DARWIN 519 Log(("RTMemAlloc %x failed!!\n", cchFullPath + 1)); 520 rc = VERR_NO_MEMORY; 521 } 522 } 523 else /* Client speaks UTF-16. */ 524 { 525 #ifdef RT_OS_DARWIN /* Misplaced hack! See todo! */ 314 526 /** @todo This belongs in rtPathToNative or in the windows shared folder file system driver... 315 527 * The question is simply whether the NFD normalization is actually applied on a (virtual) file … … 345 557 CFRelease(inStr); 346 558 #endif 347 /* Client sends us UCS2, so convert it to UTF8. */ 348 Log(("Root %s path %.*ls\n", pszRoot, pPath->u16Length/sizeof(pPath->String.ucs2[0]), pPath->String.ucs2)); 559 560 /* Strip leading slashes and calculate the UTF-8 length. */ 561 size_t cwcSrc = pPath->u16Length / sizeof(RTUTF16); 562 PRTUTF16 pwszSrc = &pPath->String.ucs2[0]; 563 Log(("Root %s path %.*ls\n", pszRoot, cwcSrc, pwszSrc)); 564 565 while ( cwcSrc > 0 566 && *pwszSrc < 0x80 567 && VBSF_IS_PATH_SLASH(pClient, (char)*pwszSrc)) 568 { 569 pwszSrc++; 570 cwcSrc--; 571 } 572 573 size_t cchPathAsUtf8 = RTUtf16CalcUtf8Len(pwszSrc); 574 #ifdef RT_OS_DARWIN 575 AssertReturnStmt(cchPathAsUtf8 >= cwcSrc, RTMemFree(pPath), VERR_INTERNAL_ERROR_3); 576 #else 577 AssertReturn(cchPathAsUtf8 >= cwcSrc, VERR_INTERNAL_ERROR_3); 578 #endif 349 579 350 580 /* Allocate buffer that will be able to contain the root prefix and 351 * the pPath converted to UTF8. Expect a 2 bytes UCS2 to be converted 352 * to 8 bytes UTF8 in the worst case. 353 */ 354 uint32_t cbFullPath = (cbRoot + ShflStringLength(pPath)) * 4; 355 pszFullPath = (char *)RTMemAllocZ(cbFullPath); 356 if (!pszFullPath) 357 { 581 * the pPath converted to UTF-8. */ 582 cchFullPath = cchRoot + cchPathAsUtf8; 583 pszFullPath = (char *)RTMemAlloc(cchFullPath + 1 + 1); 584 if (RT_LIKELY(pszFullPath != NULL)) 585 { 586 /* Copy the root prefix into the result buffer and make sure it 587 ends with a path separator. */ 588 memcpy(pszFullPath, pszRoot, cchRoot); 589 if (!RTPATH_IS_SLASH(pszFullPath[cchRoot - 1])) 590 { 591 pszFullPath[cchRoot++] = RTPATH_DELIMITER; 592 cchFullPath++; 593 } 594 595 /* Append the path specified by the guest (if any). */ 596 if (cchPathAsUtf8) 597 { 598 size_t cchActual; 599 char *pszDst = &pszFullPath[cchRoot]; 600 rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cchFullPath - cchRoot + 1, &cchActual); 601 AssertRC(rc); 602 AssertStmt(RT_FAILURE(rc) || cchActual == cchPathAsUtf8, rc = VERR_INTERNAL_ERROR_4); 603 Assert(strlen(pszDst) == cchPathAsUtf8); 604 } 605 else 606 rc = VINF_SUCCESS; 607 608 /* Terminate the string. */ 609 pszFullPath[cchRoot + cchPathAsUtf8] = '\0'; 610 } 611 else 612 { 613 Log(("RTMemAlloc %x failed!!\n", cchFullPath + 1)); 358 614 rc = VERR_NO_MEMORY; 359 615 } 360 else 361 { 362 memcpy(pszFullPath, pszRoot, cbRoot + 1); 363 char *pszDst = pszFullPath; 364 size_t cbDst = strlen(pszDst); 365 size_t cb = cbFullPath; 366 if (pszDst[cbDst - 1] != RTPATH_DELIMITER) 367 { 368 pszDst[cbDst] = RTPATH_DELIMITER; 369 cbDst++; 370 } 371 372 if (pcbFullPathRoot) 373 *pcbFullPathRoot = cbDst - 1; /* Must index the path delimiter. */ 374 375 pszDst += cbDst; 376 cb -= cbDst; 377 378 if (pPath->u16Length) 379 { 380 /* Convert and copy components. */ 381 size_t cwcSrc = pPath->u16Length / sizeof(RTUTF16); 382 PRTUTF16 pwszSrc = &pPath->String.ucs2[0]; 383 384 /* Correct path delimiters */ 385 if (pClient->PathDelimiter != RTPATH_DELIMITER) 386 { 387 LogFlow(("Correct path delimiter in %ls\n", pwszSrc)); 388 while (*pwszSrc) 389 { 390 if (*pwszSrc == pClient->PathDelimiter) 391 *pwszSrc = RTPATH_DELIMITER; 392 pwszSrc++; 393 } 394 pwszSrc = &pPath->String.ucs2[0]; 395 LogFlow(("Corrected string %ls\n", pwszSrc)); 396 } 397 if (*pwszSrc == RTPATH_DELIMITER) 398 { 399 pwszSrc++; /* we already appended a delimiter to the first part */ 400 cwcSrc--; 401 } 402 403 rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cb, &cbDst); 404 if (RT_FAILURE(rc)) 405 { 406 AssertFailed(); 407 #ifdef RT_OS_DARWIN 408 RTMemFree(pPath); 409 pPath = pPathParameter; 410 #endif 411 return rc; 412 } 413 Assert(cbDst == strlen(pszDst)); 414 415 /* Verify that the path is under the root directory. */ 416 rc = vbsfPathCheck(pszDst, cbDst); 417 if (RT_FAILURE(rc)) 418 { 419 #ifdef RT_OS_DARWIN 420 RTMemFree(pPath); 421 #endif 422 return rc; 423 } 424 425 cb -= cbDst; 426 pszDst += cbDst; 427 428 Assert(cb > 0); 429 } 430 431 /* Nul terminate the string */ 432 *pszDst = 0; 433 } 616 434 617 #ifdef RT_OS_DARWIN 435 618 RTMemFree(pPath); … … 437 620 #endif 438 621 } 439 440 622 if (RT_SUCCESS(rc)) 441 623 { 442 /* When the host file system is case sensitive and the guest expects 443 * a case insensitive fs, then problems can occur */ 444 if ( vbsfIsHostMappingCaseSensitive(root) 445 && !vbsfIsGuestMappingCaseSensitive(root)) 446 { 447 RTFSOBJINFO info; 448 char *pszLastComponent = NULL; 449 450 if (fWildCard || fPreserveLastComponent) 451 { 452 /* strip off the last path component, that has to be preserved: 453 * contains the wildcard(s) or a 'rename' target. */ 454 size_t cb = strlen(pszFullPath); 455 char *pszSrc = pszFullPath + cb - 1; 456 457 while (pszSrc > pszFullPath) 458 { 459 if (*pszSrc == RTPATH_DELIMITER) 460 break; 461 pszSrc--; 462 } 463 if (*pszSrc == RTPATH_DELIMITER) 464 { 465 bool fHaveWildcards = false; 466 char *psz = pszSrc; 467 468 while (*psz) 469 { 470 char ch = *psz; 471 if (ch == '*' || ch == '?' || ch == '>' || ch == '<' || ch == '"') 472 { 473 fHaveWildcards = true; 474 break; 475 } 476 psz++; 477 } 478 479 if (fHaveWildcards || fPreserveLastComponent) 480 { 481 pszLastComponent = pszSrc; 482 *pszLastComponent = 0; 483 } 484 } 485 } 486 487 /** @todo don't check when creating files or directories; waste of time */ 488 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); 489 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) 490 { 491 size_t cb = strlen(pszFullPath); 492 char *pszSrc = pszFullPath + cb - 1; 493 494 Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath)); 495 496 /* Find partial path that's valid */ 497 while (pszSrc > pszFullPath) 498 { 499 if (*pszSrc == RTPATH_DELIMITER) 500 { 501 *pszSrc = 0; 502 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); 503 *pszSrc = RTPATH_DELIMITER; 504 if (RT_SUCCESS(rc)) 505 { 506 #ifdef DEBUG 507 *pszSrc = 0; 508 Log(("Found valid partial path %s\n", pszFullPath)); 509 *pszSrc = RTPATH_DELIMITER; 510 #endif 511 break; 512 } 513 } 514 515 pszSrc--; 516 } 517 Assert(*pszSrc == RTPATH_DELIMITER && RT_SUCCESS(rc)); 518 if ( *pszSrc == RTPATH_DELIMITER 519 && RT_SUCCESS(rc)) 520 { 521 pszSrc++; 522 for (;;) 523 { 524 char *pszEnd = pszSrc; 525 bool fEndOfString = true; 526 527 while (*pszEnd) 528 { 529 if (*pszEnd == RTPATH_DELIMITER) 530 break; 531 pszEnd++; 532 } 533 534 if (*pszEnd == RTPATH_DELIMITER) 535 { 536 fEndOfString = false; 537 *pszEnd = 0; 538 rc = RTPathQueryInfoEx(pszSrc, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); 539 Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND); 540 } 541 else if (pszEnd == pszSrc) 542 rc = VINF_SUCCESS; /* trailing delimiter */ 543 else 544 rc = VERR_FILE_NOT_FOUND; 545 546 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) 547 { 548 /* path component is invalid; try to correct the casing */ 549 rc = vbsfCorrectCasing(pClient, pszFullPath, pszSrc); 550 if (RT_FAILURE(rc)) 551 { 552 if (!fEndOfString) 553 *pszEnd = RTPATH_DELIMITER; /* restore the original full path */ 554 break; 555 } 556 } 557 558 if (fEndOfString) 559 break; 560 561 *pszEnd = RTPATH_DELIMITER; 562 pszSrc = pszEnd + 1; 563 } 564 if (RT_FAILURE(rc)) 565 Log(("Unable to find suitable component rc=%d\n", rc)); 566 } 567 else 568 rc = VERR_FILE_NOT_FOUND; 569 570 } 571 if (pszLastComponent) 572 *pszLastComponent = RTPATH_DELIMITER; 573 574 /* might be a new file so don't fail here! */ 575 rc = VINF_SUCCESS; 576 } 577 *ppszFullPath = pszFullPath; 578 579 LogFlow(("vbsfBuildFullPath: %s rc=%Rrc\n", pszFullPath, rc)); 580 } 581 624 Assert(strlen(pszFullPath) == cchFullPath); 625 Assert(RTPATH_IS_SLASH(pszFullPath[cchRoot - 1])); /* includes delimiter. */ 626 627 if (pcbFullPathRoot) 628 *pcbFullPathRoot = cchRoot - 1; /* Must index the path delimiter. */ 629 630 /* 631 * Convert guest path delimiters into host ones and check for attempts 632 * to escape the shared folder root directory. 633 * 634 * After this, there will only be RTPATH_DELIMITER slashes in the path! 635 * 636 * ASSUMES that the root path only has RTPATH_DELIMITER as well. 637 */ 638 char ch; 639 char *pszTmp = &pszFullPath[cchRoot]; 640 while ((ch = *pszTmp) != '\0') 641 { 642 if (VBSF_IS_PATH_SLASH(pClient, ch)) 643 *pszTmp = RTPATH_DELIMITER; 644 pszTmp++; 645 } 646 LogFlow(("Corrected string %s\n", pszFullPath)); 647 648 rc = vbsfPathCheck(&pszFullPath[cchRoot]); 649 if (RT_SUCCESS(rc)) 650 { 651 /* 652 * When the host file system is case sensitive and the guest expects 653 * a case insensitive fs, then problems can occur. 654 */ 655 if ( vbsfIsHostMappingCaseSensitive(root) 656 && !vbsfIsGuestMappingCaseSensitive(root)) 657 rc = vbsfCorrectPathCasing(pClient, pszFullPath, cchFullPath, fWildCard, fPreserveLastComponent); 658 if (RT_SUCCESS(rc)) 659 { 660 /* 661 * We're good. 662 */ 663 *ppszFullPath = pszFullPath; 664 LogFlow(("vbsfBuildFullPath: %s rc=%Rrc\n", pszFullPath, rc)); 665 return rc; 666 } 667 668 /* Failed, clean up. */ 669 Log(("vbsfBuildFullPath: %s rc=%Rrc\n", pszFullPath, rc)); 670 } 671 else 672 Log(("vbsfBuildPath: Caught escape attempt: (%.*s) '%s'\n", cchRoot, pszFullPath, &pszFullPath[cchRoot])); 673 } 674 675 if (pszFullPath) 676 RTMemFree(pszFullPath); 677 *ppszFullPath = NULL; 582 678 return rc; 583 679 } … … 1960 2056 1961 2057 ShflStringInitBuffer(&dummy, sizeof(dummy)); 2058 dummy.String.ucs2[0] = '\0'; 1962 2059 rc = vbsfBuildFullPath(pClient, root, &dummy, 0, &pszFullPath, NULL); 1963 2060
Note:
See TracChangeset
for help on using the changeset viewer.