VirtualBox

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

Last change on this file since 24696 was 24374, checked in by vboxsync, 15 years ago

Shared folders host service: preserve case of a 'rename' target (xTracker 3319).

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