VirtualBox

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

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

remainder: tabs -> spaces.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.8 KB
Line 
1/** @file
2 *
3 * Shared Folders:
4 * VBox Shared Folders.
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include "mappings.h"
24#include "vbsf.h"
25#include "shflhandle.h"
26
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/fs.h>
30#include <iprt/dir.h>
31#include <iprt/file.h>
32#include <iprt/path.h>
33#include <iprt/string.h>
34#include <iprt/uni.h>
35#include <iprt/stream.h>
36#ifdef RT_OS_DARWIN
37#include <Carbon/Carbon.h>
38#endif
39
40#undef LogFlow
41#define LogFlow Log
42
43/**
44 * @todo find a better solution for supporting the execute bit for non-windows
45 * guests on windows host. Search for "0111" to find all the relevant places.
46 */
47
48void vbsfStripLastComponent (char *pszFullPath, uint32_t cbFullPathRoot)
49{
50 RTUNICP cp;
51
52 /* Do not strip root. */
53 char *s = pszFullPath + cbFullPathRoot;
54 char *delimSecondLast = NULL;
55 char *delimLast = NULL;
56
57 LogFlowFunc(("%s -> %s\n", pszFullPath, s));
58
59 for (;;)
60 {
61 cp = RTStrGetCp(s);
62
63 if (cp == RTUNICP_INVALID || cp == 0)
64 {
65 break;
66 }
67
68 if (cp == RTPATH_DELIMITER)
69 {
70 if (delimLast != NULL)
71 {
72 delimSecondLast = delimLast;
73 }
74
75 delimLast = s;
76 }
77
78 s = RTStrNextCp (s);
79 }
80
81 if (cp == 0)
82 {
83 if (delimLast + 1 == s)
84 {
85 if (delimSecondLast)
86 {
87 *delimSecondLast = 0;
88 }
89 else if (delimLast)
90 {
91 *delimLast = 0;
92 }
93 }
94 else
95 {
96 if (delimLast)
97 {
98 *delimLast = 0;
99 }
100 }
101 }
102
103 LogFlowFunc(("%s, %s, %s\n", pszFullPath, delimLast, delimSecondLast));
104}
105
106static int vbsfCorrectCasing(char *pszFullPath, char *pszStartComponent)
107{
108 PRTDIRENTRYEX pDirEntry = NULL;
109 uint32_t cbDirEntry, cbComponent;
110 int rc = VERR_FILE_NOT_FOUND;
111 PRTDIR hSearch = 0;
112 char szWildCard[4];
113
114 Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent));
115
116 cbComponent = (uint32_t) strlen(pszStartComponent);
117
118 cbDirEntry = 4096;
119 pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
120 if (pDirEntry == 0)
121 {
122 AssertFailed();
123 return VERR_NO_MEMORY;
124 }
125
126 /** @todo this is quite inefficient, especially for directories with many files */
127 Assert(pszFullPath < pszStartComponent-1);
128 Assert(*(pszStartComponent-1) == RTPATH_DELIMITER);
129 *(pszStartComponent-1) = 0;
130 strcpy(pDirEntry->szName, pszFullPath);
131 szWildCard[0] = RTPATH_DELIMITER;
132 szWildCard[1] = '*';
133 szWildCard[2] = 0;
134 strcat(pDirEntry->szName, szWildCard);
135
136 rc = RTDirOpenFiltered (&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT);
137 *(pszStartComponent-1) = RTPATH_DELIMITER;
138 if (RT_FAILURE(rc))
139 goto end;
140
141 for(;;)
142 {
143 size_t cbDirEntrySize = cbDirEntry;
144
145 rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
146 if (rc == VERR_NO_MORE_FILES)
147 break;
148
149 if (VINF_SUCCESS != rc && rc != VWRN_NO_DIRENT_INFO)
150 {
151 AssertFailed();
152 if (rc != VERR_NO_TRANSLATION)
153 break;
154 else
155 continue;
156 }
157
158 Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0]));
159 if ( pDirEntry->cbName == cbComponent
160 && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0]))
161 {
162 Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent));
163 strcpy(pszStartComponent, &pDirEntry->szName[0]);
164 rc = VINF_SUCCESS;
165 break;
166 }
167 }
168
169end:
170 if (RT_FAILURE(rc))
171 Log(("vbsfCorrectCasing %s failed with %d\n", pszStartComponent, rc));
172
173 if (pDirEntry)
174 RTMemFree(pDirEntry);
175
176 if (hSearch)
177 RTDirClose(hSearch);
178 return rc;
179}
180
181static int vbsfPathCheck(const char *pUtf8Path, size_t cbPath)
182{
183 int rc = VINF_SUCCESS;
184
185 /* The pUtf8Path is what the guest sent. Verify that the path is within the root.
186 * Count '..' and other path components and check that we do not go over the root.
187 */
188
189 size_t i = 0;
190 int cComponents = 0; /* How many normal path components. */
191 int cParentDirs = 0; /* How many '..' components. */
192
193 for (;;)
194 {
195 /* Skip leading path delimiters. */
196 while ( i < cbPath
197 && (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/'))
198 i++;
199
200 if (i >= cbPath)
201 break;
202
203 /* Check if that is a dot component. */
204 int cDots = 0;
205 while (i < cbPath && pUtf8Path[i] == '.')
206 {
207 cDots++;
208 i++;
209 }
210
211 if ( cDots >= 2 /* Consider all multidots sequences as a 'parent dir'. */
212 && (i >= cbPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/')))
213 {
214 cParentDirs++;
215 }
216 else if ( cDots == 1
217 && (i >= cbPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/')))
218 {
219 /* Single dot, nothing changes. */
220 }
221 else
222 {
223 /* Skip this component. */
224 while ( i < cbPath
225 && (pUtf8Path[i] != '\\' && pUtf8Path[i] != '/'))
226 i++;
227
228 cComponents++;
229 }
230
231 Assert(i >= cbPath || (pUtf8Path[i] == '\\' || pUtf8Path[i] == '/'));
232
233 /* Verify counters for every component. */
234 if (cParentDirs > cComponents)
235 {
236 rc = VERR_INVALID_NAME;
237 break;
238 }
239 }
240
241 return rc;
242}
243
244static int vbsfBuildFullPath (SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING pPath,
245 uint32_t cbPath, char **ppszFullPath, uint32_t *pcbFullPathRoot,
246 bool fWildCard = false, bool fPreserveLastComponent = false)
247{
248 int rc = VINF_SUCCESS;
249
250 char *pszFullPath = NULL;
251
252 /* Query UCS2 root prefix for the path, cbRoot is the length in bytes including trailing (RTUTF16)0. */
253 uint32_t cbRoot = 0;
254 PCRTUTF16 pwszRoot = vbsfMappingsQueryHostRoot (root, &cbRoot);
255
256 if (!pwszRoot || cbRoot == 0)
257 {
258 Log(("vbsfBuildFullPath: invalid root!\n"));
259 return VERR_INVALID_PARAMETER;
260 }
261
262 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
263 {
264 char *utf8Root;
265
266 /* Verify that the path is under the root directory. */
267 rc = vbsfPathCheck((const char *)&pPath->String.utf8[0], pPath->u16Length);
268
269 if (RT_SUCCESS (rc))
270 {
271 rc = RTUtf16ToUtf8 (pwszRoot, &utf8Root);
272 }
273
274 if (RT_SUCCESS (rc))
275 {
276 size_t cbUtf8Root, cbUtf8FullPath;
277 char *utf8FullPath;
278
279 cbUtf8Root = strlen (utf8Root);
280 cbUtf8FullPath = cbUtf8Root + 1 + pPath->u16Length + 1;
281 utf8FullPath = (char *) RTMemAllocZ (cbUtf8FullPath);
282
283 if (!utf8FullPath)
284 {
285 rc = VERR_NO_MEMORY;
286 *ppszFullPath = NULL;
287 Log(("RTMemAllocZ %x failed!!\n", cbUtf8FullPath));
288 }
289 else
290 {
291 memcpy (utf8FullPath, utf8Root, cbUtf8Root);
292 memcpy (utf8FullPath + cbUtf8Root + 1,
293 &pPath->String.utf8[0],
294 pPath->u16Length);
295
296 utf8FullPath[cbUtf8Root] = '/';
297 utf8FullPath[cbUtf8FullPath - 1] = 0;
298 pszFullPath = utf8FullPath;
299
300 if (pcbFullPathRoot)
301 *pcbFullPathRoot = (uint32_t)cbUtf8Root; /* Must index the path delimiter. */
302 }
303
304 RTStrFree (utf8Root);
305 }
306 else
307 {
308 Log (("vbsfBuildFullPath: RTUtf16ToUtf8 failed with %Rrc\n", rc));
309 }
310 }
311 else
312 {
313#ifdef RT_OS_DARWIN
314/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
315 * The question is simply whether the NFD normalization is actually applied on a (virtaul) file
316 * system level in darwin, or just by the user mode application libs. */
317 SHFLSTRING *pPathParameter = pPath;
318 size_t cbPathLength;
319 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
320 uint16_t ucs2Length;
321 CFRange rangeCharacters;
322
323 // Is 8 times length enough for decomposed in worst case...?
324 cbPathLength = sizeof(SHFLSTRING) + pPathParameter->u16Length * 8 + 2;
325 pPath = (SHFLSTRING *)RTMemAllocZ (cbPathLength);
326 if (!pPath)
327 {
328 rc = VERR_NO_MEMORY;
329 Log(("RTMemAllocZ %x failed!!\n", cbPathLength));
330 return rc;
331 }
332
333 ::CFStringAppendCharacters(inStr, (UniChar*)pPathParameter->String.ucs2, pPathParameter->u16Length / sizeof(pPathParameter->String.ucs2[0]));
334 ::CFStringNormalize(inStr, kCFStringNormalizationFormD);
335 ucs2Length = ::CFStringGetLength(inStr);
336
337 rangeCharacters.location = 0;
338 rangeCharacters.length = ucs2Length;
339 ::CFStringGetCharacters(inStr, rangeCharacters, pPath->String.ucs2);
340 pPath->String.ucs2[ucs2Length] = 0x0000; // NULL terminated
341 pPath->u16Length = ucs2Length * sizeof(pPath->String.ucs2[0]);
342 pPath->u16Size = pPath->u16Length + sizeof(pPath->String.ucs2[0]);
343
344 CFRelease(inStr);
345#endif
346 /* Client sends us UCS2, so convert it to UTF8. */
347 Log(("Root %ls path %.*ls\n", pwszRoot, pPath->u16Length/sizeof(pPath->String.ucs2[0]), pPath->String.ucs2));
348
349 /* Allocate buffer that will be able to contain
350 * the root prefix and the pPath converted to UTF8.
351 * Expect a 2 bytes UCS2 to be converted to 8 bytes UTF8
352 * in worst case.
353 */
354 uint32_t cbFullPath = (cbRoot/sizeof (RTUTF16) + ShflStringLength (pPath)) * 4;
355
356 pszFullPath = (char *)RTMemAllocZ (cbFullPath);
357
358 if (!pszFullPath)
359 {
360 rc = VERR_NO_MEMORY;
361 }
362 else
363 {
364 uint32_t cb = cbFullPath;
365
366 rc = RTUtf16ToUtf8Ex (pwszRoot, RTSTR_MAX, &pszFullPath, cb, NULL);
367 if (RT_FAILURE(rc))
368 {
369 AssertFailed();
370#ifdef RT_OS_DARWIN
371 RTMemFree(pPath);
372 pPath = pPathParameter;
373#endif
374 return rc;
375 }
376
377 char *dst = pszFullPath;
378
379 cbRoot = (uint32_t)strlen(dst);
380 if (dst[cbRoot - 1] != RTPATH_DELIMITER)
381 {
382 dst[cbRoot] = RTPATH_DELIMITER;
383 cbRoot++;
384 }
385
386 if (pcbFullPathRoot)
387 *pcbFullPathRoot = cbRoot - 1; /* Must index the path delimiter. */
388
389 dst += cbRoot;
390 cb -= cbRoot;
391
392 if (pPath->u16Length)
393 {
394 /* Convert and copy components. */
395 PRTUTF16 src = &pPath->String.ucs2[0];
396
397 /* Correct path delimiters */
398 if (pClient->PathDelimiter != RTPATH_DELIMITER)
399 {
400 LogFlow(("Correct path delimiter in %ls\n", src));
401 while (*src)
402 {
403 if (*src == pClient->PathDelimiter)
404 *src = RTPATH_DELIMITER;
405 src++;
406 }
407 src = &pPath->String.ucs2[0];
408 LogFlow(("Corrected string %ls\n", src));
409 }
410 if (*src == RTPATH_DELIMITER)
411 src++; /* we already appended a delimiter to the first part */
412
413 rc = RTUtf16ToUtf8Ex (src, RTSTR_MAX, &dst, cb, NULL);
414 if (RT_FAILURE(rc))
415 {
416 AssertFailed();
417#ifdef RT_OS_DARWIN
418 RTMemFree(pPath);
419 pPath = pPathParameter;
420#endif
421 return rc;
422 }
423
424 uint32_t l = (uint32_t)strlen (dst);
425
426 /* Verify that the path is under the root directory. */
427 rc = vbsfPathCheck(dst, l);
428
429 if (RT_FAILURE(rc))
430 {
431#ifdef RT_OS_DARWIN
432 RTMemFree(pPath);
433 pPath = pPathParameter;
434#endif
435 return rc;
436 }
437
438 cb -= l;
439 dst += l;
440
441 Assert(cb > 0);
442 }
443
444 /* Nul terminate the string */
445 *dst = 0;
446 }
447#ifdef RT_OS_DARWIN
448 RTMemFree(pPath);
449 pPath = pPathParameter;
450#endif
451 }
452
453 if (RT_SUCCESS (rc))
454 {
455 /* When the host file system is case sensitive and the guest expects a case insensitive fs, then problems can occur */
456 if ( vbsfIsHostMappingCaseSensitive (root)
457 && !vbsfIsGuestMappingCaseSensitive(root))
458 {
459 RTFSOBJINFO info;
460 char *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, RTPATH_F_FOLLOW_LINK);
500 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
501 {
502 uint32_t len = (uint32_t)strlen(pszFullPath);
503 char *src = pszFullPath + len - 1;
504
505 Log(("Handle case insenstive guest fs on top of host case sensitive fs for %s\n", pszFullPath));
506
507 /* Find partial path that's valid */
508 while(src > pszFullPath)
509 {
510 if (*src == RTPATH_DELIMITER)
511 {
512 *src = 0;
513 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
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, RTPATH_F_FOLLOW_LINK);
550 Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND);
551 }
552 else
553 if (end == src)
554 rc = VINF_SUCCESS; /* trailing delimiter */
555 else
556 rc = VERR_FILE_NOT_FOUND;
557
558 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
559 {
560 /* path component is invalid; try to correct the casing */
561 rc = vbsfCorrectCasing(pszFullPath, src);
562 if (RT_FAILURE(rc))
563 {
564 if (!fEndOfString)
565 *end = RTPATH_DELIMITER; /* restore the original full path */
566 break;
567 }
568 }
569
570 if (fEndOfString)
571 break;
572
573 *end = RTPATH_DELIMITER;
574 src = end + 1;
575 }
576 if (RT_FAILURE(rc))
577 Log(("Unable to find suitable component rc=%d\n", rc));
578 }
579 else
580 rc = VERR_FILE_NOT_FOUND;
581
582 }
583 if (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 attibutes
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 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 (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, RTPATH_F_FOLLOW_LINK)))
890 {
891#ifdef RT_OS_WINDOWS
892 info.Attr.fMode |= 0111;
893#endif
894 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 (ulimt -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 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 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(char *pszPath, SHFLCREATEPARMS *pParms)
1158{
1159 RTFSOBJINFO info;
1160 int rc;
1161
1162 rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
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 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(pszFullPath, pParms);
1248 }
1249 else
1250 {
1251 /* Query path information. */
1252 RTFSOBJINFO info;
1253
1254 rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
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 (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, RTPATH_F_FOLLOW_LINK);
1534 if (rc == VERR_NO_MORE_FILES)
1535 {
1536 *pIndex = 0; /* listing completed */
1537 break;
1538 }
1539
1540 if (VINF_SUCCESS != rc && rc != VWRN_NO_DIRENT_INFO)
1541 {
1542 AssertFailed();
1543 if (rc != VERR_NO_TRANSLATION)
1544 break;
1545 else
1546 continue;
1547 }
1548 }
1549
1550 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String);
1551 if (fUtf8)
1552 cbNeeded += pDirEntry->cbName + 1;
1553 else
1554 /* Overestimating, but that's ok */
1555 cbNeeded += (pDirEntry->cbName + 1) * 2;
1556
1557 if (cbBufferOrg < cbNeeded)
1558 {
1559 /* No room, so save this directory entry, or else it's lost forever */
1560 pHandle->dir.pLastValidEntry = pDirEntry;
1561
1562 if (*pcFiles == 0)
1563 {
1564 AssertFailed();
1565 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1566 }
1567 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1568 }
1569
1570#ifdef RT_OS_WINDOWS
1571 pDirEntry->Info.Attr.fMode |= 0111;
1572#endif
1573 pSFDEntry->Info = pDirEntry->Info;
1574 pSFDEntry->cucShortName = 0;
1575
1576 if (fUtf8)
1577 {
1578 void *src, *dst;
1579
1580 src = &pDirEntry->szName[0];
1581 dst = &pSFDEntry->name.String.utf8[0];
1582
1583 memcpy (dst, src, pDirEntry->cbName + 1);
1584
1585 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1586 pSFDEntry->name.u16Length = pDirEntry->cbName;
1587 }
1588 else
1589 {
1590 pSFDEntry->name.String.ucs2[0] = 0;
1591 pwszString = pSFDEntry->name.String.ucs2;
1592 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1593 AssertRC(rc2);
1594
1595#ifdef RT_OS_DARWIN
1596/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1597 * The question is simply whether the NFD normalization is actually applied on a (virtaul) file
1598 * system level in darwin, or just by the user mode application libs. */
1599 {
1600 // Convert to
1601 // Normalization Form C (composed Unicode). We need this because
1602 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1603 // while most other OS', server-side programs usually expect NFC.
1604 uint16_t ucs2Length;
1605 CFRange rangeCharacters;
1606 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1607
1608 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len (pwszString));
1609 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1610 ucs2Length = ::CFStringGetLength(inStr);
1611
1612 rangeCharacters.location = 0;
1613 rangeCharacters.length = ucs2Length;
1614 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1615 pwszString[ucs2Length] = 0x0000; // NULL terminated
1616
1617 CFRelease(inStr);
1618 }
1619#endif
1620 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len (pSFDEntry->name.String.ucs2) * 2;
1621 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1622
1623 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1624 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1625
1626 // adjust cbNeeded (it was overestimated before)
1627 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1628 }
1629
1630 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1631 *pcbBuffer += cbNeeded;
1632 cbBufferOrg-= cbNeeded;
1633
1634 *pcFiles += 1;
1635
1636 /* Free the saved last entry, that we've just returned */
1637 if (pHandle->dir.pLastValidEntry)
1638 {
1639 RTMemFree(pHandle->dir.pLastValidEntry);
1640 pHandle->dir.pLastValidEntry = NULL;
1641 }
1642
1643 if (flags & SHFL_LIST_RETURN_ONE)
1644 break; /* we're done */
1645 }
1646 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1647
1648end:
1649 if (pDirEntry)
1650 RTMemFree(pDirEntry);
1651
1652 return rc;
1653}
1654
1655int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1656{
1657 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1658 int rc = VINF_SUCCESS;
1659 RTFSOBJINFO *pObjInfo = (RTFSOBJINFO *)pBuffer;
1660
1661
1662 if (pHandle == 0 || pcbBuffer == 0 || pObjInfo == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1663 {
1664 AssertFailed();
1665 return VERR_INVALID_PARAMETER;
1666 }
1667
1668 /* @todo other options */
1669 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1670
1671 *pcbBuffer = 0;
1672
1673 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1674 {
1675 rc = RTDirQueryInfo(pHandle->dir.Handle, pObjInfo, RTFSOBJATTRADD_NOTHING);
1676 }
1677 else
1678 {
1679 rc = RTFileQueryInfo(pHandle->file.Handle, pObjInfo, RTFSOBJATTRADD_NOTHING);
1680#ifdef RT_OS_WINDOWS
1681 if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
1682 pObjInfo->Attr.fMode |= 0111;
1683#endif
1684 }
1685 if (rc == VINF_SUCCESS)
1686 {
1687 *pcbBuffer = sizeof(RTFSOBJINFO);
1688 }
1689 else
1690 AssertFailed();
1691
1692 return rc;
1693}
1694
1695static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1696{
1697 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1698 int rc = VINF_SUCCESS;
1699 RTFSOBJINFO *pSFDEntry;
1700
1701 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1702 {
1703 AssertFailed();
1704 return VERR_INVALID_PARAMETER;
1705 }
1706
1707 *pcbBuffer = 0;
1708 pSFDEntry = (RTFSOBJINFO *)pBuffer;
1709
1710 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1711
1712 /* Change only the time values that are not zero */
1713 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1714 {
1715 rc = RTDirSetTimes(pHandle->dir.Handle,
1716 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1717 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1718 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1719 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1720 );
1721 }
1722 else
1723 {
1724 rc = RTFileSetTimes(pHandle->file.Handle,
1725 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1726 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1727 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1728 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1729 );
1730 }
1731 if (rc != VINF_SUCCESS)
1732 {
1733 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1734 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1735 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1736 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1737 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1738 /* temporary hack */
1739 rc = VINF_SUCCESS;
1740 }
1741
1742 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_FILE)
1743 {
1744 /* Change file attributes if necessary */
1745 if (pSFDEntry->Attr.fMode)
1746 {
1747 RTFMODE fMode = pSFDEntry->Attr.fMode;
1748
1749#ifndef RT_OS_WINDOWS
1750 /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be
1751 * able to access this file anymore. Only for guests, which set the UNIX mode. */
1752 if (fMode & RTFS_UNIX_MASK)
1753 fMode |= RTFS_UNIX_IRUSR;
1754#endif
1755
1756 rc = RTFileSetMode((RTFILE)pHandle->file.Handle, fMode);
1757 if (rc != VINF_SUCCESS)
1758 {
1759 Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc));
1760 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1761 rc = VINF_SUCCESS;
1762 }
1763 }
1764 }
1765 /* TODO: mode for directories */
1766
1767 if (rc == VINF_SUCCESS)
1768 {
1769 uint32_t bufsize = sizeof(*pSFDEntry);
1770
1771 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1772 if (rc == VINF_SUCCESS)
1773 {
1774 *pcbBuffer = sizeof(RTFSOBJINFO);
1775 }
1776 else
1777 AssertFailed();
1778 }
1779
1780 return rc;
1781}
1782
1783
1784static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1785{
1786 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1787 int rc = VINF_SUCCESS;
1788 RTFSOBJINFO *pSFDEntry;
1789
1790 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1791 {
1792 AssertFailed();
1793 return VERR_INVALID_PARAMETER;
1794 }
1795
1796 *pcbBuffer = 0;
1797 pSFDEntry = (RTFSOBJINFO *)pBuffer;
1798
1799 if (flags & SHFL_INFO_SIZE)
1800 {
1801 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1802 if (rc != VINF_SUCCESS)
1803 AssertFailed();
1804 }
1805 else
1806 AssertFailed();
1807
1808 if (rc == VINF_SUCCESS)
1809 {
1810 RTFSOBJINFO fileinfo;
1811
1812 /* Query the new object info and return it */
1813 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1814 if (rc == VINF_SUCCESS)
1815 {
1816#ifdef RT_OS_WINDOWS
1817 fileinfo.Attr.fMode |= 0111;
1818#endif
1819 *pSFDEntry = fileinfo;
1820 *pcbBuffer = sizeof(RTFSOBJINFO);
1821 }
1822 else
1823 AssertFailed();
1824 }
1825
1826 return rc;
1827}
1828
1829int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1830{
1831 int rc = VINF_SUCCESS;
1832 SHFLVOLINFO *pSFDEntry;
1833 char *pszFullPath = NULL;
1834 SHFLSTRING dummy;
1835
1836 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1837 {
1838 AssertFailed();
1839 return VERR_INVALID_PARAMETER;
1840 }
1841
1842 /* @todo other options */
1843 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1844
1845 *pcbBuffer = 0;
1846 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1847
1848 ShflStringInitBuffer(&dummy, sizeof(dummy));
1849 rc = vbsfBuildFullPath (pClient, root, &dummy, 0, &pszFullPath, NULL);
1850
1851 if (RT_SUCCESS (rc))
1852 {
1853 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1854 if (rc != VINF_SUCCESS)
1855 goto exit;
1856
1857 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1858 if (rc != VINF_SUCCESS)
1859 goto exit;
1860
1861 rc = RTFsQueryProperties(pszFullPath, &pSFDEntry->fsProperties);
1862 if (rc != VINF_SUCCESS)
1863 goto exit;
1864
1865 *pcbBuffer = sizeof(SHFLVOLINFO);
1866 }
1867 else AssertFailed();
1868
1869exit:
1870 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
1871 /* free the path string */
1872 vbsfFreeFullPath(pszFullPath);
1873 return rc;
1874}
1875
1876int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1877{
1878 if (pcbBuffer == 0 || pBuffer == 0)
1879 {
1880 AssertFailed();
1881 return VERR_INVALID_PARAMETER;
1882 }
1883
1884 if (flags & SHFL_INFO_FILE)
1885 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1886
1887 if (flags & SHFL_INFO_VOLUME)
1888 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
1889
1890 AssertFailed();
1891 return VERR_INVALID_PARAMETER;
1892}
1893
1894int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1895{
1896 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
1897
1898 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1899 {
1900 AssertFailed();
1901 return VERR_INVALID_PARAMETER;
1902 }
1903
1904 /* is the guest allowed to write to this share? */
1905 bool fWritable;
1906 int rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1907 if (RT_FAILURE(rc) || !fWritable)
1908 return VERR_WRITE_PROTECT;
1909
1910 if (flags & SHFL_INFO_FILE)
1911 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1912
1913 if (flags & SHFL_INFO_SIZE)
1914 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1915
1916// if (flags & SHFL_INFO_VOLUME)
1917// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1918 AssertFailed();
1919 return VERR_INVALID_PARAMETER;
1920}
1921
1922int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1923{
1924 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1925 uint32_t fRTLock = 0;
1926 int rc;
1927
1928 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
1929
1930 if (pHandle == 0)
1931 {
1932 AssertFailed();
1933 return VERR_INVALID_HANDLE;
1934 }
1935 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
1936 || (flags & SHFL_LOCK_ENTIRE)
1937 )
1938 {
1939 AssertFailed();
1940 return VERR_INVALID_PARAMETER;
1941 }
1942
1943 /* Lock type */
1944 switch(flags & SHFL_LOCK_MODE_MASK)
1945 {
1946 case SHFL_LOCK_SHARED:
1947 fRTLock = RTFILE_LOCK_READ;
1948 break;
1949
1950 case SHFL_LOCK_EXCLUSIVE:
1951 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
1952 break;
1953
1954 default:
1955 AssertFailed();
1956 return VERR_INVALID_PARAMETER;
1957 }
1958
1959 /* Lock wait type */
1960 if (flags & SHFL_LOCK_WAIT)
1961 fRTLock |= RTFILE_LOCK_WAIT;
1962 else
1963 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
1964
1965#ifdef RT_OS_WINDOWS
1966 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
1967 if (rc != VINF_SUCCESS)
1968 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1969#else
1970 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
1971 rc = VINF_SUCCESS;
1972#endif
1973 return rc;
1974}
1975
1976int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1977{
1978 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1979 int rc;
1980
1981 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
1982
1983 if (pHandle == 0)
1984 {
1985 return VERR_INVALID_HANDLE;
1986 }
1987 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
1988 || (flags & SHFL_LOCK_ENTIRE)
1989 )
1990 {
1991 return VERR_INVALID_PARAMETER;
1992 }
1993
1994#ifdef RT_OS_WINDOWS
1995 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
1996 if (rc != VINF_SUCCESS)
1997 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1998#else
1999 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
2000 rc = VINF_SUCCESS;
2001#endif
2002
2003 return rc;
2004}
2005
2006
2007int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
2008{
2009 int rc = VINF_SUCCESS;
2010
2011 /* Validate input */
2012 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR)
2013 || cbPath == 0
2014 || pPath == 0)
2015 {
2016 AssertFailed();
2017 return VERR_INVALID_PARAMETER;
2018 }
2019
2020 /* Build a host full path for the given path
2021 * and convert ucs2 to utf8 if necessary.
2022 */
2023 char *pszFullPath = NULL;
2024
2025 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, NULL);
2026 if (RT_SUCCESS (rc))
2027 {
2028 /* is the guest allowed to write to this share? */
2029 bool fWritable;
2030 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
2031 if (RT_FAILURE(rc) || !fWritable)
2032 rc = VERR_WRITE_PROTECT;
2033
2034 if (RT_SUCCESS (rc))
2035 {
2036 if (flags & SHFL_REMOVE_FILE)
2037 rc = RTFileDelete(pszFullPath);
2038 else
2039 rc = RTDirRemove(pszFullPath);
2040 }
2041
2042#ifndef DEBUG_dmik
2043 // VERR_ACCESS_DENIED for example?
2044 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
2045#endif
2046 /* free the path string */
2047 vbsfFreeFullPath(pszFullPath);
2048 }
2049 return rc;
2050}
2051
2052
2053int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
2054{
2055 int rc = VINF_SUCCESS;
2056
2057 /* Validate input */
2058 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
2059 || pSrc == 0
2060 || pDest == 0)
2061 {
2062 AssertFailed();
2063 return VERR_INVALID_PARAMETER;
2064 }
2065
2066 /* Build a host full path for the given path
2067 * and convert ucs2 to utf8 if necessary.
2068 */
2069 char *pszFullPathSrc = NULL;
2070 char *pszFullPathDest = NULL;
2071
2072 rc = vbsfBuildFullPath (pClient, root, pSrc, pSrc->u16Size, &pszFullPathSrc, NULL);
2073 if (rc != VINF_SUCCESS)
2074 return rc;
2075
2076 rc = vbsfBuildFullPath (pClient, root, pDest, pDest->u16Size, &pszFullPathDest, NULL, false, true);
2077 if (RT_SUCCESS (rc))
2078 {
2079 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
2080
2081 /* is the guest allowed to write to this share? */
2082 bool fWritable;
2083 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
2084 if (RT_FAILURE(rc) || !fWritable)
2085 rc = VERR_WRITE_PROTECT;
2086
2087 if (RT_SUCCESS (rc))
2088 {
2089 if (flags & SHFL_RENAME_FILE)
2090 {
2091 rc = RTFileMove(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0);
2092 }
2093 else
2094 {
2095 /* NT ignores the REPLACE flag and simply return and already exists error. */
2096 rc = RTDirRename(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0);
2097 }
2098 }
2099
2100#ifndef DEBUG_dmik
2101 AssertRC(rc);
2102#endif
2103 /* free the path string */
2104 vbsfFreeFullPath(pszFullPathDest);
2105 }
2106 /* free the path string */
2107 vbsfFreeFullPath(pszFullPathSrc);
2108 return rc;
2109}
2110
2111/*
2112 * Clean up our mess by freeing all handles that are still valid.
2113 *
2114 */
2115int vbsfDisconnect(SHFLCLIENTDATA *pClient)
2116{
2117 for (int i=0;i<SHFLHANDLE_MAX;i++)
2118 {
2119 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(i, SHFL_HF_TYPE_MASK);
2120
2121 if (pHandle)
2122 {
2123 Log(("Open handle %08x\n", i));
2124 vbsfClose(pClient, SHFL_HANDLE_ROOT /* incorrect, but it's not important */, (SHFLHANDLE)i);
2125 }
2126 }
2127 return VINF_SUCCESS;
2128}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use