VirtualBox

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

Last change on this file since 51524 was 51250, checked in by vboxsync, 11 years ago

shared folders: several fixes, cleanup

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