VirtualBox

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

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

sf: parameter validation.

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