VirtualBox

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

Last change on this file since 30268 was 30268, checked in by vboxsync, 14 years ago

logging fix

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

© 2023 Oracle
ContactPrivacy policyTerms of Use