VirtualBox

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

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

HostServices/Shared Folders: fixed parameter parsing for symlinks, fixes 32/64-bit issue (thanks Brian Campbell!)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.2 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 if (end == src)
553 rc = VINF_SUCCESS; /* trailing delimiter */
554 else
555 rc = VERR_FILE_NOT_FOUND;
556
557 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
558 {
559 /* path component is invalid; try to correct the casing */
560 rc = vbsfCorrectCasing(pClient, pszFullPath, src);
561 if (RT_FAILURE(rc))
562 {
563 if (!fEndOfString)
564 *end = RTPATH_DELIMITER; /* restore the original full path */
565 break;
566 }
567 }
568
569 if (fEndOfString)
570 break;
571
572 *end = RTPATH_DELIMITER;
573 src = end + 1;
574 }
575 if (RT_FAILURE(rc))
576 Log(("Unable to find suitable component rc=%d\n", rc));
577 }
578 else
579 rc = VERR_FILE_NOT_FOUND;
580
581 }
582 if (pszLastComponent)
583 *pszLastComponent = RTPATH_DELIMITER;
584
585 /* might be a new file so don't fail here! */
586 rc = VINF_SUCCESS;
587 }
588 *ppszFullPath = pszFullPath;
589
590 LogFlow(("vbsfBuildFullPath: %s rc=%Rrc\n", pszFullPath, rc));
591 }
592
593 return rc;
594}
595
596static void vbsfFreeFullPath (char *pszFullPath)
597{
598 RTMemFree (pszFullPath);
599}
600
601/**
602 * Convert shared folder create flags (see include/iprt/shflsvc.h) into iprt create flags.
603 *
604 * @returns iprt status code
605 * @param fShflFlags shared folder create flags
606 * @param fMode file attributes
607 * @retval pfOpen iprt create flags
608 */
609static int vbsfConvertFileOpenFlags(unsigned fShflFlags, RTFMODE fMode, SHFLHANDLE handleInitial, uint32_t *pfOpen)
610{
611 uint32_t fOpen = 0;
612 int rc = VINF_SUCCESS;
613
614 if ( (fMode & RTFS_DOS_MASK) != 0
615 && (fMode & RTFS_UNIX_MASK) == 0)
616 {
617 /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*.
618 * @todo this is based on rtFsModeNormalize/rtFsModeFromDos.
619 * May be better to use RTFsModeNormalize here.
620 */
621 fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
622 /* x for directories. */
623 if (fMode & RTFS_DOS_DIRECTORY)
624 fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
625 /* writable? */
626 if (!(fMode & RTFS_DOS_READONLY))
627 fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
628
629 /* Set the requested mode using only allowed bits. */
630 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
631 }
632 else
633 {
634 /* Old linux and solaris additions did not initialize the Info.Attr.fMode field
635 * and it contained random bits from stack. Detect this using the handle field value
636 * passed from the guest: old additions set it (incorrectly) to 0, new additions
637 * set it to SHFL_HANDLE_NIL(~0).
638 */
639 if (handleInitial == 0)
640 {
641 /* Old additions. Do nothing, use default mode. */
642 }
643 else
644 {
645 /* New additions or Windows additions. Set the requested mode using only allowed bits.
646 * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode
647 * will be set in fOpen.
648 */
649 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
650 }
651 }
652
653 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_RW))
654 {
655 default:
656 case SHFL_CF_ACCESS_NONE:
657 {
658 /** @todo treat this as read access, but theoretically this could be a no access request. */
659 fOpen |= RTFILE_O_READ;
660 Log(("FLAG: SHFL_CF_ACCESS_NONE\n"));
661 break;
662 }
663
664 case SHFL_CF_ACCESS_READ:
665 {
666 fOpen |= RTFILE_O_READ;
667 Log(("FLAG: SHFL_CF_ACCESS_READ\n"));
668 break;
669 }
670
671 case SHFL_CF_ACCESS_WRITE:
672 {
673 fOpen |= RTFILE_O_WRITE;
674 Log(("FLAG: SHFL_CF_ACCESS_WRITE\n"));
675 break;
676 }
677
678 case SHFL_CF_ACCESS_READWRITE:
679 {
680 fOpen |= RTFILE_O_READWRITE;
681 Log(("FLAG: SHFL_CF_ACCESS_READWRITE\n"));
682 break;
683 }
684 }
685
686 if (fShflFlags & SHFL_CF_ACCESS_APPEND)
687 {
688 fOpen |= RTFILE_O_APPEND;
689 }
690
691 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR))
692 {
693 default:
694 case SHFL_CF_ACCESS_ATTR_NONE:
695 {
696 fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
697 Log(("FLAG: SHFL_CF_ACCESS_ATTR_NONE\n"));
698 break;
699 }
700
701 case SHFL_CF_ACCESS_ATTR_READ:
702 {
703 fOpen |= RTFILE_O_ACCESS_ATTR_READ;
704 Log(("FLAG: SHFL_CF_ACCESS_ATTR_READ\n"));
705 break;
706 }
707
708 case SHFL_CF_ACCESS_ATTR_WRITE:
709 {
710 fOpen |= RTFILE_O_ACCESS_ATTR_WRITE;
711 Log(("FLAG: SHFL_CF_ACCESS_ATTR_WRITE\n"));
712 break;
713 }
714
715 case SHFL_CF_ACCESS_ATTR_READWRITE:
716 {
717 fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE;
718 Log(("FLAG: SHFL_CF_ACCESS_ATTR_READWRITE\n"));
719 break;
720 }
721 }
722
723 /* Sharing mask */
724 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_DENY))
725 {
726 default:
727 case SHFL_CF_ACCESS_DENYNONE:
728 fOpen |= RTFILE_O_DENY_NONE;
729 Log(("FLAG: SHFL_CF_ACCESS_DENYNONE\n"));
730 break;
731
732 case SHFL_CF_ACCESS_DENYREAD:
733 fOpen |= RTFILE_O_DENY_READ;
734 Log(("FLAG: SHFL_CF_ACCESS_DENYREAD\n"));
735 break;
736
737 case SHFL_CF_ACCESS_DENYWRITE:
738 fOpen |= RTFILE_O_DENY_WRITE;
739 Log(("FLAG: SHFL_CF_ACCESS_DENYWRITE\n"));
740 break;
741
742 case SHFL_CF_ACCESS_DENYALL:
743 fOpen |= RTFILE_O_DENY_ALL;
744 Log(("FLAG: SHFL_CF_ACCESS_DENYALL\n"));
745 break;
746 }
747
748 /* Open/Create action mask */
749 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
750 {
751 case SHFL_CF_ACT_OPEN_IF_EXISTS:
752 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
753 {
754 fOpen |= RTFILE_O_OPEN_CREATE;
755 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
756 }
757 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
758 {
759 fOpen |= RTFILE_O_OPEN;
760 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
761 }
762 else
763 {
764 Log(("FLAGS: invalid open/create action combination\n"));
765 rc = VERR_INVALID_PARAMETER;
766 }
767 break;
768 case SHFL_CF_ACT_FAIL_IF_EXISTS:
769 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
770 {
771 fOpen |= RTFILE_O_CREATE;
772 Log(("FLAGS: SHFL_CF_ACT_FAIL_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
773 }
774 else
775 {
776 Log(("FLAGS: invalid open/create action combination\n"));
777 rc = VERR_INVALID_PARAMETER;
778 }
779 break;
780 case SHFL_CF_ACT_REPLACE_IF_EXISTS:
781 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
782 {
783 fOpen |= RTFILE_O_CREATE_REPLACE;
784 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
785 }
786 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
787 {
788 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
789 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
790 }
791 else
792 {
793 Log(("FLAGS: invalid open/create action combination\n"));
794 rc = VERR_INVALID_PARAMETER;
795 }
796 break;
797 case SHFL_CF_ACT_OVERWRITE_IF_EXISTS:
798 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
799 {
800 fOpen |= RTFILE_O_CREATE_REPLACE;
801 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
802 }
803 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
804 {
805 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
806 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
807 }
808 else
809 {
810 Log(("FLAGS: invalid open/create action combination\n"));
811 rc = VERR_INVALID_PARAMETER;
812 }
813 break;
814 default:
815 rc = VERR_INVALID_PARAMETER;
816 Log(("FLAG: SHFL_CF_ACT_MASK_IF_EXISTS - invalid parameter\n"));
817 }
818
819 if (RT_SUCCESS(rc))
820 {
821 *pfOpen = fOpen;
822 }
823 return rc;
824}
825
826/**
827 * Open a file or create and open a new one.
828 *
829 * @returns IPRT status code
830 * @param pClient Data structure describing the client accessing the shared folder
831 * @param pszPath Path to the file or folder on the host.
832 * @param pParms->CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
833 * @param pParms->Info When a new file is created this specifies the initial parameters.
834 * When a file is created or overwritten, it also specifies the
835 * initial size.
836 * @retval pParms->Result Shared folder status code, see include/VBox/shflsvc.h
837 * @retval pParms->Handle On success the (shared folder) handle of the file opened or
838 * created
839 * @retval pParms->Info On success the parameters of the file opened or created
840 */
841static int vbsfOpenFile (SHFLCLIENTDATA *pClient, const char *pszPath, SHFLCREATEPARMS *pParms)
842{
843 LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms));
844 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
845
846 SHFLHANDLE handle = SHFL_HANDLE_NIL;
847 SHFLFILEHANDLE *pHandle = 0;
848 /* Open or create a file. */
849 uint32_t fOpen = 0;
850 bool fNoError = false;
851 static int cErrors;
852
853 int rc = vbsfConvertFileOpenFlags(pParms->CreateFlags, pParms->Info.Attr.fMode, pParms->Handle, &fOpen);
854 if (RT_SUCCESS(rc))
855 {
856 handle = vbsfAllocFileHandle();
857 }
858 if (SHFL_HANDLE_NIL != handle)
859 {
860 rc = VERR_NO_MEMORY; /* If this fails - rewritten immediately on success. */
861 pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(handle, SHFL_HF_TYPE_FILE);
862 }
863 if (0 != pHandle)
864 {
865 rc = RTFileOpen(&pHandle->file.Handle, pszPath, fOpen);
866 }
867 if (RT_FAILURE (rc))
868 {
869 switch (rc)
870 {
871 case VERR_FILE_NOT_FOUND:
872 pParms->Result = SHFL_FILE_NOT_FOUND;
873
874 /* This actually isn't an error, so correct the rc before return later,
875 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
876 fNoError = true;
877 break;
878 case VERR_PATH_NOT_FOUND:
879 pParms->Result = SHFL_PATH_NOT_FOUND;
880
881 /* This actually isn't an error, so correct the rc before return later,
882 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
883 fNoError = true;
884 break;
885 case VERR_ALREADY_EXISTS:
886 RTFSOBJINFO info;
887
888 /** @todo Possible race left here. */
889 if (RT_SUCCESS(RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient))))
890 {
891#ifdef RT_OS_WINDOWS
892 info.Attr.fMode |= 0111;
893#endif
894 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
895 }
896 pParms->Result = SHFL_FILE_EXISTS;
897
898 /* This actually isn't an error, so correct the rc before return later,
899 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
900 fNoError = true;
901 break;
902 case VERR_TOO_MANY_OPEN_FILES:
903 if (cErrors < 32)
904 {
905 LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath));
906#if defined RT_OS_LINUX || RT_OS_SOLARIS
907 if (cErrors < 1)
908 LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimit -n)\n"));
909#endif
910 cErrors++;
911 }
912 pParms->Result = SHFL_NO_RESULT;
913 break;
914 default:
915 pParms->Result = SHFL_NO_RESULT;
916 }
917 }
918
919 if (RT_SUCCESS(rc))
920 {
921 /** @note The shared folder status code is very approximate, as the runtime
922 * does not really provide this information. */
923 pParms->Result = SHFL_FILE_EXISTS; /* We lost the information as to whether it was
924 created when we eliminated the race. */
925 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
926 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
927 || ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS
928 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
929 {
930 /* For now, we do not treat a failure here as fatal. */
931 /* @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if
932 SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
933 RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
934 pParms->Result = SHFL_FILE_REPLACED;
935 }
936 if ( ( SHFL_CF_ACT_FAIL_IF_EXISTS
937 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
938 || ( SHFL_CF_ACT_CREATE_IF_NEW
939 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
940 {
941 pParms->Result = SHFL_FILE_CREATED;
942 }
943#if 0
944 /* @todo */
945 /* Set new attributes. */
946 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
947 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
948 || ( SHFL_CF_ACT_CREATE_IF_NEW
949 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
950 {
951 RTFileSetTimes(pHandle->file.Handle,
952 &pParms->Info.AccessTime,
953 &pParms->Info.ModificationTime,
954 &pParms->Info.ChangeTime,
955 &pParms->Info.BirthTime
956 );
957
958 RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
959 }
960#endif
961 RTFSOBJINFO info;
962
963 /* Get file information */
964 rc = RTFileQueryInfo (pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
965 if (RT_SUCCESS(rc))
966 {
967#ifdef RT_OS_WINDOWS
968 info.Attr.fMode |= 0111;
969#endif
970 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
971 }
972 }
973 if (RT_FAILURE(rc))
974 {
975 if ( (0 != pHandle)
976 && (NIL_RTFILE != pHandle->file.Handle)
977 && (0 != pHandle->file.Handle))
978 {
979 RTFileClose(pHandle->file.Handle);
980 pHandle->file.Handle = NIL_RTFILE;
981 }
982 if (SHFL_HANDLE_NIL != handle)
983 {
984 vbsfFreeFileHandle (handle);
985 }
986 pParms->Handle = SHFL_HANDLE_NIL;
987 }
988 else
989 {
990 pParms->Handle = handle;
991 }
992
993 /* Report the driver that all is okay, we're done here */
994 if (fNoError)
995 rc = VINF_SUCCESS;
996
997 LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc));
998 return rc;
999}
1000
1001/**
1002 * Open a folder or create and open a new one.
1003 *
1004 * @returns IPRT status code
1005 * @param pszPath Path to the file or folder on the host.
1006 * @param pParms->CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
1007 * @retval pParms->Result Shared folder status code, see include/VBox/shflsvc.h
1008 * @retval pParms->Handle On success the (shared folder) handle of the folder opened or
1009 * created
1010 * @retval pParms->Info On success the parameters of the folder opened or created
1011 *
1012 * @note folders are created with fMode = 0777
1013 */
1014static int vbsfOpenDir (const char *pszPath, SHFLCREATEPARMS *pParms)
1015{
1016 LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms));
1017 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
1018
1019 int rc = VERR_NO_MEMORY;
1020 SHFLHANDLE handle = vbsfAllocDirHandle();
1021 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(handle, SHFL_HF_TYPE_DIR);
1022 if (0 != pHandle)
1023 {
1024 rc = VINF_SUCCESS;
1025 pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */
1026 /** @todo Can anyone think of a sensible, race-less way to do this? Although
1027 I suspect that the race is inherent, due to the API available... */
1028 /* Try to create the folder first if "create if new" is specified. If this
1029 fails, and "open if exists" is specified, then we ignore the failure and try
1030 to open the folder anyway. */
1031 if ( SHFL_CF_ACT_CREATE_IF_NEW
1032 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))
1033 {
1034 /** @todo render supplied attributes.
1035 * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */
1036 RTFMODE fMode = 0777;
1037
1038 pParms->Result = SHFL_FILE_CREATED;
1039 rc = RTDirCreate(pszPath, fMode);
1040 if (RT_FAILURE (rc))
1041 {
1042 switch (rc)
1043 {
1044 case VERR_ALREADY_EXISTS:
1045 pParms->Result = SHFL_FILE_EXISTS;
1046 break;
1047 case VERR_PATH_NOT_FOUND:
1048 pParms->Result = SHFL_PATH_NOT_FOUND;
1049 break;
1050 default:
1051 pParms->Result = SHFL_NO_RESULT;
1052 }
1053 }
1054 }
1055 if ( RT_SUCCESS(rc)
1056 || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
1057 {
1058 /* Open the directory now */
1059 rc = RTDirOpen (&pHandle->dir.Handle, pszPath);
1060 if (RT_SUCCESS(rc))
1061 {
1062 RTFSOBJINFO info;
1063
1064 rc = RTDirQueryInfo (pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING);
1065 if (RT_SUCCESS(rc))
1066 {
1067 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
1068 }
1069 }
1070 else
1071 {
1072 switch (rc)
1073 {
1074 case VERR_FILE_NOT_FOUND: /* Does this make sense? */
1075 pParms->Result = SHFL_FILE_NOT_FOUND;
1076 break;
1077 case VERR_PATH_NOT_FOUND:
1078 pParms->Result = SHFL_PATH_NOT_FOUND;
1079 break;
1080 case VERR_ACCESS_DENIED:
1081 pParms->Result = SHFL_FILE_EXISTS;
1082 break;
1083 default:
1084 pParms->Result = SHFL_NO_RESULT;
1085 }
1086 }
1087 }
1088 }
1089 if (RT_FAILURE(rc))
1090 {
1091 if ( (0 != pHandle)
1092 && (0 != pHandle->dir.Handle))
1093 {
1094 RTDirClose(pHandle->dir.Handle);
1095 pHandle->dir.Handle = 0;
1096 }
1097 if (SHFL_HANDLE_NIL != handle)
1098 {
1099 vbsfFreeFileHandle (handle);
1100 }
1101 pParms->Handle = SHFL_HANDLE_NIL;
1102 }
1103 else
1104 {
1105 pParms->Handle = handle;
1106 }
1107 LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc));
1108 return rc;
1109}
1110
1111static int vbsfCloseDir (SHFLFILEHANDLE *pHandle)
1112{
1113 int rc = VINF_SUCCESS;
1114
1115 LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n",
1116 pHandle->dir.Handle, pHandle->dir.SearchHandle));
1117
1118 RTDirClose (pHandle->dir.Handle);
1119
1120 if (pHandle->dir.SearchHandle)
1121 RTDirClose(pHandle->dir.SearchHandle);
1122
1123 if (pHandle->dir.pLastValidEntry)
1124 {
1125 RTMemFree(pHandle->dir.pLastValidEntry);
1126 pHandle->dir.pLastValidEntry = NULL;
1127 }
1128
1129 LogFlow(("vbsfCloseDir: rc = %d\n", rc));
1130
1131 return rc;
1132}
1133
1134
1135static int vbsfCloseFile (SHFLFILEHANDLE *pHandle)
1136{
1137 int rc = VINF_SUCCESS;
1138
1139 LogFlow(("vbsfCloseFile: Handle = %08X\n",
1140 pHandle->file.Handle));
1141
1142 rc = RTFileClose (pHandle->file.Handle);
1143
1144 LogFlow(("vbsfCloseFile: rc = %d\n", rc));
1145
1146 return rc;
1147}
1148
1149/**
1150 * Look up file or folder information by host path.
1151 *
1152 * @returns iprt status code (currently VINF_SUCCESS)
1153 * @param pszFullPath The path of the file to be looked up
1154 * @retval pParms->Result Status of the operation (success or error)
1155 * @retval pParms->Info On success, information returned about the file
1156 */
1157static int vbsfLookupFile(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms)
1158{
1159 RTFSOBJINFO info;
1160 int rc;
1161
1162 rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1163 LogFlow(("SHFL_CF_LOOKUP\n"));
1164 /* Client just wants to know if the object exists. */
1165 switch (rc)
1166 {
1167 case VINF_SUCCESS:
1168 {
1169#ifdef RT_OS_WINDOWS
1170 info.Attr.fMode |= 0111;
1171#endif
1172 vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info);
1173 pParms->Result = SHFL_FILE_EXISTS;
1174 break;
1175 }
1176
1177 case VERR_FILE_NOT_FOUND:
1178 {
1179 pParms->Result = SHFL_FILE_NOT_FOUND;
1180 rc = VINF_SUCCESS;
1181 break;
1182 }
1183
1184 case VERR_PATH_NOT_FOUND:
1185 {
1186 pParms->Result = SHFL_PATH_NOT_FOUND;
1187 rc = VINF_SUCCESS;
1188 break;
1189 }
1190 }
1191 pParms->Handle = SHFL_HANDLE_NIL;
1192 return rc;
1193}
1194
1195/**
1196 * Create or open a file or folder. Perform character set and case
1197 * conversion on the file name if necessary.
1198 *
1199 * @returns IPRT status code, but see note below
1200 * @param pClient Data structure describing the client accessing the shared
1201 * folder
1202 * @param root The index of the shared folder in the table of mappings.
1203 * The host path of the shared folder is found using this.
1204 * @param pPath The path of the file or folder relative to the host path
1205 * indexed by root.
1206 * @param cbPath Presumably the length of the path in pPath. Actually
1207 * ignored, as pPath contains a length parameter.
1208 * @param pParms->Info If a new file is created or an old one overwritten, set
1209 * these attributes
1210 * @retval pParms->Result Shared folder result code, see include/VBox/shflsvc.h
1211 * @retval pParms->Handle Shared folder handle to the newly opened file
1212 * @retval pParms->Info Attributes of the file or folder opened
1213 *
1214 * @note This function returns success if a "non-exceptional" error occurred,
1215 * such as "no such file". In this case, the caller should check the
1216 * pParms->Result return value and whether pParms->Handle is valid.
1217 */
1218int vbsfCreate (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
1219{
1220 int rc = VINF_SUCCESS;
1221
1222 LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
1223 pClient, pPath, cbPath, pParms, pParms->CreateFlags));
1224
1225 /* Check the client access rights to the root. */
1226 /** @todo */
1227
1228 /* Build a host full path for the given path, handle file name case issues (if the guest
1229 * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
1230 * necessary.
1231 */
1232 char *pszFullPath = NULL;
1233 uint32_t cbFullPathRoot = 0;
1234
1235 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1236
1237 if (RT_SUCCESS (rc))
1238 {
1239 /* Reset return value in case client forgot to do so.
1240 * pParms->Handle must not be reset here, as it is used
1241 * in vbsfOpenFile to detect old additions.
1242 */
1243 pParms->Result = SHFL_NO_RESULT;
1244
1245 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
1246 {
1247 rc = vbsfLookupFile(pClient, pszFullPath, pParms);
1248 }
1249 else
1250 {
1251 /* Query path information. */
1252 RTFSOBJINFO info;
1253
1254 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1255 LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc));
1256
1257 if (RT_SUCCESS(rc))
1258 {
1259 /* Mark it as a directory in case the caller didn't. */
1260 /**
1261 * @todo I left this in in order not to change the behaviour of the
1262 * function too much. Is it really needed, and should it really be
1263 * here?
1264 */
1265 if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
1266 {
1267 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
1268 }
1269
1270 /**
1271 * @todo This should be in the Windows Guest Additions, as no-one else
1272 * needs it.
1273 */
1274 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
1275 {
1276 vbsfStripLastComponent (pszFullPath, cbFullPathRoot);
1277 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
1278 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
1279 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
1280 pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
1281 pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
1282 }
1283 }
1284
1285 rc = VINF_SUCCESS;
1286
1287 /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation
1288 * will cause changes.
1289 *
1290 * Actual operations (write, set attr, etc), which can write to a shared folder, have
1291 * the check and will return VERR_WRITE_PROTECT if the folder is not writable.
1292 */
1293 if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS
1294 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS
1295 || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW
1296 )
1297 {
1298 /* is the guest allowed to write to this share? */
1299 bool fWritable;
1300 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1301 if (RT_FAILURE(rc) || !fWritable)
1302 rc = VERR_WRITE_PROTECT;
1303 }
1304
1305 if (RT_SUCCESS(rc))
1306 {
1307 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
1308 {
1309 rc = vbsfOpenDir (pszFullPath, pParms);
1310 }
1311 else
1312 {
1313 rc = vbsfOpenFile (pClient, pszFullPath, pParms);
1314 }
1315 }
1316 else
1317 {
1318 pParms->Handle = SHFL_HANDLE_NIL;
1319 }
1320 }
1321
1322 /* free the path string */
1323 vbsfFreeFullPath(pszFullPath);
1324 }
1325
1326 Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
1327
1328 return rc;
1329}
1330
1331int vbsfClose (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1332{
1333 int rc = VINF_SUCCESS;
1334
1335 LogFlow(("vbsfClose: pClient = %p, Handle = %RX64\n",
1336 pClient, Handle));
1337
1338 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1339 //Assert(pHandle);
1340 if (!pHandle)
1341 return VERR_INVALID_HANDLE;
1342
1343 switch (ShflHandleType (&pHandle->Header))
1344 {
1345 case SHFL_HF_TYPE_DIR:
1346 {
1347 rc = vbsfCloseDir (pHandle);
1348 break;
1349 }
1350 case SHFL_HF_TYPE_FILE:
1351 {
1352 rc = vbsfCloseFile (pHandle);
1353 break;
1354 }
1355 default:
1356 AssertFailed();
1357 break;
1358 }
1359 vbsfFreeFileHandle(Handle);
1360
1361 Log(("vbsfClose: rc = %Rrc\n", rc));
1362
1363 return rc;
1364}
1365
1366int vbsfRead (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1367{
1368 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1369 size_t count = 0;
1370 int rc;
1371
1372 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1373 {
1374 AssertFailed();
1375 return VERR_INVALID_PARAMETER;
1376 }
1377
1378 Log(("vbsfRead %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer));
1379
1380 if (*pcbBuffer == 0)
1381 return VINF_SUCCESS; /* @todo correct? */
1382
1383
1384 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1385 if (rc != VINF_SUCCESS)
1386 {
1387 AssertRC(rc);
1388 return rc;
1389 }
1390
1391 rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1392 *pcbBuffer = (uint32_t)count;
1393 Log(("RTFileRead returned %Rrc bytes read %x\n", rc, count));
1394 return rc;
1395}
1396
1397int vbsfWrite (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1398{
1399 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1400 size_t count = 0;
1401 int rc;
1402
1403 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1404 {
1405 AssertFailed();
1406 return VERR_INVALID_PARAMETER;
1407 }
1408
1409 Log(("vbsfWrite %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer));
1410
1411 /* Is the guest allowed to write to this share?
1412 * XXX Actually this check was still done in vbsfCreate() -- RTFILE_O_WRITE cannot be set if vbsfMappingsQueryWritable() failed. */
1413 bool fWritable;
1414 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1415 if (RT_FAILURE(rc) || !fWritable)
1416 return VERR_WRITE_PROTECT;
1417
1418 if (*pcbBuffer == 0)
1419 return VINF_SUCCESS; /** @todo correct? */
1420
1421 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1422 if (rc != VINF_SUCCESS)
1423 {
1424 AssertRC(rc);
1425 return rc;
1426 }
1427
1428 rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1429 *pcbBuffer = (uint32_t)count;
1430 Log(("RTFileWrite returned %Rrc bytes written %x\n", rc, count));
1431 return rc;
1432}
1433
1434
1435int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1436{
1437 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1438 int rc = VINF_SUCCESS;
1439
1440 if (pHandle == 0)
1441 {
1442 AssertFailed();
1443 return VERR_INVALID_HANDLE;
1444 }
1445
1446 Log(("vbsfFlush %RX64\n", Handle));
1447 rc = RTFileFlush(pHandle->file.Handle);
1448 AssertRC(rc);
1449 return rc;
1450}
1451
1452int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags,
1453 uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles)
1454{
1455 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR);
1456 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1457 uint32_t cbDirEntry, cbBufferOrg;
1458 int rc = VINF_SUCCESS;
1459 PSHFLDIRINFO pSFDEntry;
1460 PRTUTF16 pwszString;
1461 PRTDIR DirHandle;
1462 bool fUtf8;
1463
1464 fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1465
1466 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1467 {
1468 AssertFailed();
1469 return VERR_INVALID_PARAMETER;
1470 }
1471 Assert(pIndex && *pIndex == 0);
1472 DirHandle = pHandle->dir.Handle;
1473
1474 cbDirEntry = 4096;
1475 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1476 if (pDirEntry == 0)
1477 {
1478 AssertFailed();
1479 return VERR_NO_MEMORY;
1480 }
1481
1482 cbBufferOrg = *pcbBuffer;
1483 *pcbBuffer = 0;
1484 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1485
1486 *pIndex = 1; /* not yet complete */
1487 *pcFiles = 0;
1488
1489 if (pPath)
1490 {
1491 if (pHandle->dir.SearchHandle == 0)
1492 {
1493 /* Build a host full path for the given path
1494 * and convert ucs2 to utf8 if necessary.
1495 */
1496 char *pszFullPath = NULL;
1497
1498 Assert(pHandle->dir.pLastValidEntry == 0);
1499
1500 rc = vbsfBuildFullPath (pClient, root, pPath, pPath->u16Size, &pszFullPath, NULL, true);
1501
1502 if (RT_SUCCESS (rc))
1503 {
1504 rc = RTDirOpenFiltered (&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT);
1505
1506 /* free the path string */
1507 vbsfFreeFullPath(pszFullPath);
1508
1509 if (RT_FAILURE (rc))
1510 goto end;
1511 }
1512 else
1513 goto end;
1514 }
1515 Assert(pHandle->dir.SearchHandle);
1516 DirHandle = pHandle->dir.SearchHandle;
1517 }
1518
1519 while(cbBufferOrg)
1520 {
1521 size_t cbDirEntrySize = cbDirEntry;
1522 uint32_t cbNeeded;
1523
1524 /* Do we still have a valid last entry for the active search? If so, then return it here */
1525 if (pHandle->dir.pLastValidEntry)
1526 {
1527 pDirEntry = pHandle->dir.pLastValidEntry;
1528 }
1529 else
1530 {
1531 pDirEntry = pDirEntryOrg;
1532
1533 rc = RTDirReadEx(DirHandle, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
1534 if (rc == VERR_NO_MORE_FILES)
1535 {
1536 *pIndex = 0; /* listing completed */
1537 break;
1538 }
1539
1540 if ( rc != VINF_SUCCESS
1541 && rc != VWRN_NO_DIRENT_INFO)
1542 {
1543 //AssertFailed();
1544 if ( rc == VERR_NO_TRANSLATION
1545 || rc == VERR_INVALID_UTF8_ENCODING)
1546 continue;
1547 break;
1548 }
1549 }
1550
1551 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String);
1552 if (fUtf8)
1553 cbNeeded += pDirEntry->cbName + 1;
1554 else
1555 /* Overestimating, but that's ok */
1556 cbNeeded += (pDirEntry->cbName + 1) * 2;
1557
1558 if (cbBufferOrg < cbNeeded)
1559 {
1560 /* No room, so save this directory entry, or else it's lost forever */
1561 pHandle->dir.pLastValidEntry = pDirEntry;
1562
1563 if (*pcFiles == 0)
1564 {
1565 AssertFailed();
1566 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1567 }
1568 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1569 }
1570
1571#ifdef RT_OS_WINDOWS
1572 pDirEntry->Info.Attr.fMode |= 0111;
1573#endif
1574 vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info);
1575 pSFDEntry->cucShortName = 0;
1576
1577 if (fUtf8)
1578 {
1579 void *src, *dst;
1580
1581 src = &pDirEntry->szName[0];
1582 dst = &pSFDEntry->name.String.utf8[0];
1583
1584 memcpy (dst, src, pDirEntry->cbName + 1);
1585
1586 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1587 pSFDEntry->name.u16Length = pDirEntry->cbName;
1588 }
1589 else
1590 {
1591 pSFDEntry->name.String.ucs2[0] = 0;
1592 pwszString = pSFDEntry->name.String.ucs2;
1593 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1594 AssertRC(rc2);
1595
1596#ifdef RT_OS_DARWIN
1597/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1598 * The question is simply whether the NFD normalization is actually applied on a (virtual) file
1599 * system level in darwin, or just by the user mode application libs. */
1600 {
1601 // Convert to
1602 // Normalization Form C (composed Unicode). We need this because
1603 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1604 // while most other OS', server-side programs usually expect NFC.
1605 uint16_t ucs2Length;
1606 CFRange rangeCharacters;
1607 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1608
1609 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len (pwszString));
1610 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1611 ucs2Length = ::CFStringGetLength(inStr);
1612
1613 rangeCharacters.location = 0;
1614 rangeCharacters.length = ucs2Length;
1615 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1616 pwszString[ucs2Length] = 0x0000; // NULL terminated
1617
1618 CFRelease(inStr);
1619 }
1620#endif
1621 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len (pSFDEntry->name.String.ucs2) * 2;
1622 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1623
1624 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1625 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1626
1627 // adjust cbNeeded (it was overestimated before)
1628 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1629 }
1630
1631 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1632 *pcbBuffer += cbNeeded;
1633 cbBufferOrg-= cbNeeded;
1634
1635 *pcFiles += 1;
1636
1637 /* Free the saved last entry, that we've just returned */
1638 if (pHandle->dir.pLastValidEntry)
1639 {
1640 RTMemFree(pHandle->dir.pLastValidEntry);
1641 pHandle->dir.pLastValidEntry = NULL;
1642 }
1643
1644 if (flags & SHFL_LIST_RETURN_ONE)
1645 break; /* we're done */
1646 }
1647 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1648
1649end:
1650 if (pDirEntry)
1651 RTMemFree(pDirEntry);
1652
1653 return rc;
1654}
1655
1656int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer)
1657{
1658 int rc = VINF_SUCCESS;
1659
1660 if (pPath == 0 || pBuffer == 0)
1661 {
1662 AssertFailed();
1663 return VERR_INVALID_PARAMETER;
1664 }
1665
1666 /* Build a host full path for the given path, handle file name case issues
1667 * (if the guest expects case-insensitive paths but the host is
1668 * case-sensitive) and convert ucs2 to utf8 if necessary.
1669 */
1670 char *pszFullPath = NULL;
1671 uint32_t cbFullPathRoot = 0;
1672
1673 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1674
1675 if (RT_SUCCESS (rc))
1676 {
1677 rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer);
1678
1679 /* free the path string */
1680 vbsfFreeFullPath(pszFullPath);
1681 }
1682
1683 return rc;
1684}
1685
1686int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1687{
1688 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1689 int rc = VINF_SUCCESS;
1690 SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer;
1691 RTFSOBJINFO fileinfo;
1692
1693
1694 if (pHandle == 0 || pcbBuffer == 0 || pObjInfo == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1695 {
1696 AssertFailed();
1697 return VERR_INVALID_PARAMETER;
1698 }
1699
1700 /* @todo other options */
1701 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1702
1703 *pcbBuffer = 0;
1704
1705 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1706 {
1707 rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1708 }
1709 else
1710 {
1711 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1712#ifdef RT_OS_WINDOWS
1713 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1714 pObjInfo->Attr.fMode |= 0111;
1715#endif
1716 }
1717 if (rc == VINF_SUCCESS)
1718 {
1719 vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo);
1720 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1721 }
1722 else
1723 AssertFailed();
1724
1725 return rc;
1726}
1727
1728static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1729{
1730 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1731 int rc = VINF_SUCCESS;
1732 SHFLFSOBJINFO *pSFDEntry;
1733
1734 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1735 {
1736 AssertFailed();
1737 return VERR_INVALID_PARAMETER;
1738 }
1739
1740 *pcbBuffer = 0;
1741 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1742
1743 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1744
1745 /* Change only the time values that are not zero */
1746 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1747 {
1748 rc = RTDirSetTimes(pHandle->dir.Handle,
1749 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1750 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1751 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1752 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1753 );
1754 }
1755 else
1756 {
1757 rc = RTFileSetTimes(pHandle->file.Handle,
1758 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1759 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1760 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1761 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1762 );
1763 }
1764 if (rc != VINF_SUCCESS)
1765 {
1766 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1767 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1768 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1769 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1770 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime)));
1771 /* temporary hack */
1772 rc = VINF_SUCCESS;
1773 }
1774
1775 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_FILE)
1776 {
1777 /* Change file attributes if necessary */
1778 if (pSFDEntry->Attr.fMode)
1779 {
1780 RTFMODE fMode = pSFDEntry->Attr.fMode;
1781
1782#ifndef RT_OS_WINDOWS
1783 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
1784 * able to access this file anymore. Only for guests, which set the UNIX mode. */
1785 if (fMode & RTFS_UNIX_MASK)
1786 fMode |= RTFS_UNIX_IRUSR;
1787#endif
1788
1789 rc = RTFileSetMode((RTFILE)pHandle->file.Handle, fMode);
1790 if (rc != VINF_SUCCESS)
1791 {
1792 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
1793 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1794 rc = VINF_SUCCESS;
1795 }
1796 }
1797 }
1798 /* TODO: mode for directories */
1799
1800 if (rc == VINF_SUCCESS)
1801 {
1802 uint32_t bufsize = sizeof(*pSFDEntry);
1803
1804 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1805 if (rc == VINF_SUCCESS)
1806 {
1807 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1808 }
1809 else
1810 AssertFailed();
1811 }
1812
1813 return rc;
1814}
1815
1816
1817static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1818{
1819 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1820 int rc = VINF_SUCCESS;
1821 SHFLFSOBJINFO *pSFDEntry;
1822
1823 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO))
1824 {
1825 AssertFailed();
1826 return VERR_INVALID_PARAMETER;
1827 }
1828
1829 *pcbBuffer = 0;
1830 pSFDEntry = (SHFLFSOBJINFO *)pBuffer;
1831
1832 if (flags & SHFL_INFO_SIZE)
1833 {
1834 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1835 if (rc != VINF_SUCCESS)
1836 AssertFailed();
1837 }
1838 else
1839 AssertFailed();
1840
1841 if (rc == VINF_SUCCESS)
1842 {
1843 RTFSOBJINFO fileinfo;
1844
1845 /* Query the new object info and return it */
1846 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1847 if (rc == VINF_SUCCESS)
1848 {
1849#ifdef RT_OS_WINDOWS
1850 fileinfo.Attr.fMode |= 0111;
1851#endif
1852 vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo);
1853 *pcbBuffer = sizeof(SHFLFSOBJINFO);
1854 }
1855 else
1856 AssertFailed();
1857 }
1858
1859 return rc;
1860}
1861
1862int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1863{
1864 int rc = VINF_SUCCESS;
1865 SHFLVOLINFO *pSFDEntry;
1866 char *pszFullPath = NULL;
1867 SHFLSTRING dummy;
1868
1869 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1870 {
1871 AssertFailed();
1872 return VERR_INVALID_PARAMETER;
1873 }
1874
1875 /* @todo other options */
1876 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1877
1878 *pcbBuffer = 0;
1879 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1880
1881 ShflStringInitBuffer(&dummy, sizeof(dummy));
1882 rc = vbsfBuildFullPath (pClient, root, &dummy, 0, &pszFullPath, NULL);
1883
1884 if (RT_SUCCESS (rc))
1885 {
1886 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1887 if (rc != VINF_SUCCESS)
1888 goto exit;
1889
1890 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1891 if (rc != VINF_SUCCESS)
1892 goto exit;
1893
1894 RTFSPROPERTIES FsProperties;
1895 rc = RTFsQueryProperties(pszFullPath, &FsProperties);
1896 if (rc != VINF_SUCCESS)
1897 goto exit;
1898 vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties);
1899
1900 *pcbBuffer = sizeof(SHFLVOLINFO);
1901 }
1902 else AssertFailed();
1903
1904exit:
1905 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
1906 /* free the path string */
1907 vbsfFreeFullPath(pszFullPath);
1908 return rc;
1909}
1910
1911int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1912{
1913 if (pcbBuffer == 0 || pBuffer == 0)
1914 {
1915 AssertFailed();
1916 return VERR_INVALID_PARAMETER;
1917 }
1918
1919 if (flags & SHFL_INFO_FILE)
1920 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1921
1922 if (flags & SHFL_INFO_VOLUME)
1923 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
1924
1925 AssertFailed();
1926 return VERR_INVALID_PARAMETER;
1927}
1928
1929int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1930{
1931 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
1932
1933 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1934 {
1935 AssertFailed();
1936 return VERR_INVALID_PARAMETER;
1937 }
1938
1939 /* is the guest allowed to write to this share? */
1940 bool fWritable;
1941 int rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1942 if (RT_FAILURE(rc) || !fWritable)
1943 return VERR_WRITE_PROTECT;
1944
1945 if (flags & SHFL_INFO_FILE)
1946 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1947
1948 if (flags & SHFL_INFO_SIZE)
1949 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1950
1951// if (flags & SHFL_INFO_VOLUME)
1952// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1953 AssertFailed();
1954 return VERR_INVALID_PARAMETER;
1955}
1956
1957int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1958{
1959 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1960 uint32_t fRTLock = 0;
1961 int rc;
1962
1963 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
1964
1965 if (pHandle == 0)
1966 {
1967 AssertFailed();
1968 return VERR_INVALID_HANDLE;
1969 }
1970 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
1971 || (flags & SHFL_LOCK_ENTIRE)
1972 )
1973 {
1974 AssertFailed();
1975 return VERR_INVALID_PARAMETER;
1976 }
1977
1978 /* Lock type */
1979 switch(flags & SHFL_LOCK_MODE_MASK)
1980 {
1981 case SHFL_LOCK_SHARED:
1982 fRTLock = RTFILE_LOCK_READ;
1983 break;
1984
1985 case SHFL_LOCK_EXCLUSIVE:
1986 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
1987 break;
1988
1989 default:
1990 AssertFailed();
1991 return VERR_INVALID_PARAMETER;
1992 }
1993
1994 /* Lock wait type */
1995 if (flags & SHFL_LOCK_WAIT)
1996 fRTLock |= RTFILE_LOCK_WAIT;
1997 else
1998 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
1999
2000#ifdef RT_OS_WINDOWS
2001 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
2002 if (rc != VINF_SUCCESS)
2003 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2004#else
2005 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
2006 rc = VINF_SUCCESS;
2007#endif
2008 return rc;
2009}
2010
2011int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
2012{
2013 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
2014 int rc;
2015
2016 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
2017
2018 if (pHandle == 0)
2019 {
2020 return VERR_INVALID_HANDLE;
2021 }
2022 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
2023 || (flags & SHFL_LOCK_ENTIRE)
2024 )
2025 {
2026 return VERR_INVALID_PARAMETER;
2027 }
2028
2029#ifdef RT_OS_WINDOWS
2030 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
2031 if (rc != VINF_SUCCESS)
2032 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
2033#else
2034 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
2035 rc = VINF_SUCCESS;
2036#endif
2037
2038 return rc;
2039}
2040
2041
2042int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
2043{
2044 int rc = VINF_SUCCESS;
2045
2046 /* Validate input */
2047 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK)
2048 || cbPath == 0
2049 || pPath == 0)
2050 {
2051 AssertFailed();
2052 return VERR_INVALID_PARAMETER;
2053 }
2054
2055 /* Build a host full path for the given path
2056 * and convert ucs2 to utf8 if necessary.
2057 */
2058 char *pszFullPath = NULL;
2059
2060 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, NULL);
2061 if (RT_SUCCESS (rc))
2062 {
2063 /* is the guest allowed to write to this share? */
2064 bool fWritable;
2065 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
2066 if (RT_FAILURE(rc) || !fWritable)
2067 rc = VERR_WRITE_PROTECT;
2068
2069 if (RT_SUCCESS (rc))
2070 {
2071 if (flags & SHFL_REMOVE_SYMLINK)
2072 rc = RTSymlinkDelete(pszFullPath);
2073 else if (flags & SHFL_REMOVE_FILE)
2074 rc = RTFileDelete(pszFullPath);
2075 else
2076 rc = RTDirRemove(pszFullPath);
2077 }
2078
2079#ifndef DEBUG_dmik
2080 // VERR_ACCESS_DENIED for example?
2081 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
2082#endif
2083 /* free the path string */
2084 vbsfFreeFullPath(pszFullPath);
2085 }
2086 return rc;
2087}
2088
2089
2090int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
2091{
2092 int rc = VINF_SUCCESS;
2093
2094 /* Validate input */
2095 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
2096 || pSrc == 0
2097 || pDest == 0)
2098 {
2099 AssertFailed();
2100 return VERR_INVALID_PARAMETER;
2101 }
2102
2103 /* Build a host full path for the given path
2104 * and convert ucs2 to utf8 if necessary.
2105 */
2106 char *pszFullPathSrc = NULL;
2107 char *pszFullPathDest = NULL;
2108
2109 rc = vbsfBuildFullPath (pClient, root, pSrc, pSrc->u16Size, &pszFullPathSrc, NULL);
2110 if (rc != VINF_SUCCESS)
2111 return rc;
2112
2113 rc = vbsfBuildFullPath (pClient, root, pDest, pDest->u16Size, &pszFullPathDest, NULL, false, true);
2114 if (RT_SUCCESS (rc))
2115 {
2116 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
2117
2118 /* is the guest allowed to write to this share? */
2119 bool fWritable;
2120 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
2121 if (RT_FAILURE(rc) || !fWritable)
2122 rc = VERR_WRITE_PROTECT;
2123
2124 if (RT_SUCCESS (rc))
2125 {
2126 if (flags & SHFL_RENAME_FILE)
2127 {
2128 rc = RTFileMove(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0);
2129 }
2130 else
2131 {
2132 /* NT ignores the REPLACE flag and simply return and already exists error. */
2133 rc = RTDirRename(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0);
2134 }
2135 }
2136
2137#ifndef DEBUG_dmik
2138 AssertRC(rc);
2139#endif
2140 /* free the path string */
2141 vbsfFreeFullPath(pszFullPathDest);
2142 }
2143 /* free the path string */
2144 vbsfFreeFullPath(pszFullPathSrc);
2145 return rc;
2146}
2147
2148int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo)
2149{
2150 int rc = VINF_SUCCESS;
2151
2152 char *pszFullNewPath = NULL;
2153 char *pszOldPath = NULL;
2154
2155 /* XXX: no support for UCS2 at the moment. */
2156 if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
2157 return VERR_NOT_IMPLEMENTED;
2158
2159 rc = vbsfBuildFullPath (pClient, root, pNewPath, pNewPath->u16Size, &pszFullNewPath, NULL);
2160 if (rc != VINF_SUCCESS)
2161 return rc;
2162
2163 rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8, RTSYMLINKTYPE_UNKNOWN);
2164 if (RT_SUCCESS (rc))
2165 {
2166 RTFSOBJINFO info;
2167 rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient));
2168 if (RT_SUCCESS(rc))
2169 vbfsCopyFsObjInfoFromIprt(pInfo, &info);
2170 }
2171
2172 vbsfFreeFullPath(pszFullNewPath);
2173
2174 return rc;
2175}
2176
2177/*
2178 * Clean up our mess by freeing all handles that are still valid.
2179 *
2180 */
2181int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2182{
2183 for (int i=0;i<SHFLHANDLE_MAX;i++)
2184 {
2185 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(i, SHFL_HF_TYPE_MASK);
2186
2187 if (pHandle)
2188 {
2189 Log(("Open handle %08x\n", i));
2190 vbsfClose(pClient, SHFL_HANDLE_ROOT /* incorrect, but it's not important */, (SHFLHANDLE)i);
2191 }
2192 }
2193 return VINF_SUCCESS;
2194}
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