VirtualBox

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

Last change on this file since 39230 was 38736, checked in by vboxsync, 13 years ago

*: Please don NOT redefine logger macros.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.2 KB
Line 
1/* $Id: vbsf.cpp 38736 2011-09-13 13:58:47Z vboxsync $ */
2/** @file
3 * Shared Folders - VBox Shared Folders.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "mappings.h"
19#include "vbsf.h"
20#include "shflhandle.h"
21
22#include <iprt/alloc.h>
23#include <iprt/assert.h>
24#include <iprt/fs.h>
25#include <iprt/dir.h>
26#include <iprt/file.h>
27#include <iprt/path.h>
28#include <iprt/string.h>
29#include <iprt/symlink.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#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK)
37
38/**
39 * @todo find a better solution for supporting the execute bit for non-windows
40 * guests on windows host. Search for "0111" to find all the relevant places.
41 */
42
43void vbsfStripLastComponent (char *pszFullPath, uint32_t cbFullPathRoot)
44{
45 RTUNICP cp;
46
47 /* Do not strip root. */
48 char *s = pszFullPath + cbFullPathRoot;
49 char *delimSecondLast = NULL;
50 char *delimLast = NULL;
51
52 LogFlowFunc(("%s -> %s\n", pszFullPath, s));
53
54 for (;;)
55 {
56 cp = RTStrGetCp(s);
57
58 if (cp == RTUNICP_INVALID || cp == 0)
59 {
60 break;
61 }
62
63 if (cp == RTPATH_DELIMITER)
64 {
65 if (delimLast != NULL)
66 {
67 delimSecondLast = delimLast;
68 }
69
70 delimLast = s;
71 }
72
73 s = RTStrNextCp (s);
74 }
75
76 if (cp == 0)
77 {
78 if (delimLast + 1 == s)
79 {
80 if (delimSecondLast)
81 {
82 *delimSecondLast = 0;
83 }
84 else if (delimLast)
85 {
86 *delimLast = 0;
87 }
88 }
89 else
90 {
91 if (delimLast)
92 {
93 *delimLast = 0;
94 }
95 }
96 }
97
98 LogFlowFunc(("%s, %s, %s\n", pszFullPath, delimLast, delimSecondLast));
99}
100
101static int vbsfCorrectCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, char *pszStartComponent)
102{
103 PRTDIRENTRYEX pDirEntry = NULL;
104 uint32_t cbDirEntry, cbComponent;
105 int rc = VERR_FILE_NOT_FOUND;
106 PRTDIR hSearch = 0;
107 char szWildCard[4];
108
109 Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent));
110
111 cbComponent = (uint32_t) strlen(pszStartComponent);
112
113 cbDirEntry = 4096;
114 pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
115 if (pDirEntry == 0)
116 {
117 AssertFailed();
118 return VERR_NO_MEMORY;
119 }
120
121 /** @todo this is quite inefficient, especially for directories with many files */
122 Assert(pszFullPath < pszStartComponent-1);
123 Assert(*(pszStartComponent-1) == RTPATH_DELIMITER);
124 *(pszStartComponent-1) = 0;
125 strcpy(pDirEntry->szName, pszFullPath);
126 szWildCard[0] = RTPATH_DELIMITER;
127 szWildCard[1] = '*';
128 szWildCard[2] = 0;
129 strcat(pDirEntry->szName, szWildCard);
130
131 rc = RTDirOpenFiltered (&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT);
132 *(pszStartComponent-1) = RTPATH_DELIMITER;
133 if (RT_FAILURE(rc))
134 goto end;
135
136 for (;;)
137 {
138 size_t cbDirEntrySize = cbDirEntry;
139
140 rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
141 if (rc == VERR_NO_MORE_FILES)
142 break;
143
144 if ( rc != VINF_SUCCESS
145 && rc != VWRN_NO_DIRENT_INFO)
146 {
147 AssertFailed();
148 if ( rc == VERR_NO_TRANSLATION
149 || rc == VERR_INVALID_UTF8_ENCODING)
150 continue;
151 break;
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 (virtual) 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, SHFL_RT_LINK(pClient));
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 insensitive 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, SHFL_RT_LINK(pClient));
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, SHFL_RT_LINK(pClient));
546 Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND);
547 }
548 else if (end == src)
549 rc = VINF_SUCCESS; /* trailing delimiter */
550 else
551 rc = VERR_FILE_NOT_FOUND;
552
553 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
554 {
555 /* path component is invalid; try to correct the casing */
556 rc = vbsfCorrectCasing(pClient, pszFullPath, src);
557 if (RT_FAILURE(rc))
558 {
559 if (!fEndOfString)
560 *end = RTPATH_DELIMITER; /* restore the original full path */
561 break;
562 }
563 }
564
565 if (fEndOfString)
566 break;
567
568 *end = RTPATH_DELIMITER;
569 src = end + 1;
570 }
571 if (RT_FAILURE(rc))
572 Log(("Unable to find suitable component rc=%d\n", rc));
573 }
574 else
575 rc = VERR_FILE_NOT_FOUND;
576
577 }
578 if (pszLastComponent)
579 *pszLastComponent = RTPATH_DELIMITER;
580
581 /* might be a new file so don't fail here! */
582 rc = VINF_SUCCESS;
583 }
584 *ppszFullPath = pszFullPath;
585
586 LogFlow(("vbsfBuildFullPath: %s rc=%Rrc\n", pszFullPath, rc));
587 }
588
589 return rc;
590}
591
592static void vbsfFreeFullPath (char *pszFullPath)
593{
594 RTMemFree (pszFullPath);
595}
596
597/**
598 * Convert shared folder create flags (see include/iprt/shflsvc.h) into iprt create flags.
599 *
600 * @returns iprt status code
601 * @param fShflFlags shared folder create flags
602 * @param fMode file attributes
603 * @retval pfOpen iprt create flags
604 */
605static int vbsfConvertFileOpenFlags(unsigned fShflFlags, RTFMODE fMode, SHFLHANDLE handleInitial, uint32_t *pfOpen)
606{
607 uint32_t fOpen = 0;
608 int rc = VINF_SUCCESS;
609
610 if ( (fMode & RTFS_DOS_MASK) != 0
611 && (fMode & RTFS_UNIX_MASK) == 0)
612 {
613 /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*.
614 * @todo this is based on rtFsModeNormalize/rtFsModeFromDos.
615 * May be better to use RTFsModeNormalize here.
616 */
617 fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
618 /* x for directories. */
619 if (fMode & RTFS_DOS_DIRECTORY)
620 fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
621 /* writable? */
622 if (!(fMode & RTFS_DOS_READONLY))
623 fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
624
625 /* Set the requested mode using only allowed bits. */
626 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
627 }
628 else
629 {
630 /* Old linux and solaris additions did not initialize the Info.Attr.fMode field
631 * and it contained random bits from stack. Detect this using the handle field value
632 * passed from the guest: old additions set it (incorrectly) to 0, new additions
633 * set it to SHFL_HANDLE_NIL(~0).
634 */
635 if (handleInitial == 0)
636 {
637 /* Old additions. Do nothing, use default mode. */
638 }
639 else
640 {
641 /* New additions or Windows additions. Set the requested mode using only allowed bits.
642 * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode
643 * will be set in fOpen.
644 */
645 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
646 }
647 }
648
649 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_RW))
650 {
651 default:
652 case SHFL_CF_ACCESS_NONE:
653 {
654 /** @todo treat this as read access, but theoretically this could be a no access request. */
655 fOpen |= RTFILE_O_READ;
656 Log(("FLAG: SHFL_CF_ACCESS_NONE\n"));
657 break;
658 }
659
660 case SHFL_CF_ACCESS_READ:
661 {
662 fOpen |= RTFILE_O_READ;
663 Log(("FLAG: SHFL_CF_ACCESS_READ\n"));
664 break;
665 }
666
667 case SHFL_CF_ACCESS_WRITE:
668 {
669 fOpen |= RTFILE_O_WRITE;
670 Log(("FLAG: SHFL_CF_ACCESS_WRITE\n"));
671 break;
672 }
673
674 case SHFL_CF_ACCESS_READWRITE:
675 {
676 fOpen |= RTFILE_O_READWRITE;
677 Log(("FLAG: SHFL_CF_ACCESS_READWRITE\n"));
678 break;
679 }
680 }
681
682 if (fShflFlags & SHFL_CF_ACCESS_APPEND)
683 {
684 fOpen |= RTFILE_O_APPEND;
685 }
686
687 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR))
688 {
689 default:
690 case SHFL_CF_ACCESS_ATTR_NONE:
691 {
692 fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
693 Log(("FLAG: SHFL_CF_ACCESS_ATTR_NONE\n"));
694 break;
695 }
696
697 case SHFL_CF_ACCESS_ATTR_READ:
698 {
699 fOpen |= RTFILE_O_ACCESS_ATTR_READ;
700 Log(("FLAG: SHFL_CF_ACCESS_ATTR_READ\n"));
701 break;
702 }
703
704 case SHFL_CF_ACCESS_ATTR_WRITE:
705 {
706 fOpen |= RTFILE_O_ACCESS_ATTR_WRITE;
707 Log(("FLAG: SHFL_CF_ACCESS_ATTR_WRITE\n"));
708 break;
709 }
710
711 case SHFL_CF_ACCESS_ATTR_READWRITE:
712 {
713 fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE;
714 Log(("FLAG: SHFL_CF_ACCESS_ATTR_READWRITE\n"));
715 break;
716 }
717 }
718
719 /* Sharing mask */
720 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_DENY))
721 {
722 default:
723 case SHFL_CF_ACCESS_DENYNONE:
724 fOpen |= RTFILE_O_DENY_NONE;
725 Log(("FLAG: SHFL_CF_ACCESS_DENYNONE\n"));
726 break;
727
728 case SHFL_CF_ACCESS_DENYREAD:
729 fOpen |= RTFILE_O_DENY_READ;
730 Log(("FLAG: SHFL_CF_ACCESS_DENYREAD\n"));
731 break;
732
733 case SHFL_CF_ACCESS_DENYWRITE:
734 fOpen |= RTFILE_O_DENY_WRITE;
735 Log(("FLAG: SHFL_CF_ACCESS_DENYWRITE\n"));
736 break;
737
738 case SHFL_CF_ACCESS_DENYALL:
739 fOpen |= RTFILE_O_DENY_ALL;
740 Log(("FLAG: SHFL_CF_ACCESS_DENYALL\n"));
741 break;
742 }
743
744 /* Open/Create action mask */
745 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
746 {
747 case SHFL_CF_ACT_OPEN_IF_EXISTS:
748 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
749 {
750 fOpen |= RTFILE_O_OPEN_CREATE;
751 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
752 }
753 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
754 {
755 fOpen |= RTFILE_O_OPEN;
756 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
757 }
758 else
759 {
760 Log(("FLAGS: invalid open/create action combination\n"));
761 rc = VERR_INVALID_PARAMETER;
762 }
763 break;
764 case SHFL_CF_ACT_FAIL_IF_EXISTS:
765 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
766 {
767 fOpen |= RTFILE_O_CREATE;
768 Log(("FLAGS: SHFL_CF_ACT_FAIL_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
769 }
770 else
771 {
772 Log(("FLAGS: invalid open/create action combination\n"));
773 rc = VERR_INVALID_PARAMETER;
774 }
775 break;
776 case SHFL_CF_ACT_REPLACE_IF_EXISTS:
777 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
778 {
779 fOpen |= RTFILE_O_CREATE_REPLACE;
780 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
781 }
782 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
783 {
784 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
785 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
786 }
787 else
788 {
789 Log(("FLAGS: invalid open/create action combination\n"));
790 rc = VERR_INVALID_PARAMETER;
791 }
792 break;
793 case SHFL_CF_ACT_OVERWRITE_IF_EXISTS:
794 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
795 {
796 fOpen |= RTFILE_O_CREATE_REPLACE;
797 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
798 }
799 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
800 {
801 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
802 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
803 }
804 else
805 {
806 Log(("FLAGS: invalid open/create action combination\n"));
807 rc = VERR_INVALID_PARAMETER;
808 }
809 break;
810 default:
811 rc = VERR_INVALID_PARAMETER;
812 Log(("FLAG: SHFL_CF_ACT_MASK_IF_EXISTS - invalid parameter\n"));
813 }
814
815 if (RT_SUCCESS(rc))
816 {
817 *pfOpen = fOpen;
818 }
819 return rc;
820}
821
822/**
823 * Open a file or create and open a new one.
824 *
825 * @returns IPRT status code
826 * @param pClient Data structure describing the client accessing the shared folder
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 (SHFLCLIENTDATA *pClient, 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, SHFL_RT_LINK(pClient))))
886 {
887#ifdef RT_OS_WINDOWS
888 info.Attr.fMode |= 0111;
889#endif
890 vbfsCopyFsObjInfoFromIprt(&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 (ulimit -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 vbfsCopyFsObjInfoFromIprt(&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 vbfsCopyFsObjInfoFromIprt(&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(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms)
1154{
1155 RTFSOBJINFO info;
1156 int rc;
1157
1158 rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
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 vbfsCopyFsObjInfoFromIprt(&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(pClient, pszFullPath, pParms);
1244 }
1245 else
1246 {
1247 /* Query path information. */
1248 RTFSOBJINFO info;
1249
1250 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
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 (pClient, 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, SHFL_RT_LINK(pClient));
1530 if (rc == VERR_NO_MORE_FILES)
1531 {
1532 *pIndex = 0; /* listing completed */
1533 break;
1534 }
1535
1536 if ( rc != VINF_SUCCESS
1537 && rc != VWRN_NO_DIRENT_INFO)
1538 {
1539 //AssertFailed();
1540 if ( rc == VERR_NO_TRANSLATION
1541 || rc == VERR_INVALID_UTF8_ENCODING)
1542 continue;
1543 break;
1544 }
1545 }
1546
1547 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String);
1548 if (fUtf8)
1549 cbNeeded += pDirEntry->cbName + 1;
1550 else
1551 /* Overestimating, but that's ok */
1552 cbNeeded += (pDirEntry->cbName + 1) * 2;
1553
1554 if (cbBufferOrg < cbNeeded)
1555 {
1556 /* No room, so save this directory entry, or else it's lost forever */
1557 pHandle->dir.pLastValidEntry = pDirEntry;
1558
1559 if (*pcFiles == 0)
1560 {
1561 AssertFailed();
1562 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1563 }
1564 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1565 }
1566
1567#ifdef RT_OS_WINDOWS
1568 pDirEntry->Info.Attr.fMode |= 0111;
1569#endif
1570 vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
1571 pSFDEntry->cucShortName = 0;
1572
1573 if (fUtf8)
1574 {
1575 void *src, *dst;
1576
1577 src = &pDirEntry->szName[0];
1578 dst = &pSFDEntry->name.String.utf8[0];
1579
1580 memcpy (dst, src, pDirEntry->cbName + 1);
1581
1582 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1583 pSFDEntry->name.u16Length = pDirEntry->cbName;
1584 }
1585 else
1586 {
1587 pSFDEntry->name.String.ucs2[0] = 0;
1588 pwszString = pSFDEntry->name.String.ucs2;
1589 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1590 AssertRC(rc2);
1591
1592#ifdef RT_OS_DARWIN
1593/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1594 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
1595 * system level in darwin, or just by the user mode application libs. */
1596 {
1597 // Convert to
1598 // Normalization Form C (composed Unicode). We need this because
1599 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1600 // while most other OS', server-side programs usually expect NFC.
1601 uint16_t ucs2Length;
1602 CFRange rangeCharacters;
1603 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1604
1605 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len (pwszString));
1606 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1607 ucs2Length = ::CFStringGetLength(inStr);
1608
1609 rangeCharacters.location = 0;
1610 rangeCharacters.length = ucs2Length;
1611 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1612 pwszString[ucs2Length] = 0x0000; // NULL terminated
1613
1614 CFRelease(inStr);
1615 }
1616#endif
1617 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len (pSFDEntry->name.String.ucs2) * 2;
1618 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1619
1620 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1621 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1622
1623 // adjust cbNeeded (it was overestimated before)
1624 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1625 }
1626
1627 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1628 *pcbBuffer += cbNeeded;
1629 cbBufferOrg-= cbNeeded;
1630
1631 *pcFiles += 1;
1632
1633 /* Free the saved last entry, that we've just returned */
1634 if (pHandle->dir.pLastValidEntry)
1635 {
1636 RTMemFree(pHandle->dir.pLastValidEntry);
1637 pHandle->dir.pLastValidEntry = NULL;
1638 }
1639
1640 if (flags & SHFL_LIST_RETURN_ONE)
1641 break; /* we're done */
1642 }
1643 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1644
1645end:
1646 if (pDirEntry)
1647 RTMemFree(pDirEntry);
1648
1649 return rc;
1650}
1651
1652int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
1653{
1654 int rc = VINF_SUCCESS;
1655
1656 if (pPath == 0 || pBuffer == 0)
1657 {
1658 AssertFailed();
1659 return VERR_INVALID_PARAMETER;
1660 }
1661
1662 /* Build a host full path for the given path, handle file name case issues
1663 * (if the guest expects case-insensitive paths but the host is
1664 * case-sensitive) and convert ucs2 to utf8 if necessary.
1665 */
1666 char *pszFullPath = NULL;
1667 uint32_t cbFullPathRoot = 0;
1668
1669 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1670
1671 if (RT_SUCCESS (rc))
1672 {
1673 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer);
1674
1675 /* free the path string */
1676 vbsfFreeFullPath(pszFullPath);
1677 }
1678
1679 return rc;
1680}
1681
1682int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1683{
1684 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1685 int rc = VINF_SUCCESS;
1686 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
1687 RTFSOBJINFO fileinfo;
1688
1689
1690 if (pHandle == 0 || pcbBuffer == 0 || pObjInfo == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1691 {
1692 AssertFailed();
1693 return VERR_INVALID_PARAMETER;
1694 }
1695
1696 /* @todo other options */
1697 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1698
1699 *pcbBuffer = 0;
1700
1701 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1702 {
1703 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1704 }
1705 else
1706 {
1707 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1708#ifdef RT_OS_WINDOWS
1709 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1710 pObjInfo->Attr.fMode |= 0111;
1711#endif
1712 }
1713 if (rc == VINF_SUCCESS)
1714 {
1715 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
1716 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1717 }
1718 else
1719 AssertFailed();
1720
1721 return rc;
1722}
1723
1724static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1725{
1726 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1727 int rc = VINF_SUCCESS;
1728 SHFLFSOBJINFO *pSFDEntry;
1729
1730 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1731 {
1732 AssertFailed();
1733 return VERR_INVALID_PARAMETER;
1734 }
1735
1736 *pcbBuffer = 0;
1737 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1738
1739 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1740
1741 /* Change only the time values that are not zero */
1742 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1743 {
1744 rc = RTDirSetTimes(pHandle->dir.Handle,
1745 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1746 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1747 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1748 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1749 );
1750 }
1751 else
1752 {
1753 rc = RTFileSetTimes(pHandle->file.Handle,
1754 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1755 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1756 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1757 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1758 );
1759 }
1760 if (rc != VINF_SUCCESS)
1761 {
1762 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1763 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1764 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1765 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1766 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
1767 /* temporary hack */
1768 rc = VINF_SUCCESS;
1769 }
1770
1771 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_FILE)
1772 {
1773 /* Change file attributes if necessary */
1774 if (pSFDEntry->Attr.fMode)
1775 {
1776 RTFMODE fMode = pSFDEntry->Attr.fMode;
1777
1778#ifndef RT_OS_WINDOWS
1779 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
1780 * able to access this file anymore. Only for guests, which set the UNIX mode. */
1781 if (fMode & RTFS_UNIX_MASK)
1782 fMode |= RTFS_UNIX_IRUSR;
1783#endif
1784
1785 rc = RTFileSetMode((RTFILE)pHandle->file.Handle, fMode);
1786 if (rc != VINF_SUCCESS)
1787 {
1788 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
1789 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1790 rc = VINF_SUCCESS;
1791 }
1792 }
1793 }
1794 /* TODO: mode for directories */
1795
1796 if (rc == VINF_SUCCESS)
1797 {
1798 uint32_t bufsize = sizeof(*pSFDEntry);
1799
1800 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1801 if (rc == VINF_SUCCESS)
1802 {
1803 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1804 }
1805 else
1806 AssertFailed();
1807 }
1808
1809 return rc;
1810}
1811
1812
1813static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1814{
1815 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1816 int rc = VINF_SUCCESS;
1817 SHFLFSOBJINFO *pSFDEntry;
1818
1819 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1820 {
1821 AssertFailed();
1822 return VERR_INVALID_PARAMETER;
1823 }
1824
1825 *pcbBuffer = 0;
1826 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1827
1828 if (flags & SHFL_INFO_SIZE)
1829 {
1830 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1831 if (rc != VINF_SUCCESS)
1832 AssertFailed();
1833 }
1834 else
1835 AssertFailed();
1836
1837 if (rc == VINF_SUCCESS)
1838 {
1839 RTFSOBJINFO fileinfo;
1840
1841 /* Query the new object info and return it */
1842 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1843 if (rc == VINF_SUCCESS)
1844 {
1845#ifdef RT_OS_WINDOWS
1846 fileinfo.Attr.fMode |= 0111;
1847#endif
1848 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
1849 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1850 }
1851 else
1852 AssertFailed();
1853 }
1854
1855 return rc;
1856}
1857
1858int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1859{
1860 int rc = VINF_SUCCESS;
1861 SHFLVOLINFO *pSFDEntry;
1862 char *pszFullPath = NULL;
1863 SHFLSTRING dummy;
1864
1865 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1866 {
1867 AssertFailed();
1868 return VERR_INVALID_PARAMETER;
1869 }
1870
1871 /* @todo other options */
1872 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1873
1874 *pcbBuffer = 0;
1875 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1876
1877 ShflStringInitBuffer(&dummy, sizeof(dummy));
1878 rc = vbsfBuildFullPath (pClient, root, &dummy, 0, &pszFullPath, NULL);
1879
1880 if (RT_SUCCESS (rc))
1881 {
1882 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1883 if (rc != VINF_SUCCESS)
1884 goto exit;
1885
1886 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1887 if (rc != VINF_SUCCESS)
1888 goto exit;
1889
1890 RTFSPROPERTIES FsProperties;
1891 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
1892 if (rc != VINF_SUCCESS)
1893 goto exit;
1894 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
1895
1896 *pcbBuffer = sizeof(SHFLVOLINFO);
1897 }
1898 else AssertFailed();
1899
1900exit:
1901 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
1902 /* free the path string */
1903 vbsfFreeFullPath(pszFullPath);
1904 return rc;
1905}
1906
1907int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1908{
1909 if (pcbBuffer == 0 || pBuffer == 0)
1910 {
1911 AssertFailed();
1912 return VERR_INVALID_PARAMETER;
1913 }
1914
1915 if (flags & SHFL_INFO_FILE)
1916 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1917
1918 if (flags & SHFL_INFO_VOLUME)
1919 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
1920
1921 AssertFailed();
1922 return VERR_INVALID_PARAMETER;
1923}
1924
1925int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1926{
1927 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
1928
1929 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1930 {
1931 AssertFailed();
1932 return VERR_INVALID_PARAMETER;
1933 }
1934
1935 /* is the guest allowed to write to this share? */
1936 bool fWritable;
1937 int rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1938 if (RT_FAILURE(rc) || !fWritable)
1939 return VERR_WRITE_PROTECT;
1940
1941 if (flags & SHFL_INFO_FILE)
1942 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1943
1944 if (flags & SHFL_INFO_SIZE)
1945 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1946
1947// if (flags & SHFL_INFO_VOLUME)
1948// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1949 AssertFailed();
1950 return VERR_INVALID_PARAMETER;
1951}
1952
1953int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1954{
1955 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1956 uint32_t fRTLock = 0;
1957 int rc;
1958
1959 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
1960
1961 if (pHandle == 0)
1962 {
1963 AssertFailed();
1964 return VERR_INVALID_HANDLE;
1965 }
1966 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
1967 || (flags & SHFL_LOCK_ENTIRE)
1968 )
1969 {
1970 AssertFailed();
1971 return VERR_INVALID_PARAMETER;
1972 }
1973
1974 /* Lock type */
1975 switch(flags & SHFL_LOCK_MODE_MASK)
1976 {
1977 case SHFL_LOCK_SHARED:
1978 fRTLock = RTFILE_LOCK_READ;
1979 break;
1980
1981 case SHFL_LOCK_EXCLUSIVE:
1982 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
1983 break;
1984
1985 default:
1986 AssertFailed();
1987 return VERR_INVALID_PARAMETER;
1988 }
1989
1990 /* Lock wait type */
1991 if (flags & SHFL_LOCK_WAIT)
1992 fRTLock |= RTFILE_LOCK_WAIT;
1993 else
1994 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
1995
1996#ifdef RT_OS_WINDOWS
1997 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
1998 if (rc != VINF_SUCCESS)
1999 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2000#else
2001 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
2002 rc = VINF_SUCCESS;
2003#endif
2004 return rc;
2005}
2006
2007int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2008{
2009 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
2010 int rc;
2011
2012 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
2013
2014 if (pHandle == 0)
2015 {
2016 return VERR_INVALID_HANDLE;
2017 }
2018 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
2019 || (flags & SHFL_LOCK_ENTIRE)
2020 )
2021 {
2022 return VERR_INVALID_PARAMETER;
2023 }
2024
2025#ifdef RT_OS_WINDOWS
2026 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
2027 if (rc != VINF_SUCCESS)
2028 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2029#else
2030 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
2031 rc = VINF_SUCCESS;
2032#endif
2033
2034 return rc;
2035}
2036
2037
2038int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
2039{
2040 int rc = VINF_SUCCESS;
2041
2042 /* Validate input */
2043 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK)
2044 || cbPath == 0
2045 || pPath == 0)
2046 {
2047 AssertFailed();
2048 return VERR_INVALID_PARAMETER;
2049 }
2050
2051 /* Build a host full path for the given path
2052 * and convert ucs2 to utf8 if necessary.
2053 */
2054 char *pszFullPath = NULL;
2055
2056 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, NULL);
2057 if (RT_SUCCESS (rc))
2058 {
2059 /* is the guest allowed to write to this share? */
2060 bool fWritable;
2061 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
2062 if (RT_FAILURE(rc) || !fWritable)
2063 rc = VERR_WRITE_PROTECT;
2064
2065 if (RT_SUCCESS (rc))
2066 {
2067 if (flags & SHFL_REMOVE_SYMLINK)
2068 rc = RTSymlinkDelete(pszFullPath);
2069 else if (flags & SHFL_REMOVE_FILE)
2070 rc = RTFileDelete(pszFullPath);
2071 else
2072 rc = RTDirRemove(pszFullPath);
2073 }
2074
2075#ifndef DEBUG_dmik
2076 // VERR_ACCESS_DENIED for example?
2077 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
2078#endif
2079 /* free the path string */
2080 vbsfFreeFullPath(pszFullPath);
2081 }
2082 return rc;
2083}
2084
2085
2086int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
2087{
2088 int rc = VINF_SUCCESS;
2089
2090 /* Validate input */
2091 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
2092 || pSrc == 0
2093 || pDest == 0)
2094 {
2095 AssertFailed();
2096 return VERR_INVALID_PARAMETER;
2097 }
2098
2099 /* Build a host full path for the given path
2100 * and convert ucs2 to utf8 if necessary.
2101 */
2102 char *pszFullPathSrc = NULL;
2103 char *pszFullPathDest = NULL;
2104
2105 rc = vbsfBuildFullPath (pClient, root, pSrc, pSrc->u16Size, &pszFullPathSrc, NULL);
2106 if (rc != VINF_SUCCESS)
2107 return rc;
2108
2109 rc = vbsfBuildFullPath (pClient, root, pDest, pDest->u16Size, &pszFullPathDest, NULL, false, true);
2110 if (RT_SUCCESS (rc))
2111 {
2112 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
2113
2114 /* is the guest allowed to write to this share? */
2115 bool fWritable;
2116 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
2117 if (RT_FAILURE(rc) || !fWritable)
2118 rc = VERR_WRITE_PROTECT;
2119
2120 if (RT_SUCCESS (rc))
2121 {
2122 if (flags & SHFL_RENAME_FILE)
2123 {
2124 rc = RTFileMove(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0);
2125 }
2126 else
2127 {
2128 /* NT ignores the REPLACE flag and simply return and already exists error. */
2129 rc = RTDirRename(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0);
2130 }
2131 }
2132
2133#ifndef DEBUG_dmik
2134 AssertRC(rc);
2135#endif
2136 /* free the path string */
2137 vbsfFreeFullPath(pszFullPathDest);
2138 }
2139 /* free the path string */
2140 vbsfFreeFullPath(pszFullPathSrc);
2141 return rc;
2142}
2143
2144int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
2145{
2146 int rc = VINF_SUCCESS;
2147
2148 char *pszFullNewPath = NULL;
2149 char *pszOldPath = NULL;
2150
2151 /* XXX: no support for UCS2 at the moment. */
2152 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
2153 return VERR_NOT_IMPLEMENTED;
2154
2155 rc = vbsfBuildFullPath (pClient, root, pNewPath, pNewPath->u16Size, &pszFullNewPath, NULL);
2156 if (rc != VINF_SUCCESS)
2157 return rc;
2158
2159 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8, RTSYMLINKTYPE_UNKNOWN);
2160 if (RT_SUCCESS (rc))
2161 {
2162 RTFSOBJINFO info;
2163 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
2164 if (RT_SUCCESS(rc))
2165 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2166 }
2167
2168 vbsfFreeFullPath(pszFullNewPath);
2169
2170 return rc;
2171}
2172
2173/*
2174 * Clean up our mess by freeing all handles that are still valid.
2175 *
2176 */
2177int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2178{
2179 for (int i=0;i<SHFLHANDLE_MAX;i++)
2180 {
2181 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(i, SHFL_HF_TYPE_MASK);
2182
2183 if (pHandle)
2184 {
2185 Log(("Open handle %08x\n", i));
2186 vbsfClose(pClient, SHFL_HANDLE_ROOT /* incorrect, but it's not important */, (SHFLHANDLE)i);
2187 }
2188 }
2189 return VINF_SUCCESS;
2190}
Note: See TracBrowser for help on using the repository browser.

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