VirtualBox

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

Last change on this file since 39913 was 39646, checked in by vboxsync, 13 years ago

SharedFolders: more fixes

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