Changeset 56961 in vbox
- Timestamp:
- Jul 17, 2015 7:55:21 AM (10 years ago)
- svn:sync-xref-src-repo-rev:
- 101671
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/HostServices/SharedFolders/vbsfpath.cpp
r56938 r56961 1 1 /* $Id$ */ 2 2 /** @file 3 * Shared Folders - VBox Shared Folders.3 * Shared Folders - guest/host path convertion and verification. 4 4 */ 5 5 6 6 /* 7 * Copyright (C) 2006-201 3Oracle Corporation7 * Copyright (C) 2006-2015 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 20 20 #endif 21 21 22 #include "vbsfpath.h" 22 23 #include "mappings.h" 23 24 #include "vbsf.h" … … 25 26 26 27 #include <iprt/alloc.h> 28 #include <iprt/asm.h> 27 29 #include <iprt/assert.h> 28 30 #include <iprt/fs.h> … … 49 51 */ 50 52 51 void vbsfStripLastComponent(char *pszFullPath, uint32_t cbFullPathRoot)52 {53 RTUNICP cp;54 55 /* Do not strip root. */56 char *s = pszFullPath + cbFullPathRoot;57 char *delimSecondLast = NULL;58 char *delimLast = NULL;59 60 LogFlowFunc(("%s -> %s\n", pszFullPath, s));61 62 for (;;)63 {64 cp = RTStrGetCp(s);65 66 if (cp == RTUNICP_INVALID || cp == 0)67 {68 break;69 }70 71 if (cp == RTPATH_DELIMITER)72 {73 if (delimLast != NULL)74 {75 delimSecondLast = delimLast;76 }77 78 delimLast = s;79 }80 81 s = RTStrNextCp(s);82 }83 84 if (cp == 0)85 {86 if (delimLast + 1 == s)87 {88 if (delimSecondLast)89 {90 *delimSecondLast = 0;91 }92 else if (delimLast)93 {94 *delimLast = 0;95 }96 }97 else98 {99 if (delimLast)100 {101 *delimLast = 0;102 }103 }104 }105 106 LogFlowFunc(("%s, %s, %s\n", pszFullPath, delimLast, delimSecondLast));107 }108 109 53 /** 110 54 * Corrects the casing of the final component … … 370 314 371 315 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) ) 316 #ifdef RT_OS_DARWIN 317 /* Misplaced hack! See todo! */ 318 319 /** Normalize the string using kCFStringNormalizationFormD. 320 * 321 * @param pwszSrc The input UTF-16 string. 322 * @param cwcSrc Length of the input string in characters. 323 * @param ppwszDst Where to store the pointer to the resulting normalized string. 324 * @param pcwcDst Where to store length of the normalized string in characters (without the trailing nul). 325 */ 326 static int vbsfNormalizeStringDarwin(const PRTUTF16 pwszSrc, uint32_t cwcSrc, PRTUTF16 *ppwszDst, uint32_t *pcwcDst) 327 { 328 /** @todo This belongs in rtPathToNative or in the windows shared folder file system driver... 329 * The question is simply whether the NFD normalization is actually applied on a (virtual) file 330 * system level in darwin, or just by the user mode application libs. */ 331 332 PRTUTF16 pwszNFD; 333 uint32_t cwcNFD; 334 335 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0); 336 337 /* Is 8 times length enough for decomposed in worst case...? */ 338 size_t cbNFDAlloc = cwcSrc * 8 + 2; 339 pwszNFD = (PRTUTF16)RTMemAllocZ(cbNFDAlloc); 340 if (!pwszNFD) 341 { 342 return VERR_NO_MEMORY; 343 } 344 345 ::CFStringAppendCharacters(inStr, (UniChar*)pwszSrc, cwcSrc); 346 ::CFStringNormalize(inStr, kCFStringNormalizationFormD); 347 cwcNFD = ::CFStringGetLength(inStr); 348 349 CFRange rangeCharacters; 350 rangeCharacters.location = 0; 351 rangeCharacters.length = cwcNFD; 352 ::CFStringGetCharacters(inStr, rangeCharacters, pwszNFD); 353 354 pwszNFD[cwcNFD] = 0x0000; /* NULL terminated */ 355 356 CFRelease(inStr); 357 358 *ppwszDst = pwszNFD; 359 *pcwcDst = cwcNFD; 360 return VINF_SUCCESS; 361 } 382 362 #endif 383 363 … … 400 380 * checking absolute paths! 401 381 */ 402 static int vbsfPathCheck (const char *pszPath)382 static int vbsfPathCheckRootEscape(const char *pszPath) 403 383 { 404 384 /* … … 457 437 } 458 438 459 static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING pPath, 460 uint32_t cbPath, char **ppszFullPath, uint32_t *pcbFullPathRoot, 461 bool fWildCard = false, bool fPreserveLastComponent = false) 462 { 463 /* Resolve the root prefix into a string. */ 464 const char *pszRoot = vbsfMappingsQueryHostRoot(root); 465 if ( !pszRoot 466 || !*pszRoot) 467 { 468 Log(("vbsfBuildFullPath: invalid root!\n")); 469 return VERR_INVALID_PARAMETER; 470 } 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; 439 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 440 /* See MSDN "Naming Files, Paths, and Namespaces". 441 * '<', '>' and '"' are allowed as possible wildcards (see ANSI_DOS_STAR, etc in ntifs.h) 442 */ 443 static const char sachCharBlackList[] = ":/\\|"; 444 #elif defined(RT_OS_DARWIN) 445 /* Technically only '/' is not allowed, but apparently ':' has a special meaning in Finder. 446 */ 447 static const char sachCharBlackList[] = ":/"; 448 #else 449 /* Something else. */ 450 static const char sachCharBlackList[] = "/"; 451 #endif 452 453 /** Verify if the character can be used in a host file name. 454 * Wildcard characters ('?', '*') are allowed. 455 * 456 * @param c Character to verify. 457 */ 458 static bool vbsfPathIsValidNameChar(char c) 459 { 460 /* Character 0 is not allowed too. */ 461 if (c == 0 || strchr(sachCharBlackList, c)) 462 { 463 return false; 464 } 465 466 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 467 /* Characters less than 32 are not allowed. */ 468 if (c < 32) 469 { 470 return false; 471 } 472 #endif 473 474 return true; 475 } 476 477 /** Verify if the character is a wildcard. 478 * 479 * @param c Character to verify. 480 */ 481 static bool vbsfPathIsWildcardChar(char c) 482 { 483 if ( c == '*' 484 || c == '?' 485 #ifdef RT_OS_WINDOWS /* See ntifs.h */ 486 || c == '<' /* ANSI_DOS_STAR */ 487 || c == '>' /* ANSI_DOS_QM */ 488 || c == '"' /* ANSI_DOS_DOT */ 489 #endif 490 ) 491 { 492 return true; 493 } 494 495 return false; 496 } 497 498 int vbsfPathGuestToHost(SHFLCLIENTDATA *pClient, SHFLROOT hRoot, 499 PSHFLSTRING pGuestString, uint32_t cbGuestString, 500 char **ppszHostPath, uint32_t *pcbHostPathRoot, 501 bool fWildCard, bool fPreserveLastComponent, 502 uint32_t *pfu32PathFlags) 503 { 504 #ifdef VBOX_STRICT 505 /* 506 * Check that the pGuestPath has correct size and encoding. 507 */ 508 if (ShflStringIsValidIn(pGuestString, cbGuestString, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) == false) 509 { 510 LogFunc(("Invalid input string\n")); 511 return VERR_INTERNAL_ERROR; 512 } 513 #else 514 NOREF(cbGuestString); 515 #endif 516 517 /* 518 * Resolve the root handle into a string. 519 */ 520 uint32_t cbRootLen = 0; 521 const char *pszRoot = NULL; 522 int rc = vbsfMappingsQueryHostRootEx(hRoot, &pszRoot, &cbRootLen); 523 if (RT_FAILURE(rc)) 524 { 525 LogFunc(("invalid root\n")); 526 return rc; 527 } 528 529 AssertReturn(cbRootLen > 0, VERR_INTERNAL_ERROR_2); /* vbsfMappingsQueryHostRootEx ensures this. */ 530 531 /* 532 * Get the UTF8 string with the relative path provided by the guest. 533 * If guest uses UTF-16 then convert it to UTF-8. 534 */ 535 uint32_t cbGuestPath; 536 const char *pchGuestPath; 537 char *pchGuestPathAllocated = NULL; /* Converted from UTF-16. */ 483 538 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) 484 539 { 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); 540 /* UTF-8 */ 541 cbGuestPath = pGuestString->u16Length; 542 pchGuestPath = (char *)&pGuestString->String.utf8[0]; 543 } 544 else 545 { 546 /* UTF-16 */ 547 uint32_t cwcSrc; 548 PRTUTF16 pwszSrc; 549 550 #ifdef RT_OS_DARWIN /* Misplaced hack! See todo! */ 551 cwcSrc = 0; 552 pwszSrc = NULL; 553 rc = vbsfNormalizeStringDarwin(&pGuestString->String.ucs2[0], 554 pGuestString->u16Length / sizeof(RTUTF16), 555 &pwszSrc, &cwcSrc); 556 #else 557 cwcSrc = pGuestString->u16Length / sizeof(RTUTF16); 558 pwszSrc = &pGuestString->String.ucs2[0]; 559 #endif 560 561 if (RT_SUCCESS(rc)) 562 { 563 size_t cbPathAsUtf8 = RTUtf16CalcUtf8Len(pwszSrc); 564 if (cbPathAsUtf8 >= cwcSrc) 565 { 566 /* Allocate buffer that will be able to contain the converted UTF-8 string. */ 567 pchGuestPathAllocated = (char *)RTMemAlloc(cbPathAsUtf8 + 1); 568 if (RT_LIKELY(pchGuestPathAllocated != NULL)) 569 { 570 if (RT_LIKELY(cbPathAsUtf8)) 571 { 572 size_t cchActual; 573 char *pszDst = pchGuestPathAllocated; 574 rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cbPathAsUtf8 + 1, &cchActual); 575 AssertRC(rc); 576 AssertStmt(RT_FAILURE(rc) || cchActual == cbPathAsUtf8, rc = VERR_INTERNAL_ERROR_4); 577 Assert(strlen(pszDst) == cbPathAsUtf8); 578 } 579 580 if (RT_SUCCESS(rc)) 581 { 582 /* Terminate the string. */ 583 pchGuestPathAllocated[cbPathAsUtf8] = '\0'; 584 585 cbGuestPath = cbPathAsUtf8; 586 pchGuestPath = pchGuestPathAllocated; 587 } 588 } 589 else 590 { 591 rc = VERR_NO_MEMORY; 592 } 593 } 594 else 595 { 596 AssertFailed(); 597 rc = VERR_INTERNAL_ERROR_3; 598 } 599 600 #ifdef RT_OS_DARWIN 601 RTMemFree(pwszSrc); 602 #endif 603 } 604 } 605 606 char *pszFullPath = NULL; 607 608 if (RT_SUCCESS(rc)) 609 { 610 LogFlowFunc(("Root %s path %.*s\n", pszRoot, cbGuestPath, pchGuestPath)); 611 612 /* 613 * Allocate enough memory to build the host full path from the root and the relative path. 614 */ 615 uint32_t cbFullPathAlloc = cbRootLen + 1 + cbGuestPath + 1; /* root + possible_slash + relative + 0 */ 616 pszFullPath = (char *)RTMemAlloc(cbFullPathAlloc); 501 617 if (RT_LIKELY(pszFullPath != NULL)) 502 618 { 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; 619 /* Copy the root. */ 620 memcpy(pszFullPath, pszRoot, cbRootLen); 621 if (!RTPATH_IS_SLASH(pszFullPath[cbRootLen - 1])) 622 { 623 pszFullPath[cbRootLen++] = RTPATH_SLASH; 624 } 625 626 /* Init the pointer for the relative path. */ 627 char *pchDst = &pszFullPath[cbRootLen]; 628 629 uint32_t cbSrc = cbGuestPath; 630 const char *pchSrc = pchGuestPath; 631 632 /* Strip leading delimiters from the path the guest specified. */ 633 while ( cbSrc > 0 634 && *pchSrc == pClient->PathDelimiter) 635 { 636 ++pchSrc; 637 --cbSrc; 638 } 639 640 /* 641 * Iterate the guest path components, verify each of them 642 * and append to the host full path replacing delimiters with host slash. 643 */ 644 bool fLastComponentHasWildcard = false; 645 for (; cbSrc > 0; --cbSrc, ++pchSrc) 646 { 647 if (RT_LIKELY(*pchSrc != pClient->PathDelimiter)) 648 { 649 if (RT_LIKELY(vbsfPathIsValidNameChar(*pchSrc))) 650 { 651 if (pfu32PathFlags && vbsfPathIsWildcardChar(*pchSrc)) 652 { 653 fLastComponentHasWildcard = true; 654 } 655 656 *pchDst++ = *pchSrc; 657 } 658 else 659 { 660 rc = VERR_INVALID_NAME; 661 break; 662 } 663 } 664 else 665 { 666 /* Replace with the host slash. */ 667 *pchDst++ = RTPATH_SLASH; 668 669 if (pfu32PathFlags && fLastComponentHasWildcard && cbSrc > 1) 670 { 671 /* Processed component has a wildcard and there are more characters in the path. */ 672 *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_PREFIX; 673 } 674 fLastComponentHasWildcard = false; 675 } 676 } 677 678 if (RT_SUCCESS(rc)) 679 { 680 const size_t cbFullPathLength = pchDst - &pszFullPath[0]; /* As strlen(pszFullPath). */ 681 682 *pchDst++ = 0; 683 684 if (pfu32PathFlags && fLastComponentHasWildcard) 685 { 686 *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_LAST; 687 } 688 689 /* Check the appended path for root escapes. */ 690 rc = vbsfPathCheckRootEscape(&pszFullPath[cbRootLen]); 691 if (RT_SUCCESS(rc)) 692 { 693 /* 694 * When the host file system is case sensitive and the guest expects 695 * a case insensitive fs, then problems can occur. 696 */ 697 if ( vbsfIsHostMappingCaseSensitive(hRoot) 698 && !vbsfIsGuestMappingCaseSensitive(hRoot)) 699 { 700 rc = vbsfCorrectPathCasing(pClient, pszFullPath, cbFullPathLength, fWildCard, fPreserveLastComponent); 701 } 702 703 if (RT_SUCCESS(rc)) 704 { 705 LogFunc(("%s\n", pszFullPath)); 706 707 /* Return the full host path. */ 708 *ppszHostPath = pszFullPath; 709 710 if (pcbHostPathRoot) 711 { 712 *pcbHostPathRoot = cbRootLen - 1; /* Must index the path delimiter. */ 713 } 714 } 715 } 716 } 516 717 } 517 718 else 518 719 { 519 Log(("RTMemAlloc %x failed!!\n", cchFullPath + 1));520 720 rc = VERR_NO_MEMORY; 521 721 } 522 722 } 523 else /* Client speaks UTF-16. */ 524 { 525 #ifdef RT_OS_DARWIN /* Misplaced hack! See todo! */ 526 /** @todo This belongs in rtPathToNative or in the windows shared folder file system driver... 527 * The question is simply whether the NFD normalization is actually applied on a (virtual) file 528 * system level in darwin, or just by the user mode application libs. */ 529 SHFLSTRING *pPathParameter = pPath; 530 size_t cbPathLength; 531 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0); 532 uint16_t ucs2Length; 533 CFRange rangeCharacters; 534 535 // Is 8 times length enough for decomposed in worst case...? 536 cbPathLength = sizeof(SHFLSTRING) + pPathParameter->u16Length * 8 + 2; 537 pPath = (SHFLSTRING *)RTMemAllocZ(cbPathLength); 538 if (!pPath) 539 { 540 rc = VERR_NO_MEMORY; 541 Log(("RTMemAllocZ %x failed!!\n", cbPathLength)); 542 return rc; 543 } 544 545 ::CFStringAppendCharacters(inStr, (UniChar*)pPathParameter->String.ucs2, 546 pPathParameter->u16Length / sizeof(pPathParameter->String.ucs2[0])); 547 ::CFStringNormalize(inStr, kCFStringNormalizationFormD); 548 ucs2Length = ::CFStringGetLength(inStr); 549 550 rangeCharacters.location = 0; 551 rangeCharacters.length = ucs2Length; 552 ::CFStringGetCharacters(inStr, rangeCharacters, pPath->String.ucs2); 553 pPath->String.ucs2[ucs2Length] = 0x0000; // NULL terminated 554 pPath->u16Length = ucs2Length * sizeof(pPath->String.ucs2[0]); 555 pPath->u16Size = pPath->u16Length + sizeof(pPath->String.ucs2[0]); 556 557 CFRelease(inStr); 558 #endif 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 579 580 /* Allocate buffer that will be able to contain the root prefix and 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)); 614 rc = VERR_NO_MEMORY; 615 } 616 617 #ifdef RT_OS_DARWIN 618 RTMemFree(pPath); 619 pPath = pPathParameter; 620 #endif 621 } 723 724 /* 725 * Cleanup. 726 */ 727 RTMemFree(pchGuestPathAllocated); 728 622 729 if (RT_SUCCESS(rc)) 623 730 { 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; 731 return rc; 732 } 733 734 /* 735 * Cleanup on failure. 736 */ 737 RTMemFree(pszFullPath); 738 678 739 return rc; 679 740 } 680 741 681 static void vbsfFreeFullPath(char *pszFullPath) 682 { 683 RTMemFree(pszFullPath); 684 } 685 686 /** 687 * Convert shared folder create flags (see include/iprt/shflsvc.h) into iprt create flags. 688 * 689 * @returns iprt status code 690 * @param fShflFlags shared folder create flags 691 * @param fMode file attributes 692 * @retval pfOpen iprt create flags 693 */ 694 static int vbsfConvertFileOpenFlags(unsigned fShflFlags, RTFMODE fMode, SHFLHANDLE handleInitial, uint32_t *pfOpen) 695 { 696 uint32_t fOpen = 0; 697 int rc = VINF_SUCCESS; 698 699 if ( (fMode & RTFS_DOS_MASK) != 0 700 && (fMode & RTFS_UNIX_MASK) == 0) 701 { 702 /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*. 703 * @todo this is based on rtFsModeNormalize/rtFsModeFromDos. 704 * May be better to use RTFsModeNormalize here. 705 */ 706 fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH; 707 /* x for directories. */ 708 if (fMode & RTFS_DOS_DIRECTORY) 709 fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH; 710 /* writable? */ 711 if (!(fMode & RTFS_DOS_READONLY)) 712 fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH; 713 714 /* Set the requested mode using only allowed bits. */ 715 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK; 716 } 717 else 718 { 719 /* Old linux and solaris additions did not initialize the Info.Attr.fMode field 720 * and it contained random bits from stack. Detect this using the handle field value 721 * passed from the guest: old additions set it (incorrectly) to 0, new additions 722 * set it to SHFL_HANDLE_NIL(~0). 723 */ 724 if (handleInitial == 0) 725 { 726 /* Old additions. Do nothing, use default mode. */ 727 } 728 else 729 { 730 /* New additions or Windows additions. Set the requested mode using only allowed bits. 731 * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode 732 * will be set in fOpen. 733 */ 734 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK; 735 } 736 } 737 738 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_RW)) 739 { 740 default: 741 case SHFL_CF_ACCESS_NONE: 742 { 743 /** @todo treat this as read access, but theoretically this could be a no access request. */ 744 fOpen |= RTFILE_O_READ; 745 Log(("FLAG: SHFL_CF_ACCESS_NONE\n")); 746 break; 747 } 748 749 case SHFL_CF_ACCESS_READ: 750 { 751 fOpen |= RTFILE_O_READ; 752 Log(("FLAG: SHFL_CF_ACCESS_READ\n")); 753 break; 754 } 755 756 case SHFL_CF_ACCESS_WRITE: 757 { 758 fOpen |= RTFILE_O_WRITE; 759 Log(("FLAG: SHFL_CF_ACCESS_WRITE\n")); 760 break; 761 } 762 763 case SHFL_CF_ACCESS_READWRITE: 764 { 765 fOpen |= RTFILE_O_READWRITE; 766 Log(("FLAG: SHFL_CF_ACCESS_READWRITE\n")); 767 break; 768 } 769 } 770 771 if (fShflFlags & SHFL_CF_ACCESS_APPEND) 772 { 773 fOpen |= RTFILE_O_APPEND; 774 } 775 776 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR)) 777 { 778 default: 779 case SHFL_CF_ACCESS_ATTR_NONE: 780 { 781 fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT; 782 Log(("FLAG: SHFL_CF_ACCESS_ATTR_NONE\n")); 783 break; 784 } 785 786 case SHFL_CF_ACCESS_ATTR_READ: 787 { 788 fOpen |= RTFILE_O_ACCESS_ATTR_READ; 789 Log(("FLAG: SHFL_CF_ACCESS_ATTR_READ\n")); 790 break; 791 } 792 793 case SHFL_CF_ACCESS_ATTR_WRITE: 794 { 795 fOpen |= RTFILE_O_ACCESS_ATTR_WRITE; 796 Log(("FLAG: SHFL_CF_ACCESS_ATTR_WRITE\n")); 797 break; 798 } 799 800 case SHFL_CF_ACCESS_ATTR_READWRITE: 801 { 802 fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE; 803 Log(("FLAG: SHFL_CF_ACCESS_ATTR_READWRITE\n")); 804 break; 805 } 806 } 807 808 /* Sharing mask */ 809 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_DENY)) 810 { 811 default: 812 case SHFL_CF_ACCESS_DENYNONE: 813 fOpen |= RTFILE_O_DENY_NONE; 814 Log(("FLAG: SHFL_CF_ACCESS_DENYNONE\n")); 815 break; 816 817 case SHFL_CF_ACCESS_DENYREAD: 818 fOpen |= RTFILE_O_DENY_READ; 819 Log(("FLAG: SHFL_CF_ACCESS_DENYREAD\n")); 820 break; 821 822 case SHFL_CF_ACCESS_DENYWRITE: 823 fOpen |= RTFILE_O_DENY_WRITE; 824 Log(("FLAG: SHFL_CF_ACCESS_DENYWRITE\n")); 825 break; 826 827 case SHFL_CF_ACCESS_DENYALL: 828 fOpen |= RTFILE_O_DENY_ALL; 829 Log(("FLAG: SHFL_CF_ACCESS_DENYALL\n")); 830 break; 831 } 832 833 /* Open/Create action mask */ 834 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_EXISTS)) 835 { 836 case SHFL_CF_ACT_OPEN_IF_EXISTS: 837 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) 838 { 839 fOpen |= RTFILE_O_OPEN_CREATE; 840 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n")); 841 } 842 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) 843 { 844 fOpen |= RTFILE_O_OPEN; 845 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n")); 846 } 847 else 848 { 849 Log(("FLAGS: invalid open/create action combination\n")); 850 rc = VERR_INVALID_PARAMETER; 851 } 852 break; 853 case SHFL_CF_ACT_FAIL_IF_EXISTS: 854 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) 855 { 856 fOpen |= RTFILE_O_CREATE; 857 Log(("FLAGS: SHFL_CF_ACT_FAIL_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n")); 858 } 859 else 860 { 861 Log(("FLAGS: invalid open/create action combination\n")); 862 rc = VERR_INVALID_PARAMETER; 863 } 864 break; 865 case SHFL_CF_ACT_REPLACE_IF_EXISTS: 866 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) 867 { 868 fOpen |= RTFILE_O_CREATE_REPLACE; 869 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n")); 870 } 871 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) 872 { 873 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE; 874 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n")); 875 } 876 else 877 { 878 Log(("FLAGS: invalid open/create action combination\n")); 879 rc = VERR_INVALID_PARAMETER; 880 } 881 break; 882 case SHFL_CF_ACT_OVERWRITE_IF_EXISTS: 883 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) 884 { 885 fOpen |= RTFILE_O_CREATE_REPLACE; 886 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n")); 887 } 888 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) 889 { 890 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE; 891 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n")); 892 } 893 else 894 { 895 Log(("FLAGS: invalid open/create action combination\n")); 896 rc = VERR_INVALID_PARAMETER; 897 } 898 break; 899 default: 900 rc = VERR_INVALID_PARAMETER; 901 Log(("FLAG: SHFL_CF_ACT_MASK_IF_EXISTS - invalid parameter\n")); 902 } 903 904 if (RT_SUCCESS(rc)) 905 { 906 *pfOpen = fOpen; 907 } 908 return rc; 909 } 910 911 /** 912 * Open a file or create and open a new one. 913 * 914 * @returns IPRT status code 915 * @param pClient Data structure describing the client accessing the shared folder 916 * @param pszPath Path to the file or folder on the host. 917 * @param pParms->CreateFlags Creation or open parameters, see include/VBox/shflsvc.h 918 * @param pParms->Info When a new file is created this specifies the initial parameters. 919 * When a file is created or overwritten, it also specifies the 920 * initial size. 921 * @retval pParms->Result Shared folder status code, see include/VBox/shflsvc.h 922 * @retval pParms->Handle On success the (shared folder) handle of the file opened or 923 * created 924 * @retval pParms->Info On success the parameters of the file opened or created 925 */ 926 static int vbsfOpenFile(SHFLCLIENTDATA *pClient, const char *pszPath, SHFLCREATEPARMS *pParms) 927 { 928 LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms)); 929 Log(("SHFL create flags %08x\n", pParms->CreateFlags)); 930 931 SHFLHANDLE handle = SHFL_HANDLE_NIL; 932 SHFLFILEHANDLE *pHandle = 0; 933 /* Open or create a file. */ 934 uint32_t fOpen = 0; 935 bool fNoError = false; 936 static int cErrors; 937 938 int rc = vbsfConvertFileOpenFlags(pParms->CreateFlags, pParms->Info.Attr.fMode, pParms->Handle, &fOpen); 939 if (RT_SUCCESS(rc)) 940 { 941 rc = VERR_NO_MEMORY; /* Default error. */ 942 handle = vbsfAllocFileHandle(pClient); 943 if (handle != SHFL_HANDLE_NIL) 944 { 945 pHandle = vbsfQueryFileHandle(pClient, handle); 946 if (pHandle) 947 { 948 rc = RTFileOpen(&pHandle->file.Handle, pszPath, fOpen); 949 } 950 } 951 } 952 if (RT_FAILURE(rc)) 953 { 954 switch (rc) 955 { 956 case VERR_FILE_NOT_FOUND: 957 pParms->Result = SHFL_FILE_NOT_FOUND; 958 959 /* This actually isn't an error, so correct the rc before return later, 960 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */ 961 fNoError = true; 962 break; 963 case VERR_PATH_NOT_FOUND: 964 pParms->Result = SHFL_PATH_NOT_FOUND; 965 966 /* This actually isn't an error, so correct the rc before return later, 967 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */ 968 fNoError = true; 969 break; 970 case VERR_ALREADY_EXISTS: 971 RTFSOBJINFO info; 972 973 /** @todo Possible race left here. */ 974 if (RT_SUCCESS(RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)))) 975 { 976 #ifdef RT_OS_WINDOWS 977 info.Attr.fMode |= 0111; 978 #endif 979 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info); 980 } 981 pParms->Result = SHFL_FILE_EXISTS; 982 983 /* This actually isn't an error, so correct the rc before return later, 984 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */ 985 fNoError = true; 986 break; 987 case VERR_TOO_MANY_OPEN_FILES: 988 if (cErrors < 32) 989 { 990 LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath)); 991 #if defined RT_OS_LINUX || RT_OS_SOLARIS 992 if (cErrors < 1) 993 LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimit -n)\n")); 994 #endif 995 cErrors++; 996 } 997 pParms->Result = SHFL_NO_RESULT; 998 break; 999 default: 1000 pParms->Result = SHFL_NO_RESULT; 1001 } 1002 } 1003 else 1004 { 1005 /** @note The shared folder status code is very approximate, as the runtime 1006 * does not really provide this information. */ 1007 pParms->Result = SHFL_FILE_EXISTS; /* We lost the information as to whether it was 1008 created when we eliminated the race. */ 1009 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS 1010 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)) 1011 || ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS 1012 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))) 1013 { 1014 /* For now, we do not treat a failure here as fatal. */ 1015 /* @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if 1016 SHFL_CF_ACT_FAIL_IF_EXISTS is set. */ 1017 RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject); 1018 pParms->Result = SHFL_FILE_REPLACED; 1019 } 1020 if ( ( SHFL_CF_ACT_FAIL_IF_EXISTS 1021 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)) 1022 || ( SHFL_CF_ACT_CREATE_IF_NEW 1023 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))) 1024 { 1025 pParms->Result = SHFL_FILE_CREATED; 1026 } 1027 #if 0 1028 /* @todo */ 1029 /* Set new attributes. */ 1030 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS 1031 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)) 1032 || ( SHFL_CF_ACT_CREATE_IF_NEW 1033 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))) 1034 { 1035 RTFileSetTimes(pHandle->file.Handle, 1036 &pParms->Info.AccessTime, 1037 &pParms->Info.ModificationTime, 1038 &pParms->Info.ChangeTime, 1039 &pParms->Info.BirthTime 1040 ); 1041 1042 RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode); 1043 } 1044 #endif 1045 RTFSOBJINFO info; 1046 1047 /* Get file information */ 1048 rc = RTFileQueryInfo(pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING); 1049 if (RT_SUCCESS(rc)) 1050 { 1051 #ifdef RT_OS_WINDOWS 1052 info.Attr.fMode |= 0111; 1053 #endif 1054 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info); 1055 } 1056 } 1057 /* Free resources if any part of the function has failed. */ 1058 if (RT_FAILURE(rc)) 1059 { 1060 if ( (0 != pHandle) 1061 && (NIL_RTFILE != pHandle->file.Handle) 1062 && (0 != pHandle->file.Handle)) 1063 { 1064 RTFileClose(pHandle->file.Handle); 1065 pHandle->file.Handle = NIL_RTFILE; 1066 } 1067 if (SHFL_HANDLE_NIL != handle) 1068 { 1069 vbsfFreeFileHandle(pClient, handle); 1070 } 1071 pParms->Handle = SHFL_HANDLE_NIL; 1072 } 1073 else 1074 { 1075 pParms->Handle = handle; 1076 } 1077 1078 /* Report the driver that all is okay, we're done here */ 1079 if (fNoError) 1080 rc = VINF_SUCCESS; 1081 1082 LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc)); 1083 return rc; 1084 } 1085 1086 /** 1087 * Open a folder or create and open a new one. 1088 * 1089 * @returns IPRT status code 1090 * @param pszPath Path to the file or folder on the host. 1091 * @param pParms->CreateFlags Creation or open parameters, see include/VBox/shflsvc.h 1092 * @retval pParms->Result Shared folder status code, see include/VBox/shflsvc.h 1093 * @retval pParms->Handle On success the (shared folder) handle of the folder opened or 1094 * created 1095 * @retval pParms->Info On success the parameters of the folder opened or created 1096 * 1097 * @note folders are created with fMode = 0777 1098 */ 1099 static int vbsfOpenDir(SHFLCLIENTDATA *pClient, const char *pszPath, 1100 SHFLCREATEPARMS *pParms) 1101 { 1102 LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms)); 1103 Log(("SHFL create flags %08x\n", pParms->CreateFlags)); 1104 1105 int rc = VERR_NO_MEMORY; 1106 SHFLHANDLE handle = vbsfAllocDirHandle(pClient); 1107 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, handle); 1108 if (0 != pHandle) 1109 { 1110 rc = VINF_SUCCESS; 1111 pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */ 1112 /** @todo Can anyone think of a sensible, race-less way to do this? Although 1113 I suspect that the race is inherent, due to the API available... */ 1114 /* Try to create the folder first if "create if new" is specified. If this 1115 fails, and "open if exists" is specified, then we ignore the failure and try 1116 to open the folder anyway. */ 1117 if ( SHFL_CF_ACT_CREATE_IF_NEW 1118 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)) 1119 { 1120 /** @todo render supplied attributes. 1121 * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */ 1122 RTFMODE fMode = 0777; 1123 1124 pParms->Result = SHFL_FILE_CREATED; 1125 rc = RTDirCreate(pszPath, fMode, 0); 1126 if (RT_FAILURE(rc)) 1127 { 1128 switch (rc) 1129 { 1130 case VERR_ALREADY_EXISTS: 1131 pParms->Result = SHFL_FILE_EXISTS; 1132 break; 1133 case VERR_PATH_NOT_FOUND: 1134 pParms->Result = SHFL_PATH_NOT_FOUND; 1135 break; 1136 default: 1137 pParms->Result = SHFL_NO_RESULT; 1138 } 1139 } 1140 } 1141 if ( RT_SUCCESS(rc) 1142 || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))) 1143 { 1144 /* Open the directory now */ 1145 rc = RTDirOpenFiltered(&pHandle->dir.Handle, pszPath, RTDIRFILTER_NONE, 0); 1146 if (RT_SUCCESS(rc)) 1147 { 1148 RTFSOBJINFO info; 1149 1150 rc = RTDirQueryInfo(pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING); 1151 if (RT_SUCCESS(rc)) 1152 { 1153 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info); 1154 } 1155 } 1156 else 1157 { 1158 switch (rc) 1159 { 1160 case VERR_FILE_NOT_FOUND: /* Does this make sense? */ 1161 pParms->Result = SHFL_FILE_NOT_FOUND; 1162 break; 1163 case VERR_PATH_NOT_FOUND: 1164 pParms->Result = SHFL_PATH_NOT_FOUND; 1165 break; 1166 case VERR_ACCESS_DENIED: 1167 pParms->Result = SHFL_FILE_EXISTS; 1168 break; 1169 default: 1170 pParms->Result = SHFL_NO_RESULT; 1171 } 1172 } 1173 } 1174 } 1175 if (RT_FAILURE(rc)) 1176 { 1177 if ( (0 != pHandle) 1178 && (0 != pHandle->dir.Handle)) 1179 { 1180 RTDirClose(pHandle->dir.Handle); 1181 pHandle->dir.Handle = 0; 1182 } 1183 if (SHFL_HANDLE_NIL != handle) 1184 { 1185 vbsfFreeFileHandle(pClient, handle); 1186 } 1187 pParms->Handle = SHFL_HANDLE_NIL; 1188 } 1189 else 1190 { 1191 pParms->Handle = handle; 1192 } 1193 LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc)); 1194 return rc; 1195 } 1196 1197 static int vbsfCloseDir(SHFLFILEHANDLE *pHandle) 1198 { 1199 int rc = VINF_SUCCESS; 1200 1201 LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n", 1202 pHandle->dir.Handle, pHandle->dir.SearchHandle)); 1203 1204 RTDirClose(pHandle->dir.Handle); 1205 1206 if (pHandle->dir.SearchHandle) 1207 RTDirClose(pHandle->dir.SearchHandle); 1208 1209 if (pHandle->dir.pLastValidEntry) 1210 { 1211 RTMemFree(pHandle->dir.pLastValidEntry); 1212 pHandle->dir.pLastValidEntry = NULL; 1213 } 1214 1215 LogFlow(("vbsfCloseDir: rc = %d\n", rc)); 1216 1217 return rc; 1218 } 1219 1220 1221 static int vbsfCloseFile(SHFLFILEHANDLE *pHandle) 1222 { 1223 int rc = VINF_SUCCESS; 1224 1225 LogFlow(("vbsfCloseFile: Handle = %08X\n", 1226 pHandle->file.Handle)); 1227 1228 rc = RTFileClose(pHandle->file.Handle); 1229 1230 LogFlow(("vbsfCloseFile: rc = %d\n", rc)); 1231 1232 return rc; 1233 } 1234 1235 /** 1236 * Look up file or folder information by host path. 1237 * 1238 * @returns iprt status code (currently VINF_SUCCESS) 1239 * @param pszFullPath The path of the file to be looked up 1240 * @retval pParms->Result Status of the operation (success or error) 1241 * @retval pParms->Info On success, information returned about the file 1242 */ 1243 static int vbsfLookupFile(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms) 1244 { 1245 RTFSOBJINFO info; 1246 int rc; 1247 1248 rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); 1249 LogFlow(("SHFL_CF_LOOKUP\n")); 1250 /* Client just wants to know if the object exists. */ 1251 switch (rc) 1252 { 1253 case VINF_SUCCESS: 1254 { 1255 #ifdef RT_OS_WINDOWS 1256 info.Attr.fMode |= 0111; 1257 #endif 1258 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info); 1259 pParms->Result = SHFL_FILE_EXISTS; 1260 break; 1261 } 1262 1263 case VERR_FILE_NOT_FOUND: 1264 { 1265 pParms->Result = SHFL_FILE_NOT_FOUND; 1266 rc = VINF_SUCCESS; 1267 break; 1268 } 1269 1270 case VERR_PATH_NOT_FOUND: 1271 { 1272 pParms->Result = SHFL_PATH_NOT_FOUND; 1273 rc = VINF_SUCCESS; 1274 break; 1275 } 1276 } 1277 pParms->Handle = SHFL_HANDLE_NIL; 1278 return rc; 1279 } 1280 1281 #ifdef UNITTEST 1282 /** Unit test the SHFL_FN_CREATE API. Located here as a form of API 1283 * documentation. */ 1284 void testCreate(RTTEST hTest) 1285 { 1286 /* Simple opening of an existing file. */ 1287 testCreateFileSimple(hTest); 1288 /* Simple opening of an existing directory. */ 1289 /** @todo How do wildcards in the path name work? */ 1290 testCreateDirSimple(hTest); 1291 /* If the number or types of parameters are wrong the API should fail. */ 1292 testCreateBadParameters(hTest); 1293 /* Add tests as required... */ 1294 } 1295 #endif 1296 /** 1297 * Create or open a file or folder. Perform character set and case 1298 * conversion on the file name if necessary. 1299 * 1300 * @returns IPRT status code, but see note below 1301 * @param pClient Data structure describing the client accessing the shared 1302 * folder 1303 * @param root The index of the shared folder in the table of mappings. 1304 * The host path of the shared folder is found using this. 1305 * @param pPath The path of the file or folder relative to the host path 1306 * indexed by root. 1307 * @param cbPath Presumably the length of the path in pPath. Actually 1308 * ignored, as pPath contains a length parameter. 1309 * @param pParms->Info If a new file is created or an old one overwritten, set 1310 * these attributes 1311 * @retval pParms->Result Shared folder result code, see include/VBox/shflsvc.h 1312 * @retval pParms->Handle Shared folder handle to the newly opened file 1313 * @retval pParms->Info Attributes of the file or folder opened 1314 * 1315 * @note This function returns success if a "non-exceptional" error occurred, 1316 * such as "no such file". In this case, the caller should check the 1317 * pParms->Result return value and whether pParms->Handle is valid. 1318 */ 1319 int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms) 1320 { 1321 int rc = VINF_SUCCESS; 1322 1323 LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n", 1324 pClient, pPath, cbPath, pParms, pParms->CreateFlags)); 1325 1326 /* Check the client access rights to the root. */ 1327 /** @todo */ 1328 1329 /* Build a host full path for the given path, handle file name case issues (if the guest 1330 * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if 1331 * necessary. 1332 */ 1333 char *pszFullPath = NULL; 1334 uint32_t cbFullPathRoot = 0; 1335 1336 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot); 1337 if (RT_SUCCESS(rc)) 1338 { 1339 /* Reset return value in case client forgot to do so. 1340 * pParms->Handle must not be reset here, as it is used 1341 * in vbsfOpenFile to detect old additions. 1342 */ 1343 pParms->Result = SHFL_NO_RESULT; 1344 1345 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP)) 1346 { 1347 rc = vbsfLookupFile(pClient, pszFullPath, pParms); 1348 } 1349 else 1350 { 1351 /* Query path information. */ 1352 RTFSOBJINFO info; 1353 1354 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); 1355 LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc)); 1356 1357 if (RT_SUCCESS(rc)) 1358 { 1359 /* Mark it as a directory in case the caller didn't. */ 1360 /** 1361 * @todo I left this in in order not to change the behaviour of the 1362 * function too much. Is it really needed, and should it really be 1363 * here? 1364 */ 1365 if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY)) 1366 { 1367 pParms->CreateFlags |= SHFL_CF_DIRECTORY; 1368 } 1369 1370 /** 1371 * @todo This should be in the Windows Guest Additions, as no-one else 1372 * needs it. 1373 */ 1374 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY)) 1375 { 1376 vbsfStripLastComponent(pszFullPath, cbFullPathRoot); 1377 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS; 1378 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW; 1379 pParms->CreateFlags |= SHFL_CF_DIRECTORY; 1380 pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS; 1381 pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW; 1382 } 1383 } 1384 1385 rc = VINF_SUCCESS; 1386 1387 /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation 1388 * will cause changes. 1389 * 1390 * Actual operations (write, set attr, etc), which can write to a shared folder, have 1391 * the check and will return VERR_WRITE_PROTECT if the folder is not writable. 1392 */ 1393 if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS 1394 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS 1395 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW 1396 ) 1397 { 1398 /* is the guest allowed to write to this share? */ 1399 bool fWritable; 1400 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable); 1401 if (RT_FAILURE(rc) || !fWritable) 1402 rc = VERR_WRITE_PROTECT; 1403 } 1404 1405 if (RT_SUCCESS(rc)) 1406 { 1407 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY)) 1408 { 1409 rc = vbsfOpenDir(pClient, pszFullPath, pParms); 1410 } 1411 else 1412 { 1413 rc = vbsfOpenFile(pClient, pszFullPath, pParms); 1414 } 1415 } 1416 else 1417 { 1418 pParms->Handle = SHFL_HANDLE_NIL; 1419 } 1420 } 1421 1422 /* free the path string */ 1423 vbsfFreeFullPath(pszFullPath); 1424 } 1425 1426 Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result)); 1427 1428 return rc; 1429 } 1430 1431 #ifdef UNITTEST 1432 /** Unit test the SHFL_FN_CLOSE API. Located here as a form of API 1433 * documentation. */ 1434 void testClose(RTTEST hTest) 1435 { 1436 /* If the API parameters are invalid the API should fail. */ 1437 testCloseBadParameters(hTest); 1438 /* Add tests as required... */ 1439 } 1440 #endif 1441 int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle) 1442 { 1443 int rc = VINF_SUCCESS; 1444 1445 LogFlow(("vbsfClose: pClient = %p, Handle = %RX64\n", 1446 pClient, Handle)); 1447 1448 uint32_t type = vbsfQueryHandleType(pClient, Handle); 1449 Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0); 1450 1451 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) 1452 { 1453 case SHFL_HF_TYPE_DIR: 1454 { 1455 rc = vbsfCloseDir(vbsfQueryDirHandle(pClient, Handle)); 1456 break; 1457 } 1458 case SHFL_HF_TYPE_FILE: 1459 { 1460 rc = vbsfCloseFile(vbsfQueryFileHandle(pClient, Handle)); 1461 break; 1462 } 1463 default: 1464 return VERR_INVALID_HANDLE; 1465 } 1466 vbsfFreeFileHandle(pClient, Handle); 1467 1468 Log(("vbsfClose: rc = %Rrc\n", rc)); 1469 1470 return rc; 1471 } 1472 1473 #ifdef UNITTEST 1474 /** Unit test the SHFL_FN_READ API. Located here as a form of API 1475 * documentation. */ 1476 void testRead(RTTEST hTest) 1477 { 1478 /* If the number or types of parameters are wrong the API should fail. */ 1479 testReadBadParameters(hTest); 1480 /* Basic reading from a file. */ 1481 testReadFileSimple(hTest); 1482 /* Add tests as required... */ 1483 } 1484 #endif 1485 int vbsfRead (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer) 1486 { 1487 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); 1488 size_t count = 0; 1489 int rc; 1490 1491 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0) 1492 { 1493 AssertFailed(); 1494 return VERR_INVALID_PARAMETER; 1495 } 1496 1497 Log(("vbsfRead %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer)); 1498 1499 if (*pcbBuffer == 0) 1500 return VINF_SUCCESS; /* @todo correct? */ 1501 1502 1503 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL); 1504 if (rc != VINF_SUCCESS) 1505 { 1506 AssertRC(rc); 1507 return rc; 1508 } 1509 1510 rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count); 1511 *pcbBuffer = (uint32_t)count; 1512 Log(("RTFileRead returned %Rrc bytes read %x\n", rc, count)); 1513 return rc; 1514 } 1515 1516 #ifdef UNITTEST 1517 /** Unit test the SHFL_FN_WRITE API. Located here as a form of API 1518 * documentation. */ 1519 void testWrite(RTTEST hTest) 1520 { 1521 /* If the number or types of parameters are wrong the API should fail. */ 1522 testWriteBadParameters(hTest); 1523 /* Simple test of writing to a file. */ 1524 testWriteFileSimple(hTest); 1525 /* Add tests as required... */ 1526 } 1527 #endif 1528 int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer) 1529 { 1530 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); 1531 size_t count = 0; 1532 int rc; 1533 1534 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0) 1535 { 1536 AssertFailed(); 1537 return VERR_INVALID_PARAMETER; 1538 } 1539 1540 Log(("vbsfWrite %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer)); 1541 1542 /* Is the guest allowed to write to this share? 1543 * XXX Actually this check was still done in vbsfCreate() -- RTFILE_O_WRITE cannot be set if vbsfMappingsQueryWritable() failed. */ 1544 bool fWritable; 1545 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable); 1546 if (RT_FAILURE(rc) || !fWritable) 1547 return VERR_WRITE_PROTECT; 1548 1549 if (*pcbBuffer == 0) 1550 return VINF_SUCCESS; /** @todo correct? */ 1551 1552 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL); 1553 if (rc != VINF_SUCCESS) 1554 { 1555 AssertRC(rc); 1556 return rc; 1557 } 1558 1559 rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count); 1560 *pcbBuffer = (uint32_t)count; 1561 Log(("RTFileWrite returned %Rrc bytes written %x\n", rc, count)); 1562 return rc; 1563 } 1564 1565 1566 #ifdef UNITTEST 1567 /** Unit test the SHFL_FN_FLUSH API. Located here as a form of API 1568 * documentation. */ 1569 void testFlush(RTTEST hTest) 1570 { 1571 /* If the number or types of parameters are wrong the API should fail. */ 1572 testFlushBadParameters(hTest); 1573 /* Simple opening and flushing of a file. */ 1574 testFlushFileSimple(hTest); 1575 /* Add tests as required... */ 1576 } 1577 #endif 1578 int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle) 1579 { 1580 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); 1581 int rc = VINF_SUCCESS; 1582 1583 if (pHandle == 0) 1584 { 1585 AssertFailed(); 1586 return VERR_INVALID_HANDLE; 1587 } 1588 1589 Log(("vbsfFlush %RX64\n", Handle)); 1590 rc = RTFileFlush(pHandle->file.Handle); 1591 AssertRC(rc); 1592 return rc; 1593 } 1594 1595 #ifdef UNITTEST 1596 /** Unit test the SHFL_FN_LIST API. Located here as a form of API 1597 * documentation. */ 1598 void testDirList(RTTEST hTest) 1599 { 1600 /* If the number or types of parameters are wrong the API should fail. */ 1601 testDirListBadParameters(hTest); 1602 /* Test listing an empty directory (simple edge case). */ 1603 testDirListEmpty(hTest); 1604 /* Add tests as required... */ 1605 } 1606 #endif 1607 int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags, 1608 uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles) 1609 { 1610 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle); 1611 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg; 1612 uint32_t cbDirEntry, cbBufferOrg; 1613 int rc = VINF_SUCCESS; 1614 PSHFLDIRINFO pSFDEntry; 1615 PRTUTF16 pwszString; 1616 PRTDIR DirHandle; 1617 bool fUtf8; 1618 1619 fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0; 1620 1621 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0) 1622 { 1623 AssertFailed(); 1624 return VERR_INVALID_PARAMETER; 1625 } 1626 Assert(pIndex && *pIndex == 0); 1627 DirHandle = pHandle->dir.Handle; 1628 1629 cbDirEntry = 4096; 1630 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry); 1631 if (pDirEntry == 0) 1632 { 1633 AssertFailed(); 1634 return VERR_NO_MEMORY; 1635 } 1636 1637 cbBufferOrg = *pcbBuffer; 1638 *pcbBuffer = 0; 1639 pSFDEntry = (PSHFLDIRINFO)pBuffer; 1640 1641 *pIndex = 1; /* not yet complete */ 1642 *pcFiles = 0; 1643 1644 if (pPath) 1645 { 1646 if (pHandle->dir.SearchHandle == 0) 1647 { 1648 /* Build a host full path for the given path 1649 * and convert ucs2 to utf8 if necessary. 1650 */ 1651 char *pszFullPath = NULL; 1652 1653 Assert(pHandle->dir.pLastValidEntry == 0); 1654 1655 rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size, &pszFullPath, NULL, true); 1656 1657 if (RT_SUCCESS(rc)) 1658 { 1659 rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0); 1660 1661 /* free the path string */ 1662 vbsfFreeFullPath(pszFullPath); 1663 1664 if (RT_FAILURE(rc)) 1665 goto end; 1666 } 1667 else 1668 goto end; 1669 } 1670 Assert(pHandle->dir.SearchHandle); 1671 DirHandle = pHandle->dir.SearchHandle; 1672 } 1673 1674 while (cbBufferOrg) 1675 { 1676 size_t cbDirEntrySize = cbDirEntry; 1677 uint32_t cbNeeded; 1678 1679 /* Do we still have a valid last entry for the active search? If so, then return it here */ 1680 if (pHandle->dir.pLastValidEntry) 1681 { 1682 pDirEntry = pHandle->dir.pLastValidEntry; 1683 } 1684 else 1685 { 1686 pDirEntry = pDirEntryOrg; 1687 1688 rc = RTDirReadEx(DirHandle, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); 1689 if (rc == VERR_NO_MORE_FILES) 1690 { 1691 *pIndex = 0; /* listing completed */ 1692 break; 1693 } 1694 1695 if ( rc != VINF_SUCCESS 1696 && rc != VWRN_NO_DIRENT_INFO) 1697 { 1698 //AssertFailed(); 1699 if ( rc == VERR_NO_TRANSLATION 1700 || rc == VERR_INVALID_UTF8_ENCODING) 1701 continue; 1702 break; 1703 } 1704 } 1705 1706 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String); 1707 if (fUtf8) 1708 cbNeeded += pDirEntry->cbName + 1; 1709 else 1710 /* Overestimating, but that's ok */ 1711 cbNeeded += (pDirEntry->cbName + 1) * 2; 1712 1713 if (cbBufferOrg < cbNeeded) 1714 { 1715 /* No room, so save this directory entry, or else it's lost forever */ 1716 pHandle->dir.pLastValidEntry = pDirEntry; 1717 1718 if (*pcFiles == 0) 1719 { 1720 AssertFailed(); 1721 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */ 1722 } 1723 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */ 1724 } 1725 1726 #ifdef RT_OS_WINDOWS 1727 pDirEntry->Info.Attr.fMode |= 0111; 1728 #endif 1729 vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info); 1730 pSFDEntry->cucShortName = 0; 1731 1732 if (fUtf8) 1733 { 1734 void *src, *dst; 1735 1736 src = &pDirEntry->szName[0]; 1737 dst = &pSFDEntry->name.String.utf8[0]; 1738 1739 memcpy(dst, src, pDirEntry->cbName + 1); 1740 1741 pSFDEntry->name.u16Size = pDirEntry->cbName + 1; 1742 pSFDEntry->name.u16Length = pDirEntry->cbName; 1743 } 1744 else 1745 { 1746 pSFDEntry->name.String.ucs2[0] = 0; 1747 pwszString = pSFDEntry->name.String.ucs2; 1748 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL); 1749 AssertRC(rc2); 1750 1751 #ifdef RT_OS_DARWIN 1752 /** @todo This belongs in rtPathToNative or in the windows shared folder file system driver... 1753 * The question is simply whether the NFD normalization is actually applied on a (virtual) file 1754 * system level in darwin, or just by the user mode application libs. */ 1755 { 1756 // Convert to 1757 // Normalization Form C (composed Unicode). We need this because 1758 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode) 1759 // while most other OS', server-side programs usually expect NFC. 1760 uint16_t ucs2Length; 1761 CFRange rangeCharacters; 1762 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0); 1763 1764 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString)); 1765 ::CFStringNormalize(inStr, kCFStringNormalizationFormC); 1766 ucs2Length = ::CFStringGetLength(inStr); 1767 1768 rangeCharacters.location = 0; 1769 rangeCharacters.length = ucs2Length; 1770 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString); 1771 pwszString[ucs2Length] = 0x0000; // NULL terminated 1772 1773 CFRelease(inStr); 1774 } 1775 #endif 1776 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2; 1777 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2; 1778 1779 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size)); 1780 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2)); 1781 1782 // adjust cbNeeded (it was overestimated before) 1783 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size; 1784 } 1785 1786 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded); 1787 *pcbBuffer += cbNeeded; 1788 cbBufferOrg-= cbNeeded; 1789 1790 *pcFiles += 1; 1791 1792 /* Free the saved last entry, that we've just returned */ 1793 if (pHandle->dir.pLastValidEntry) 1794 { 1795 RTMemFree(pHandle->dir.pLastValidEntry); 1796 pHandle->dir.pLastValidEntry = NULL; 1797 } 1798 1799 if (flags & SHFL_LIST_RETURN_ONE) 1800 break; /* we're done */ 1801 } 1802 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0); 1803 1804 end: 1805 if (pDirEntry) 1806 RTMemFree(pDirEntry); 1807 1808 return rc; 1809 } 1810 1811 #ifdef UNITTEST 1812 /** Unit test the SHFL_FN_READLINK API. Located here as a form of API 1813 * documentation. */ 1814 void testReadLink(RTTEST hTest) 1815 { 1816 /* If the number or types of parameters are wrong the API should fail. */ 1817 testReadLinkBadParameters(hTest); 1818 /* Add tests as required... */ 1819 } 1820 #endif 1821 int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer) 1822 { 1823 int rc = VINF_SUCCESS; 1824 1825 if (pPath == 0 || pBuffer == 0) 1826 { 1827 AssertFailed(); 1828 return VERR_INVALID_PARAMETER; 1829 } 1830 1831 /* Build a host full path for the given path, handle file name case issues 1832 * (if the guest expects case-insensitive paths but the host is 1833 * case-sensitive) and convert ucs2 to utf8 if necessary. 1834 */ 1835 char *pszFullPath = NULL; 1836 uint32_t cbFullPathRoot = 0; 1837 1838 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot); 1839 1840 if (RT_SUCCESS(rc)) 1841 { 1842 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0); 1843 1844 /* free the path string */ 1845 vbsfFreeFullPath(pszFullPath); 1846 } 1847 1848 return rc; 1849 } 1850 1851 int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer) 1852 { 1853 uint32_t type = vbsfQueryHandleType(pClient, Handle); 1854 int rc = VINF_SUCCESS; 1855 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer; 1856 RTFSOBJINFO fileinfo; 1857 1858 1859 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE) 1860 || pcbBuffer == 0 1861 || pObjInfo == 0 1862 || *pcbBuffer < sizeof(SHFLFSOBJINFO)) 1863 { 1864 AssertFailed(); 1865 return VERR_INVALID_PARAMETER; 1866 } 1867 1868 /* @todo other options */ 1869 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE)); 1870 1871 *pcbBuffer = 0; 1872 1873 if (type == SHFL_HF_TYPE_DIR) 1874 { 1875 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle); 1876 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING); 1877 } 1878 else 1879 { 1880 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); 1881 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING); 1882 #ifdef RT_OS_WINDOWS 1883 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode)) 1884 pObjInfo->Attr.fMode |= 0111; 1885 #endif 1886 } 1887 if (rc == VINF_SUCCESS) 1888 { 1889 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo); 1890 *pcbBuffer = sizeof(SHFLFSOBJINFO); 1891 } 1892 else 1893 AssertFailed(); 1894 1895 return rc; 1896 } 1897 1898 static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer) 1899 { 1900 uint32_t type = vbsfQueryHandleType(pClient, Handle); 1901 int rc = VINF_SUCCESS; 1902 SHFLFSOBJINFO *pSFDEntry; 1903 1904 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE) 1905 || pcbBuffer == 0 1906 || pBuffer == 0 1907 || *pcbBuffer < sizeof(SHFLFSOBJINFO)) 1908 { 1909 AssertFailed(); 1910 return VERR_INVALID_PARAMETER; 1911 } 1912 1913 *pcbBuffer = 0; 1914 pSFDEntry = (SHFLFSOBJINFO *)pBuffer; 1915 1916 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE)); 1917 1918 /* Change only the time values that are not zero */ 1919 if (type == SHFL_HF_TYPE_DIR) 1920 { 1921 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle); 1922 rc = RTDirSetTimes(pHandle->dir.Handle, 1923 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL, 1924 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL, 1925 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL, 1926 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL 1927 ); 1928 } 1929 else 1930 { 1931 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); 1932 rc = RTFileSetTimes(pHandle->file.Handle, 1933 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL, 1934 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL, 1935 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL, 1936 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL 1937 ); 1938 } 1939 if (rc != VINF_SUCCESS) 1940 { 1941 Log(("RTFileSetTimes failed with %Rrc\n", rc)); 1942 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime))); 1943 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime))); 1944 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime))); 1945 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime))); 1946 /* temporary hack */ 1947 rc = VINF_SUCCESS; 1948 } 1949 1950 if (type == SHFL_HF_TYPE_FILE) 1951 { 1952 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); 1953 /* Change file attributes if necessary */ 1954 if (pSFDEntry->Attr.fMode) 1955 { 1956 RTFMODE fMode = pSFDEntry->Attr.fMode; 1957 1958 #ifndef RT_OS_WINDOWS 1959 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be 1960 * able to access this file anymore. Only for guests, which set the UNIX mode. */ 1961 if (fMode & RTFS_UNIX_MASK) 1962 fMode |= RTFS_UNIX_IRUSR; 1963 #endif 1964 1965 rc = RTFileSetMode(pHandle->file.Handle, fMode); 1966 if (rc != VINF_SUCCESS) 1967 { 1968 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc)); 1969 /* silent failure, because this tends to fail with e.g. windows guest & linux host */ 1970 rc = VINF_SUCCESS; 1971 } 1972 } 1973 } 1974 /* TODO: mode for directories */ 1975 1976 if (rc == VINF_SUCCESS) 1977 { 1978 uint32_t bufsize = sizeof(*pSFDEntry); 1979 1980 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry); 1981 if (rc == VINF_SUCCESS) 1982 { 1983 *pcbBuffer = sizeof(SHFLFSOBJINFO); 1984 } 1985 else 1986 AssertFailed(); 1987 } 1988 1989 return rc; 1990 } 1991 1992 1993 static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer) 1994 { 1995 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); 1996 int rc = VINF_SUCCESS; 1997 SHFLFSOBJINFO *pSFDEntry; 1998 1999 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO)) 2000 { 2001 AssertFailed(); 2002 return VERR_INVALID_PARAMETER; 2003 } 2004 2005 *pcbBuffer = 0; 2006 pSFDEntry = (SHFLFSOBJINFO *)pBuffer; 2007 2008 if (flags & SHFL_INFO_SIZE) 2009 { 2010 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject); 2011 if (rc != VINF_SUCCESS) 2012 AssertFailed(); 2013 } 2014 else 2015 AssertFailed(); 2016 2017 if (rc == VINF_SUCCESS) 2018 { 2019 RTFSOBJINFO fileinfo; 2020 2021 /* Query the new object info and return it */ 2022 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING); 2023 if (rc == VINF_SUCCESS) 2024 { 2025 #ifdef RT_OS_WINDOWS 2026 fileinfo.Attr.fMode |= 0111; 2027 #endif 2028 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo); 2029 *pcbBuffer = sizeof(SHFLFSOBJINFO); 2030 } 2031 else 2032 AssertFailed(); 2033 } 2034 2035 return rc; 2036 } 2037 2038 int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer) 2039 { 2040 int rc = VINF_SUCCESS; 2041 SHFLVOLINFO *pSFDEntry; 2042 char *pszFullPath = NULL; 2043 SHFLSTRING dummy; 2044 2045 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO)) 2046 { 2047 AssertFailed(); 2048 return VERR_INVALID_PARAMETER; 2049 } 2050 2051 /* @todo other options */ 2052 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME)); 2053 2054 *pcbBuffer = 0; 2055 pSFDEntry = (PSHFLVOLINFO)pBuffer; 2056 2057 ShflStringInitBuffer(&dummy, sizeof(dummy)); 2058 dummy.String.ucs2[0] = '\0'; 2059 rc = vbsfBuildFullPath(pClient, root, &dummy, 0, &pszFullPath, NULL); 2060 2061 if (RT_SUCCESS(rc)) 2062 { 2063 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector); 2064 if (rc != VINF_SUCCESS) 2065 goto exit; 2066 2067 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial); 2068 if (rc != VINF_SUCCESS) 2069 goto exit; 2070 2071 RTFSPROPERTIES FsProperties; 2072 rc = RTFsQueryProperties(pszFullPath, &FsProperties); 2073 if (rc != VINF_SUCCESS) 2074 goto exit; 2075 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties); 2076 2077 *pcbBuffer = sizeof(SHFLVOLINFO); 2078 } 2079 else AssertFailed(); 2080 2081 exit: 2082 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc)); 2083 /* free the path string */ 2084 vbsfFreeFullPath(pszFullPath); 2085 return rc; 2086 } 2087 2088 int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer) 2089 { 2090 if (pcbBuffer == 0 || pBuffer == 0) 2091 { 2092 AssertFailed(); 2093 return VERR_INVALID_PARAMETER; 2094 } 2095 2096 if (flags & SHFL_INFO_FILE) 2097 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer); 2098 2099 if (flags & SHFL_INFO_VOLUME) 2100 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer); 2101 2102 AssertFailed(); 2103 return VERR_INVALID_PARAMETER; 2104 } 2105 2106 #ifdef UNITTEST 2107 /** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API 2108 * documentation. */ 2109 void testFSInfo(RTTEST hTest) 2110 { 2111 /* If the number or types of parameters are wrong the API should fail. */ 2112 testFSInfoBadParameters(hTest); 2113 /* Basic get and set file size test. */ 2114 testFSInfoQuerySetFMode(hTest); 2115 /* Basic get and set dir atime test. */ 2116 testFSInfoQuerySetDirATime(hTest); 2117 /* Basic get and set file atime test. */ 2118 testFSInfoQuerySetFileATime(hTest); 2119 /* Basic set end of file. */ 2120 testFSInfoQuerySetEndOfFile(hTest); 2121 /* Add tests as required... */ 2122 } 2123 #endif 2124 int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer) 2125 { 2126 uint32_t type = vbsfQueryHandleType(pClient, Handle) 2127 & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME); 2128 2129 if (type == 0 || pcbBuffer == 0 || pBuffer == 0) 2130 { 2131 AssertFailed(); 2132 return VERR_INVALID_PARAMETER; 2133 } 2134 2135 /* is the guest allowed to write to this share? */ 2136 bool fWritable; 2137 int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable); 2138 if (RT_FAILURE(rc) || !fWritable) 2139 return VERR_WRITE_PROTECT; 2140 2141 if (flags & SHFL_INFO_FILE) 2142 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer); 2143 2144 if (flags & SHFL_INFO_SIZE) 2145 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer); 2146 2147 // if (flags & SHFL_INFO_VOLUME) 2148 // return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer); 2149 AssertFailed(); 2150 return VERR_INVALID_PARAMETER; 2151 } 2152 2153 #ifdef UNITTEST 2154 /** Unit test the SHFL_FN_LOCK API. Located here as a form of API 2155 * documentation. */ 2156 void testLock(RTTEST hTest) 2157 { 2158 /* If the number or types of parameters are wrong the API should fail. */ 2159 testLockBadParameters(hTest); 2160 /* Simple file locking and unlocking test. */ 2161 testLockFileSimple(hTest); 2162 /* Add tests as required... */ 2163 } 2164 #endif 2165 int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags) 2166 { 2167 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); 2168 uint32_t fRTLock = 0; 2169 int rc; 2170 2171 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL); 2172 2173 if (pHandle == 0) 2174 { 2175 AssertFailed(); 2176 return VERR_INVALID_HANDLE; 2177 } 2178 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL) 2179 || (flags & SHFL_LOCK_ENTIRE) 2180 ) 2181 { 2182 AssertFailed(); 2183 return VERR_INVALID_PARAMETER; 2184 } 2185 2186 /* Lock type */ 2187 switch(flags & SHFL_LOCK_MODE_MASK) 2188 { 2189 case SHFL_LOCK_SHARED: 2190 fRTLock = RTFILE_LOCK_READ; 2191 break; 2192 2193 case SHFL_LOCK_EXCLUSIVE: 2194 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE; 2195 break; 2196 2197 default: 2198 AssertFailed(); 2199 return VERR_INVALID_PARAMETER; 2200 } 2201 2202 /* Lock wait type */ 2203 if (flags & SHFL_LOCK_WAIT) 2204 fRTLock |= RTFILE_LOCK_WAIT; 2205 else 2206 fRTLock |= RTFILE_LOCK_IMMEDIATELY; 2207 2208 #ifdef RT_OS_WINDOWS 2209 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length); 2210 if (rc != VINF_SUCCESS) 2211 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc)); 2212 #else 2213 Log(("vbsfLock: Pretend success handle=%x\n", Handle)); 2214 rc = VINF_SUCCESS; 2215 #endif 2216 return rc; 2217 } 2218 2219 int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags) 2220 { 2221 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); 2222 int rc; 2223 2224 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL); 2225 2226 if (pHandle == 0) 2227 { 2228 return VERR_INVALID_HANDLE; 2229 } 2230 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL) 2231 || (flags & SHFL_LOCK_ENTIRE) 2232 ) 2233 { 2234 return VERR_INVALID_PARAMETER; 2235 } 2236 2237 #ifdef RT_OS_WINDOWS 2238 rc = RTFileUnlock(pHandle->file.Handle, offset, length); 2239 if (rc != VINF_SUCCESS) 2240 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc)); 2241 #else 2242 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle)); 2243 rc = VINF_SUCCESS; 2244 #endif 2245 2246 return rc; 2247 } 2248 2249 2250 #ifdef UNITTEST 2251 /** Unit test the SHFL_FN_REMOVE API. Located here as a form of API 2252 * documentation. */ 2253 void testRemove(RTTEST hTest) 2254 { 2255 /* If the number or types of parameters are wrong the API should fail. */ 2256 testRemoveBadParameters(hTest); 2257 /* Add tests as required... */ 2258 } 2259 #endif 2260 int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags) 2261 { 2262 int rc = VINF_SUCCESS; 2263 2264 /* Validate input */ 2265 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK) 2266 || cbPath == 0 2267 || pPath == 0) 2268 { 2269 AssertFailed(); 2270 return VERR_INVALID_PARAMETER; 2271 } 2272 2273 /* Build a host full path for the given path 2274 * and convert ucs2 to utf8 if necessary. 2275 */ 2276 char *pszFullPath = NULL; 2277 2278 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL); 2279 if (RT_SUCCESS(rc)) 2280 { 2281 /* is the guest allowed to write to this share? */ 2282 bool fWritable; 2283 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable); 2284 if (RT_FAILURE(rc) || !fWritable) 2285 rc = VERR_WRITE_PROTECT; 2286 2287 if (RT_SUCCESS(rc)) 2288 { 2289 if (flags & SHFL_REMOVE_SYMLINK) 2290 rc = RTSymlinkDelete(pszFullPath, 0); 2291 else if (flags & SHFL_REMOVE_FILE) 2292 rc = RTFileDelete(pszFullPath); 2293 else 2294 rc = RTDirRemove(pszFullPath); 2295 } 2296 2297 #ifndef DEBUG_dmik 2298 // VERR_ACCESS_DENIED for example? 2299 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY); 2300 #endif 2301 /* free the path string */ 2302 vbsfFreeFullPath(pszFullPath); 2303 } 2304 return rc; 2305 } 2306 2307 2308 #ifdef UNITTEST 2309 /** Unit test the SHFL_FN_RENAME API. Located here as a form of API 2310 * documentation. */ 2311 void testRename(RTTEST hTest) 2312 { 2313 /* If the number or types of parameters are wrong the API should fail. */ 2314 testRenameBadParameters(hTest); 2315 /* Add tests as required... */ 2316 } 2317 #endif 2318 int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags) 2319 { 2320 int rc = VINF_SUCCESS; 2321 2322 /* Validate input */ 2323 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_RENAME_REPLACE_IF_EXISTS) 2324 || pSrc == 0 2325 || pDest == 0) 2326 { 2327 AssertFailed(); 2328 return VERR_INVALID_PARAMETER; 2329 } 2330 2331 /* Build a host full path for the given path 2332 * and convert ucs2 to utf8 if necessary. 2333 */ 2334 char *pszFullPathSrc = NULL; 2335 char *pszFullPathDest = NULL; 2336 2337 rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size, &pszFullPathSrc, NULL); 2338 if (rc != VINF_SUCCESS) 2339 return rc; 2340 2341 rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size, &pszFullPathDest, NULL, false, true); 2342 if (RT_SUCCESS (rc)) 2343 { 2344 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest)); 2345 2346 /* is the guest allowed to write to this share? */ 2347 bool fWritable; 2348 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable); 2349 if (RT_FAILURE(rc) || !fWritable) 2350 rc = VERR_WRITE_PROTECT; 2351 2352 if (RT_SUCCESS(rc)) 2353 { 2354 if (flags & SHFL_RENAME_FILE) 2355 { 2356 rc = RTFileMove(pszFullPathSrc, pszFullPathDest, 2357 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0)); 2358 } 2359 else 2360 { 2361 /* NT ignores the REPLACE flag and simply return and already exists error. */ 2362 rc = RTDirRename(pszFullPathSrc, pszFullPathDest, 2363 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0)); 2364 } 2365 } 2366 2367 #ifndef DEBUG_dmik 2368 AssertRC(rc); 2369 #endif 2370 /* free the path string */ 2371 vbsfFreeFullPath(pszFullPathDest); 2372 } 2373 /* free the path string */ 2374 vbsfFreeFullPath(pszFullPathSrc); 2375 return rc; 2376 } 2377 2378 #ifdef UNITTEST 2379 /** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API 2380 * documentation. */ 2381 void testSymlink(RTTEST hTest) 2382 { 2383 /* If the number or types of parameters are wrong the API should fail. */ 2384 testSymlinkBadParameters(hTest); 2385 /* Add tests as required... */ 2386 } 2387 #endif 2388 int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo) 2389 { 2390 int rc = VINF_SUCCESS; 2391 2392 char *pszFullNewPath = NULL; 2393 const char *pszOldPath = (const char *)pOldPath->String.utf8; 2394 2395 /* XXX: no support for UCS2 at the moment. */ 2396 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) 2397 return VERR_NOT_IMPLEMENTED; 2398 2399 bool fSymlinksCreate; 2400 rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate); 2401 AssertRCReturn(rc, rc); 2402 if (!fSymlinksCreate) 2403 return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */ 2404 2405 rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size, &pszFullNewPath, NULL); 2406 AssertRCReturn(rc, rc); 2407 2408 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8, 2409 RTSYMLINKTYPE_UNKNOWN, 0); 2410 if (RT_SUCCESS(rc)) 2411 { 2412 RTFSOBJINFO info; 2413 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); 2414 if (RT_SUCCESS(rc)) 2415 vbfsCopyFsObjInfoFromIprt(pInfo, &info); 2416 } 2417 2418 vbsfFreeFullPath(pszFullNewPath); 2419 2420 return rc; 2421 } 2422 2423 /* 2424 * Clean up our mess by freeing all handles that are still valid. 2425 * 2426 */ 2427 int vbsfDisconnect(SHFLCLIENTDATA *pClient) 2428 { 2429 for (int i=0; i<SHFLHANDLE_MAX; i++) 2430 { 2431 SHFLHANDLE Handle = (SHFLHANDLE)i; 2432 if (vbsfQueryHandleType(pClient, Handle)) 2433 { 2434 Log(("Open handle %08x\n", i)); 2435 vbsfClose(pClient, SHFL_HANDLE_ROOT /* incorrect, but it's not important */, (SHFLHANDLE)i); 2436 } 2437 } 2438 return VINF_SUCCESS; 2439 } 742 void vbsfFreeHostPath(char *pszHostPath) 743 { 744 RTMemFree(pszHostPath); 745 }
Note:
See TracChangeset
for help on using the changeset viewer.