VirtualBox

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

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

Shared folders: don't abort the directory listing if an entry with an invalid encoding was found

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

© 2023 Oracle
ContactPrivacy policyTerms of Use