VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedFolders/vbsf.cpp@ 49762

Last change on this file since 49762 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette