VirtualBox

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

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

shared folder: one more missed check

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

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