VirtualBox

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

Last change on this file since 40913 was 40109, checked in by vboxsync, 13 years ago

HostServices/SharedFolders: clearer code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 74.9 KB
Line 
1/* $Id: vbsf.cpp 40109 2012-02-13 21:11:46Z 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 rc = VERR_NO_MEMORY; /* Default error. */
834 handle = vbsfAllocFileHandle(pClient);
835 if (handle != SHFL_HANDLE_NIL)
836 {
837 pHandle = vbsfQueryFileHandle(pClient, handle);
838 if (pHandle)
839 {
840 rc = RTFileOpen(&pHandle->file.Handle, pszPath, fOpen);
841 }
842 }
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 else
896 {
897 /** @note The shared folder status code is very approximate, as the runtime
898 * does not really provide this information. */
899 pParms->Result = SHFL_FILE_EXISTS; /* We lost the information as to whether it was
900 created when we eliminated the race. */
901 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
902 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
903 || ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS
904 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
905 {
906 /* For now, we do not treat a failure here as fatal. */
907 /* @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if
908 SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
909 RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
910 pParms->Result = SHFL_FILE_REPLACED;
911 }
912 if ( ( SHFL_CF_ACT_FAIL_IF_EXISTS
913 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
914 || ( SHFL_CF_ACT_CREATE_IF_NEW
915 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
916 {
917 pParms->Result = SHFL_FILE_CREATED;
918 }
919#if 0
920 /* @todo */
921 /* Set new attributes. */
922 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
923 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
924 || ( SHFL_CF_ACT_CREATE_IF_NEW
925 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
926 {
927 RTFileSetTimes(pHandle->file.Handle,
928 &pParms->Info.AccessTime,
929 &pParms->Info.ModificationTime,
930 &pParms->Info.ChangeTime,
931 &pParms->Info.BirthTime
932 );
933
934 RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
935 }
936#endif
937 RTFSOBJINFO info;
938
939 /* Get file information */
940 rc = RTFileQueryInfo(pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
941 if (RT_SUCCESS(rc))
942 {
943#ifdef RT_OS_WINDOWS
944 info.Attr.fMode |= 0111;
945#endif
946 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
947 }
948 }
949 /* Free resources if any part of the function has failed. */
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 /** @todo How do wildcards in the path name work? */
1182 testCreateDirSimple(hTest);
1183 /* If the number or types of parameters are wrong the API should fail. */
1184 testCreateBadParameters(hTest);
1185 /* Add tests as required... */
1186}
1187#endif
1188/**
1189 * Create or open a file or folder. Perform character set and case
1190 * conversion on the file name if necessary.
1191 *
1192 * @returns IPRT status code, but see note below
1193 * @param pClient Data structure describing the client accessing the shared
1194 * folder
1195 * @param root The index of the shared folder in the table of mappings.
1196 * The host path of the shared folder is found using this.
1197 * @param pPath The path of the file or folder relative to the host path
1198 * indexed by root.
1199 * @param cbPath Presumably the length of the path in pPath. Actually
1200 * ignored, as pPath contains a length parameter.
1201 * @param pParms->Info If a new file is created or an old one overwritten, set
1202 * these attributes
1203 * @retval pParms->Result Shared folder result code, see include/VBox/shflsvc.h
1204 * @retval pParms->Handle Shared folder handle to the newly opened file
1205 * @retval pParms->Info Attributes of the file or folder opened
1206 *
1207 * @note This function returns success if a "non-exceptional" error occurred,
1208 * such as "no such file". In this case, the caller should check the
1209 * pParms->Result return value and whether pParms->Handle is valid.
1210 */
1211int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
1212{
1213 int rc = VINF_SUCCESS;
1214
1215 LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
1216 pClient, pPath, cbPath, pParms, pParms->CreateFlags));
1217
1218 /* Check the client access rights to the root. */
1219 /** @todo */
1220
1221 /* Build a host full path for the given path, handle file name case issues (if the guest
1222 * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
1223 * necessary.
1224 */
1225 char *pszFullPath = NULL;
1226 uint32_t cbFullPathRoot = 0;
1227
1228 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1229 if (RT_SUCCESS(rc))
1230 {
1231 /* Reset return value in case client forgot to do so.
1232 * pParms->Handle must not be reset here, as it is used
1233 * in vbsfOpenFile to detect old additions.
1234 */
1235 pParms->Result = SHFL_NO_RESULT;
1236
1237 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
1238 {
1239 rc = vbsfLookupFile(pClient, pszFullPath, pParms);
1240 }
1241 else
1242 {
1243 /* Query path information. */
1244 RTFSOBJINFO info;
1245
1246 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1247 LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc));
1248
1249 if (RT_SUCCESS(rc))
1250 {
1251 /* Mark it as a directory in case the caller didn't. */
1252 /**
1253 * @todo I left this in in order not to change the behaviour of the
1254 * function too much. Is it really needed, and should it really be
1255 * here?
1256 */
1257 if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
1258 {
1259 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
1260 }
1261
1262 /**
1263 * @todo This should be in the Windows Guest Additions, as no-one else
1264 * needs it.
1265 */
1266 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
1267 {
1268 vbsfStripLastComponent(pszFullPath, cbFullPathRoot);
1269 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
1270 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
1271 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
1272 pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
1273 pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
1274 }
1275 }
1276
1277 rc = VINF_SUCCESS;
1278
1279 /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation
1280 * will cause changes.
1281 *
1282 * Actual operations (write, set attr, etc), which can write to a shared folder, have
1283 * the check and will return VERR_WRITE_PROTECT if the folder is not writable.
1284 */
1285 if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
1286 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS
1287 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW
1288 )
1289 {
1290 /* is the guest allowed to write to this share? */
1291 bool fWritable;
1292 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
1293 if (RT_FAILURE(rc) || !fWritable)
1294 rc = VERR_WRITE_PROTECT;
1295 }
1296
1297 if (RT_SUCCESS(rc))
1298 {
1299 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
1300 {
1301 rc = vbsfOpenDir(pClient, pszFullPath, pParms);
1302 }
1303 else
1304 {
1305 rc = vbsfOpenFile(pClient, pszFullPath, pParms);
1306 }
1307 }
1308 else
1309 {
1310 pParms->Handle = SHFL_HANDLE_NIL;
1311 }
1312 }
1313
1314 /* free the path string */
1315 vbsfFreeFullPath(pszFullPath);
1316 }
1317
1318 Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
1319
1320 return rc;
1321}
1322
1323#ifdef UNITTEST
1324/** Unit test the SHFL_FN_CLOSE API. Located here as a form of API
1325 * documentation. */
1326void testClose(RTTEST hTest)
1327{
1328 /* If the API parameters are invalid the API should fail. */
1329 testCloseBadParameters(hTest);
1330 /* Add tests as required... */
1331}
1332#endif
1333int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1334{
1335 int rc = VINF_SUCCESS;
1336
1337 LogFlow(("vbsfClose: pClient = %p, Handle = %RX64\n",
1338 pClient, Handle));
1339
1340 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1341 Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0);
1342
1343 switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE))
1344 {
1345 case SHFL_HF_TYPE_DIR:
1346 {
1347 rc = vbsfCloseDir(vbsfQueryDirHandle(pClient, Handle));
1348 break;
1349 }
1350 case SHFL_HF_TYPE_FILE:
1351 {
1352 rc = vbsfCloseFile(vbsfQueryFileHandle(pClient, Handle));
1353 break;
1354 }
1355 default:
1356 return VERR_INVALID_HANDLE;
1357 }
1358 vbsfFreeFileHandle(pClient, Handle);
1359
1360 Log(("vbsfClose: rc = %Rrc\n", rc));
1361
1362 return rc;
1363}
1364
1365#ifdef UNITTEST
1366/** Unit test the SHFL_FN_READ API. Located here as a form of API
1367 * documentation. */
1368void testRead(RTTEST hTest)
1369{
1370 /* If the number or types of parameters are wrong the API should fail. */
1371 testReadBadParameters(hTest);
1372 /* Basic reading from a file. */
1373 testReadFileSimple(hTest);
1374 /* Add tests as required... */
1375}
1376#endif
1377int vbsfRead (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1378{
1379 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1380 size_t count = 0;
1381 int rc;
1382
1383 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1384 {
1385 AssertFailed();
1386 return VERR_INVALID_PARAMETER;
1387 }
1388
1389 Log(("vbsfRead %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer));
1390
1391 if (*pcbBuffer == 0)
1392 return VINF_SUCCESS; /* @todo correct? */
1393
1394
1395 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1396 if (rc != VINF_SUCCESS)
1397 {
1398 AssertRC(rc);
1399 return rc;
1400 }
1401
1402 rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1403 *pcbBuffer = (uint32_t)count;
1404 Log(("RTFileRead returned %Rrc bytes read %x\n", rc, count));
1405 return rc;
1406}
1407
1408#ifdef UNITTEST
1409/** Unit test the SHFL_FN_WRITE API. Located here as a form of API
1410 * documentation. */
1411void testWrite(RTTEST hTest)
1412{
1413 /* If the number or types of parameters are wrong the API should fail. */
1414 testWriteBadParameters(hTest);
1415 /* Simple test of writing to a file. */
1416 testWriteFileSimple(hTest);
1417 /* Add tests as required... */
1418}
1419#endif
1420int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1421{
1422 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1423 size_t count = 0;
1424 int rc;
1425
1426 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1427 {
1428 AssertFailed();
1429 return VERR_INVALID_PARAMETER;
1430 }
1431
1432 Log(("vbsfWrite %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer));
1433
1434 /* Is the guest allowed to write to this share?
1435 * XXX Actually this check was still done in vbsfCreate() -- RTFILE_O_WRITE cannot be set if vbsfMappingsQueryWritable() failed. */
1436 bool fWritable;
1437 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
1438 if (RT_FAILURE(rc) || !fWritable)
1439 return VERR_WRITE_PROTECT;
1440
1441 if (*pcbBuffer == 0)
1442 return VINF_SUCCESS; /** @todo correct? */
1443
1444 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1445 if (rc != VINF_SUCCESS)
1446 {
1447 AssertRC(rc);
1448 return rc;
1449 }
1450
1451 rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1452 *pcbBuffer = (uint32_t)count;
1453 Log(("RTFileWrite returned %Rrc bytes written %x\n", rc, count));
1454 return rc;
1455}
1456
1457
1458#ifdef UNITTEST
1459/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API
1460 * documentation. */
1461void testFlush(RTTEST hTest)
1462{
1463 /* If the number or types of parameters are wrong the API should fail. */
1464 testFlushBadParameters(hTest);
1465 /* Simple opening and flushing of a file. */
1466 testFlushFileSimple(hTest);
1467 /* Add tests as required... */
1468}
1469#endif
1470int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1471{
1472 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1473 int rc = VINF_SUCCESS;
1474
1475 if (pHandle == 0)
1476 {
1477 AssertFailed();
1478 return VERR_INVALID_HANDLE;
1479 }
1480
1481 Log(("vbsfFlush %RX64\n", Handle));
1482 rc = RTFileFlush(pHandle->file.Handle);
1483 AssertRC(rc);
1484 return rc;
1485}
1486
1487#ifdef UNITTEST
1488/** Unit test the SHFL_FN_LIST API. Located here as a form of API
1489 * documentation. */
1490void testDirList(RTTEST hTest)
1491{
1492 /* If the number or types of parameters are wrong the API should fail. */
1493 testDirListBadParameters(hTest);
1494 /* Test listing an empty directory (simple edge case). */
1495 testDirListEmpty(hTest);
1496 /* Add tests as required... */
1497}
1498#endif
1499int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
1500 uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
1501{
1502 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1503 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1504 uint32_t cbDirEntry, cbBufferOrg;
1505 int rc = VINF_SUCCESS;
1506 PSHFLDIRINFO pSFDEntry;
1507 PRTUTF16 pwszString;
1508 PRTDIR DirHandle;
1509 bool fUtf8;
1510
1511 fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1512
1513 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1514 {
1515 AssertFailed();
1516 return VERR_INVALID_PARAMETER;
1517 }
1518 Assert(pIndex && *pIndex == 0);
1519 DirHandle = pHandle->dir.Handle;
1520
1521 cbDirEntry = 4096;
1522 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1523 if (pDirEntry == 0)
1524 {
1525 AssertFailed();
1526 return VERR_NO_MEMORY;
1527 }
1528
1529 cbBufferOrg = *pcbBuffer;
1530 *pcbBuffer = 0;
1531 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1532
1533 *pIndex = 1; /* not yet complete */
1534 *pcFiles = 0;
1535
1536 if (pPath)
1537 {
1538 if (pHandle->dir.SearchHandle == 0)
1539 {
1540 /* Build a host full path for the given path
1541 * and convert ucs2 to utf8 if necessary.
1542 */
1543 char *pszFullPath = NULL;
1544
1545 Assert(pHandle->dir.pLastValidEntry == 0);
1546
1547 rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size, &pszFullPath, NULL, true);
1548
1549 if (RT_SUCCESS(rc))
1550 {
1551 rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0);
1552
1553 /* free the path string */
1554 vbsfFreeFullPath(pszFullPath);
1555
1556 if (RT_FAILURE(rc))
1557 goto end;
1558 }
1559 else
1560 goto end;
1561 }
1562 Assert(pHandle->dir.SearchHandle);
1563 DirHandle = pHandle->dir.SearchHandle;
1564 }
1565
1566 while (cbBufferOrg)
1567 {
1568 size_t cbDirEntrySize = cbDirEntry;
1569 uint32_t cbNeeded;
1570
1571 /* Do we still have a valid last entry for the active search? If so, then return it here */
1572 if (pHandle->dir.pLastValidEntry)
1573 {
1574 pDirEntry = pHandle->dir.pLastValidEntry;
1575 }
1576 else
1577 {
1578 pDirEntry = pDirEntryOrg;
1579
1580 rc = RTDirReadEx(DirHandle, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1581 if (rc == VERR_NO_MORE_FILES)
1582 {
1583 *pIndex = 0; /* listing completed */
1584 break;
1585 }
1586
1587 if ( rc != VINF_SUCCESS
1588 && rc != VWRN_NO_DIRENT_INFO)
1589 {
1590 //AssertFailed();
1591 if ( rc == VERR_NO_TRANSLATION
1592 || rc == VERR_INVALID_UTF8_ENCODING)
1593 continue;
1594 break;
1595 }
1596 }
1597
1598 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String);
1599 if (fUtf8)
1600 cbNeeded += pDirEntry->cbName + 1;
1601 else
1602 /* Overestimating, but that's ok */
1603 cbNeeded += (pDirEntry->cbName + 1) * 2;
1604
1605 if (cbBufferOrg < cbNeeded)
1606 {
1607 /* No room, so save this directory entry, or else it's lost forever */
1608 pHandle->dir.pLastValidEntry = pDirEntry;
1609
1610 if (*pcFiles == 0)
1611 {
1612 AssertFailed();
1613 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1614 }
1615 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1616 }
1617
1618#ifdef RT_OS_WINDOWS
1619 pDirEntry->Info.Attr.fMode |= 0111;
1620#endif
1621 vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
1622 pSFDEntry->cucShortName = 0;
1623
1624 if (fUtf8)
1625 {
1626 void *src, *dst;
1627
1628 src = &pDirEntry->szName[0];
1629 dst = &pSFDEntry->name.String.utf8[0];
1630
1631 memcpy(dst, src, pDirEntry->cbName + 1);
1632
1633 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1634 pSFDEntry->name.u16Length = pDirEntry->cbName;
1635 }
1636 else
1637 {
1638 pSFDEntry->name.String.ucs2[0] = 0;
1639 pwszString = pSFDEntry->name.String.ucs2;
1640 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1641 AssertRC(rc2);
1642
1643#ifdef RT_OS_DARWIN
1644/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1645 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
1646 * system level in darwin, or just by the user mode application libs. */
1647 {
1648 // Convert to
1649 // Normalization Form C (composed Unicode). We need this because
1650 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1651 // while most other OS', server-side programs usually expect NFC.
1652 uint16_t ucs2Length;
1653 CFRange rangeCharacters;
1654 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1655
1656 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString));
1657 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1658 ucs2Length = ::CFStringGetLength(inStr);
1659
1660 rangeCharacters.location = 0;
1661 rangeCharacters.length = ucs2Length;
1662 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1663 pwszString[ucs2Length] = 0x0000; // NULL terminated
1664
1665 CFRelease(inStr);
1666 }
1667#endif
1668 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2;
1669 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1670
1671 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1672 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1673
1674 // adjust cbNeeded (it was overestimated before)
1675 cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1676 }
1677
1678 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1679 *pcbBuffer += cbNeeded;
1680 cbBufferOrg-= cbNeeded;
1681
1682 *pcFiles += 1;
1683
1684 /* Free the saved last entry, that we've just returned */
1685 if (pHandle->dir.pLastValidEntry)
1686 {
1687 RTMemFree(pHandle->dir.pLastValidEntry);
1688 pHandle->dir.pLastValidEntry = NULL;
1689 }
1690
1691 if (flags & SHFL_LIST_RETURN_ONE)
1692 break; /* we're done */
1693 }
1694 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1695
1696end:
1697 if (pDirEntry)
1698 RTMemFree(pDirEntry);
1699
1700 return rc;
1701}
1702
1703#ifdef UNITTEST
1704/** Unit test the SHFL_FN_READLINK API. Located here as a form of API
1705 * documentation. */
1706void testReadLink(RTTEST hTest)
1707{
1708 /* If the number or types of parameters are wrong the API should fail. */
1709 testReadLinkBadParameters(hTest);
1710 /* Add tests as required... */
1711}
1712#endif
1713int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
1714{
1715 int rc = VINF_SUCCESS;
1716
1717 if (pPath == 0 || pBuffer == 0)
1718 {
1719 AssertFailed();
1720 return VERR_INVALID_PARAMETER;
1721 }
1722
1723 /* Build a host full path for the given path, handle file name case issues
1724 * (if the guest expects case-insensitive paths but the host is
1725 * case-sensitive) and convert ucs2 to utf8 if necessary.
1726 */
1727 char *pszFullPath = NULL;
1728 uint32_t cbFullPathRoot = 0;
1729
1730 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1731
1732 if (RT_SUCCESS(rc))
1733 {
1734 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0);
1735
1736 /* free the path string */
1737 vbsfFreeFullPath(pszFullPath);
1738 }
1739
1740 return rc;
1741}
1742
1743int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1744{
1745 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1746 int rc = VINF_SUCCESS;
1747 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
1748 RTFSOBJINFO fileinfo;
1749
1750
1751 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
1752 || pcbBuffer == 0
1753 || pObjInfo == 0
1754 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1755 {
1756 AssertFailed();
1757 return VERR_INVALID_PARAMETER;
1758 }
1759
1760 /* @todo other options */
1761 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1762
1763 *pcbBuffer = 0;
1764
1765 if (type == SHFL_HF_TYPE_DIR)
1766 {
1767 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1768 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1769 }
1770 else
1771 {
1772 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1773 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1774#ifdef RT_OS_WINDOWS
1775 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1776 pObjInfo->Attr.fMode |= 0111;
1777#endif
1778 }
1779 if (rc == VINF_SUCCESS)
1780 {
1781 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
1782 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1783 }
1784 else
1785 AssertFailed();
1786
1787 return rc;
1788}
1789
1790static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1791{
1792 uint32_t type = vbsfQueryHandleType(pClient, Handle);
1793 int rc = VINF_SUCCESS;
1794 SHFLFSOBJINFO *pSFDEntry;
1795
1796 if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE)
1797 || pcbBuffer == 0
1798 || pBuffer == 0
1799 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1800 {
1801 AssertFailed();
1802 return VERR_INVALID_PARAMETER;
1803 }
1804
1805 *pcbBuffer = 0;
1806 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1807
1808 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1809
1810 /* Change only the time values that are not zero */
1811 if (type == SHFL_HF_TYPE_DIR)
1812 {
1813 SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle);
1814 rc = RTDirSetTimes(pHandle->dir.Handle,
1815 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1816 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1817 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1818 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1819 );
1820 }
1821 else
1822 {
1823 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1824 rc = RTFileSetTimes(pHandle->file.Handle,
1825 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1826 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1827 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1828 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1829 );
1830 }
1831 if (rc != VINF_SUCCESS)
1832 {
1833 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1834 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1835 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1836 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1837 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
1838 /* temporary hack */
1839 rc = VINF_SUCCESS;
1840 }
1841
1842 if (type == SHFL_HF_TYPE_FILE)
1843 {
1844 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1845 /* Change file attributes if necessary */
1846 if (pSFDEntry->Attr.fMode)
1847 {
1848 RTFMODE fMode = pSFDEntry->Attr.fMode;
1849
1850#ifndef RT_OS_WINDOWS
1851 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
1852 * able to access this file anymore. Only for guests, which set the UNIX mode. */
1853 if (fMode & RTFS_UNIX_MASK)
1854 fMode |= RTFS_UNIX_IRUSR;
1855#endif
1856
1857 rc = RTFileSetMode(pHandle->file.Handle, fMode);
1858 if (rc != VINF_SUCCESS)
1859 {
1860 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
1861 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1862 rc = VINF_SUCCESS;
1863 }
1864 }
1865 }
1866 /* TODO: mode for directories */
1867
1868 if (rc == VINF_SUCCESS)
1869 {
1870 uint32_t bufsize = sizeof(*pSFDEntry);
1871
1872 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1873 if (rc == VINF_SUCCESS)
1874 {
1875 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1876 }
1877 else
1878 AssertFailed();
1879 }
1880
1881 return rc;
1882}
1883
1884
1885static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1886{
1887 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
1888 int rc = VINF_SUCCESS;
1889 SHFLFSOBJINFO *pSFDEntry;
1890
1891 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1892 {
1893 AssertFailed();
1894 return VERR_INVALID_PARAMETER;
1895 }
1896
1897 *pcbBuffer = 0;
1898 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1899
1900 if (flags & SHFL_INFO_SIZE)
1901 {
1902 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1903 if (rc != VINF_SUCCESS)
1904 AssertFailed();
1905 }
1906 else
1907 AssertFailed();
1908
1909 if (rc == VINF_SUCCESS)
1910 {
1911 RTFSOBJINFO fileinfo;
1912
1913 /* Query the new object info and return it */
1914 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1915 if (rc == VINF_SUCCESS)
1916 {
1917#ifdef RT_OS_WINDOWS
1918 fileinfo.Attr.fMode |= 0111;
1919#endif
1920 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
1921 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1922 }
1923 else
1924 AssertFailed();
1925 }
1926
1927 return rc;
1928}
1929
1930int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1931{
1932 int rc = VINF_SUCCESS;
1933 SHFLVOLINFO *pSFDEntry;
1934 char *pszFullPath = NULL;
1935 SHFLSTRING dummy;
1936
1937 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1938 {
1939 AssertFailed();
1940 return VERR_INVALID_PARAMETER;
1941 }
1942
1943 /* @todo other options */
1944 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1945
1946 *pcbBuffer = 0;
1947 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1948
1949 ShflStringInitBuffer(&dummy, sizeof(dummy));
1950 rc = vbsfBuildFullPath(pClient, root, &dummy, 0, &pszFullPath, NULL);
1951
1952 if (RT_SUCCESS(rc))
1953 {
1954 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1955 if (rc != VINF_SUCCESS)
1956 goto exit;
1957
1958 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1959 if (rc != VINF_SUCCESS)
1960 goto exit;
1961
1962 RTFSPROPERTIES FsProperties;
1963 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
1964 if (rc != VINF_SUCCESS)
1965 goto exit;
1966 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
1967
1968 *pcbBuffer = sizeof(SHFLVOLINFO);
1969 }
1970 else AssertFailed();
1971
1972exit:
1973 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
1974 /* free the path string */
1975 vbsfFreeFullPath(pszFullPath);
1976 return rc;
1977}
1978
1979int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1980{
1981 if (pcbBuffer == 0 || pBuffer == 0)
1982 {
1983 AssertFailed();
1984 return VERR_INVALID_PARAMETER;
1985 }
1986
1987 if (flags & SHFL_INFO_FILE)
1988 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1989
1990 if (flags & SHFL_INFO_VOLUME)
1991 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
1992
1993 AssertFailed();
1994 return VERR_INVALID_PARAMETER;
1995}
1996
1997#ifdef UNITTEST
1998/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API
1999 * documentation. */
2000void testFSInfo(RTTEST hTest)
2001{
2002 /* If the number or types of parameters are wrong the API should fail. */
2003 testFSInfoBadParameters(hTest);
2004 /* Basic get and set file size test. */
2005 testFSInfoQuerySetFMode(hTest);
2006 /* Basic get and set dir atime test. */
2007 testFSInfoQuerySetDirATime(hTest);
2008 /* Basic get and set file atime test. */
2009 testFSInfoQuerySetFileATime(hTest);
2010 /* Basic set end of file. */
2011 testFSInfoQuerySetEndOfFile(hTest);
2012 /* Add tests as required... */
2013}
2014#endif
2015int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
2016{
2017 uint32_t type = vbsfQueryHandleType(pClient, Handle)
2018 & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
2019
2020 if (type == 0 || pcbBuffer == 0 || pBuffer == 0)
2021 {
2022 AssertFailed();
2023 return VERR_INVALID_PARAMETER;
2024 }
2025
2026 /* is the guest allowed to write to this share? */
2027 bool fWritable;
2028 int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2029 if (RT_FAILURE(rc) || !fWritable)
2030 return VERR_WRITE_PROTECT;
2031
2032 if (flags & SHFL_INFO_FILE)
2033 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2034
2035 if (flags & SHFL_INFO_SIZE)
2036 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2037
2038// if (flags & SHFL_INFO_VOLUME)
2039// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
2040 AssertFailed();
2041 return VERR_INVALID_PARAMETER;
2042}
2043
2044#ifdef UNITTEST
2045/** Unit test the SHFL_FN_LOCK API. Located here as a form of API
2046 * documentation. */
2047void testLock(RTTEST hTest)
2048{
2049 /* If the number or types of parameters are wrong the API should fail. */
2050 testLockBadParameters(hTest);
2051 /* Simple file locking and unlocking test. */
2052 testLockFileSimple(hTest);
2053 /* Add tests as required... */
2054}
2055#endif
2056int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2057{
2058 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2059 uint32_t fRTLock = 0;
2060 int rc;
2061
2062 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
2063
2064 if (pHandle == 0)
2065 {
2066 AssertFailed();
2067 return VERR_INVALID_HANDLE;
2068 }
2069 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
2070 || (flags & SHFL_LOCK_ENTIRE)
2071 )
2072 {
2073 AssertFailed();
2074 return VERR_INVALID_PARAMETER;
2075 }
2076
2077 /* Lock type */
2078 switch(flags & SHFL_LOCK_MODE_MASK)
2079 {
2080 case SHFL_LOCK_SHARED:
2081 fRTLock = RTFILE_LOCK_READ;
2082 break;
2083
2084 case SHFL_LOCK_EXCLUSIVE:
2085 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
2086 break;
2087
2088 default:
2089 AssertFailed();
2090 return VERR_INVALID_PARAMETER;
2091 }
2092
2093 /* Lock wait type */
2094 if (flags & SHFL_LOCK_WAIT)
2095 fRTLock |= RTFILE_LOCK_WAIT;
2096 else
2097 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
2098
2099#ifdef RT_OS_WINDOWS
2100 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
2101 if (rc != VINF_SUCCESS)
2102 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2103#else
2104 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
2105 rc = VINF_SUCCESS;
2106#endif
2107 return rc;
2108}
2109
2110int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2111{
2112 SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle);
2113 int rc;
2114
2115 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
2116
2117 if (pHandle == 0)
2118 {
2119 return VERR_INVALID_HANDLE;
2120 }
2121 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
2122 || (flags & SHFL_LOCK_ENTIRE)
2123 )
2124 {
2125 return VERR_INVALID_PARAMETER;
2126 }
2127
2128#ifdef RT_OS_WINDOWS
2129 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
2130 if (rc != VINF_SUCCESS)
2131 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2132#else
2133 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
2134 rc = VINF_SUCCESS;
2135#endif
2136
2137 return rc;
2138}
2139
2140
2141#ifdef UNITTEST
2142/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API
2143 * documentation. */
2144void testRemove(RTTEST hTest)
2145{
2146 /* If the number or types of parameters are wrong the API should fail. */
2147 testRemoveBadParameters(hTest);
2148 /* Add tests as required... */
2149}
2150#endif
2151int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
2152{
2153 int rc = VINF_SUCCESS;
2154
2155 /* Validate input */
2156 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK)
2157 || cbPath == 0
2158 || pPath == 0)
2159 {
2160 AssertFailed();
2161 return VERR_INVALID_PARAMETER;
2162 }
2163
2164 /* Build a host full path for the given path
2165 * and convert ucs2 to utf8 if necessary.
2166 */
2167 char *pszFullPath = NULL;
2168
2169 rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL);
2170 if (RT_SUCCESS(rc))
2171 {
2172 /* is the guest allowed to write to this share? */
2173 bool fWritable;
2174 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2175 if (RT_FAILURE(rc) || !fWritable)
2176 rc = VERR_WRITE_PROTECT;
2177
2178 if (RT_SUCCESS(rc))
2179 {
2180 if (flags & SHFL_REMOVE_SYMLINK)
2181 rc = RTSymlinkDelete(pszFullPath, 0);
2182 else if (flags & SHFL_REMOVE_FILE)
2183 rc = RTFileDelete(pszFullPath);
2184 else
2185 rc = RTDirRemove(pszFullPath);
2186 }
2187
2188#ifndef DEBUG_dmik
2189 // VERR_ACCESS_DENIED for example?
2190 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
2191#endif
2192 /* free the path string */
2193 vbsfFreeFullPath(pszFullPath);
2194 }
2195 return rc;
2196}
2197
2198
2199#ifdef UNITTEST
2200/** Unit test the SHFL_FN_RENAME API. Located here as a form of API
2201 * documentation. */
2202void testRename(RTTEST hTest)
2203{
2204 /* If the number or types of parameters are wrong the API should fail. */
2205 testRenameBadParameters(hTest);
2206 /* Add tests as required... */
2207}
2208#endif
2209int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
2210{
2211 int rc = VINF_SUCCESS;
2212
2213 /* Validate input */
2214 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
2215 || pSrc == 0
2216 || pDest == 0)
2217 {
2218 AssertFailed();
2219 return VERR_INVALID_PARAMETER;
2220 }
2221
2222 /* Build a host full path for the given path
2223 * and convert ucs2 to utf8 if necessary.
2224 */
2225 char *pszFullPathSrc = NULL;
2226 char *pszFullPathDest = NULL;
2227
2228 rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size, &pszFullPathSrc, NULL);
2229 if (rc != VINF_SUCCESS)
2230 return rc;
2231
2232 rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size, &pszFullPathDest, NULL, false, true);
2233 if (RT_SUCCESS (rc))
2234 {
2235 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
2236
2237 /* is the guest allowed to write to this share? */
2238 bool fWritable;
2239 rc = vbsfMappingsQueryWritable(pClient, root, &fWritable);
2240 if (RT_FAILURE(rc) || !fWritable)
2241 rc = VERR_WRITE_PROTECT;
2242
2243 if (RT_SUCCESS(rc))
2244 {
2245 if (flags & SHFL_RENAME_FILE)
2246 {
2247 rc = RTFileMove(pszFullPathSrc, pszFullPathDest,
2248 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0));
2249 }
2250 else
2251 {
2252 /* NT ignores the REPLACE flag and simply return and already exists error. */
2253 rc = RTDirRename(pszFullPathSrc, pszFullPathDest,
2254 ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0));
2255 }
2256 }
2257
2258#ifndef DEBUG_dmik
2259 AssertRC(rc);
2260#endif
2261 /* free the path string */
2262 vbsfFreeFullPath(pszFullPathDest);
2263 }
2264 /* free the path string */
2265 vbsfFreeFullPath(pszFullPathSrc);
2266 return rc;
2267}
2268
2269#ifdef UNITTEST
2270/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API
2271 * documentation. */
2272void testSymlink(RTTEST hTest)
2273{
2274 /* If the number or types of parameters are wrong the API should fail. */
2275 testSymlinkBadParameters(hTest);
2276 /* Add tests as required... */
2277}
2278#endif
2279int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
2280{
2281 int rc = VINF_SUCCESS;
2282
2283 char *pszFullNewPath = NULL;
2284 const char *pszOldPath = (const char *)pOldPath->String.utf8;
2285
2286 /* XXX: no support for UCS2 at the moment. */
2287 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
2288 return VERR_NOT_IMPLEMENTED;
2289
2290 bool fSymlinksCreate;
2291 rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate);
2292 AssertRCReturn(rc, rc);
2293 if (!fSymlinksCreate)
2294 return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */
2295
2296 rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size, &pszFullNewPath, NULL);
2297 AssertRCReturn(rc, rc);
2298
2299 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8,
2300 RTSYMLINKTYPE_UNKNOWN, 0);
2301 if (RT_SUCCESS(rc))
2302 {
2303 RTFSOBJINFO info;
2304 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
2305 if (RT_SUCCESS(rc))
2306 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2307 }
2308
2309 vbsfFreeFullPath(pszFullNewPath);
2310
2311 return rc;
2312}
2313
2314/*
2315 * Clean up our mess by freeing all handles that are still valid.
2316 *
2317 */
2318int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2319{
2320 for (int i=0; i<SHFLHANDLE_MAX; i++)
2321 {
2322 SHFLHANDLE Handle = (SHFLHANDLE)i;
2323 if (vbsfQueryHandleType(pClient, Handle))
2324 {
2325 Log(("Open handle %08x\n", i));
2326 vbsfClose(pClient, SHFL_HANDLE_ROOT /* incorrect, but it's not important */, (SHFLHANDLE)i);
2327 }
2328 }
2329 return VINF_SUCCESS;
2330}
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