VirtualBox

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

Last change on this file since 39541 was 39540, checked in by vboxsync, 13 years ago

HostServices/SharedFolders: starter unit test.

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