VirtualBox

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

Last change on this file since 17118 was 16486, checked in by vboxsync, 16 years ago

shared folders: log if the limit for open files is exceeded

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.6 KB
Line 
1/** @file
2 *
3 * Shared Folders:
4 * VBox Shared Folders.
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include "mappings.h"
24#include "vbsf.h"
25#include "shflhandle.h"
26
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/fs.h>
30#include <iprt/dir.h>
31#include <iprt/file.h>
32#include <iprt/path.h>
33#include <iprt/string.h>
34#include <iprt/uni.h>
35#ifdef RT_OS_DARWIN
36#include <Carbon/Carbon.h>
37#endif
38
39#undef LogFlow
40#define LogFlow Log
41
42void vbsfStripLastComponent (char *pszFullPath, uint32_t cbFullPathRoot)
43{
44 RTUNICP cp;
45
46 /* Do not strip root. */
47 char *s = pszFullPath + cbFullPathRoot;
48 char *delimSecondLast = NULL;
49 char *delimLast = NULL;
50
51 LogFlowFunc(("%s -> %s\n", pszFullPath, s));
52
53 for (;;)
54 {
55 cp = RTStrGetCp(s);
56
57 if (cp == RTUNICP_INVALID || cp == 0)
58 {
59 break;
60 }
61
62 if (cp == RTPATH_DELIMITER)
63 {
64 if (delimLast != NULL)
65 {
66 delimSecondLast = delimLast;
67 }
68
69 delimLast = s;
70 }
71
72 s = RTStrNextCp (s);
73 }
74
75 if (cp == 0)
76 {
77 if (delimLast + 1 == s)
78 {
79 if (delimSecondLast)
80 {
81 *delimSecondLast = 0;
82 }
83 else if (delimLast)
84 {
85 *delimLast = 0;
86 }
87 }
88 else
89 {
90 if (delimLast)
91 {
92 *delimLast = 0;
93 }
94 }
95 }
96
97 LogFlowFunc(("%s, %s, %s\n", pszFullPath, delimLast, delimSecondLast));
98}
99
100static int vbsfCorrectCasing(char *pszFullPath, char *pszStartComponent)
101{
102 PRTDIRENTRYEX pDirEntry = NULL;
103 uint32_t cbDirEntry, cbComponent;
104 int rc = VERR_FILE_NOT_FOUND;
105 PRTDIR hSearch = 0;
106 char szWildCard[4];
107
108 Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent));
109
110 cbComponent = (uint32_t) strlen(pszStartComponent);
111
112 cbDirEntry = 4096;
113 pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
114 if (pDirEntry == 0)
115 {
116 AssertFailed();
117 return VERR_NO_MEMORY;
118 }
119
120 /** @todo this is quite inefficient, especially for directories with many files */
121 Assert(pszFullPath < pszStartComponent-1);
122 Assert(*(pszStartComponent-1) == RTPATH_DELIMITER);
123 *(pszStartComponent-1) = 0;
124 strcpy(pDirEntry->szName, pszFullPath);
125 szWildCard[0] = RTPATH_DELIMITER;
126 szWildCard[1] = '*';
127 szWildCard[2] = 0;
128 strcat(pDirEntry->szName, szWildCard);
129
130 rc = RTDirOpenFiltered (&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT);
131 *(pszStartComponent-1) = RTPATH_DELIMITER;
132 if (RT_FAILURE(rc))
133 goto end;
134
135 for(;;)
136 {
137 size_t cbDirEntrySize = cbDirEntry;
138
139 rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING);
140 if (rc == VERR_NO_MORE_FILES)
141 break;
142
143 if (VINF_SUCCESS != rc && rc != VWRN_NO_DIRENT_INFO)
144 {
145 AssertFailed();
146 if (rc != VERR_NO_TRANSLATION)
147 break;
148 else
149 continue;
150 }
151
152 Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0]));
153 if ( pDirEntry->cbName == cbComponent
154 && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0]))
155 {
156 Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent));
157 strcpy(pszStartComponent, &pDirEntry->szName[0]);
158 rc = VINF_SUCCESS;
159 break;
160 }
161 }
162
163end:
164 if (RT_FAILURE(rc))
165 Log(("vbsfCorrectCasing %s failed with %d\n", pszStartComponent, rc));
166
167 if (pDirEntry)
168 RTMemFree(pDirEntry);
169
170 if (hSearch)
171 RTDirClose(hSearch);
172 return rc;
173}
174
175static int vbsfBuildFullPath (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath,
176 uint32_t cbPath, char **ppszFullPath, uint32_t *pcbFullPathRoot, bool fWildCard = false)
177{
178 int rc = VINF_SUCCESS;
179
180 char *pszFullPath = NULL;
181
182 /* Query UCS2 root prefix for the path, cbRoot is the length in bytes including trailing (RTUTF16)0. */
183 uint32_t cbRoot = 0;
184 PCRTUTF16 pwszRoot = vbsfMappingsQueryHostRoot (root, &cbRoot);
185
186 if (!pwszRoot || cbRoot == 0)
187 {
188 Log(("vbsfBuildFullPath: invalid root!\n"));
189 return VERR_INVALID_PARAMETER;
190 }
191
192 if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8))
193 {
194 int rc;
195 char *utf8Root;
196
197 rc = RTUtf16ToUtf8 (pwszRoot, &utf8Root);
198 if (RT_SUCCESS (rc))
199 {
200 size_t cbUtf8Root, cbUtf8FullPath;
201 char *utf8FullPath;
202
203 cbUtf8Root = strlen (utf8Root);
204 cbUtf8FullPath = cbUtf8Root + 1 + pPath->u16Length + 1;
205 utf8FullPath = (char *) RTMemAllocZ (cbUtf8FullPath);
206
207 if (!utf8FullPath)
208 {
209 rc = VERR_NO_MEMORY;
210 *ppszFullPath = NULL;
211 Log(("RTMemAllocZ %x failed!!\n", cbUtf8FullPath));
212 }
213 else
214 {
215 memcpy (utf8FullPath, utf8Root, cbUtf8Root);
216 memcpy (utf8FullPath + cbUtf8Root + 1,
217 &pPath->String.utf8[0],
218 pPath->u16Length);
219
220 utf8FullPath[cbUtf8Root] = '/';
221 utf8FullPath[cbUtf8FullPath - 1] = 0;
222 pszFullPath = utf8FullPath;
223
224 if (pcbFullPathRoot)
225 *pcbFullPathRoot = (uint32_t)cbUtf8Root; /* Must index the path delimiter. */
226 }
227
228 RTStrFree (utf8Root);
229 }
230 else
231 {
232 Log (("vbsfBuildFullPath: RTUtf16ToUtf8 failed with %Rrc\n", rc));
233 }
234 }
235 else
236 {
237#ifdef RT_OS_DARWIN
238/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
239 * The question is simply whether the NFD normalization is actually applied on a (virtaul) file
240 * system level in darwin, or just by the user mode application libs. */
241 SHFLSTRING *pPathParameter = pPath;
242 size_t cbPathLength;
243 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
244 uint16_t ucs2Length;
245 CFRange rangeCharacters;
246
247 // Is 8 times length enough for decomposed in worst case...?
248 cbPathLength = sizeof(SHFLSTRING) + pPathParameter->u16Length * 8 + 2;
249 pPath = (SHFLSTRING *)RTMemAllocZ (cbPathLength);
250 if (!pPath)
251 {
252 rc = VERR_NO_MEMORY;
253 Log(("RTMemAllocZ %x failed!!\n", cbPathLength));
254 return rc;
255 }
256
257 ::CFStringAppendCharacters(inStr, (UniChar*)pPathParameter->String.ucs2, pPathParameter->u16Length / sizeof(pPathParameter->String.ucs2[0]));
258 ::CFStringNormalize(inStr, kCFStringNormalizationFormD);
259 ucs2Length = ::CFStringGetLength(inStr);
260
261 rangeCharacters.location = 0;
262 rangeCharacters.length = ucs2Length;
263 ::CFStringGetCharacters(inStr, rangeCharacters, pPath->String.ucs2);
264 pPath->String.ucs2[ucs2Length] = 0x0000; // NULL terminated
265 pPath->u16Length = ucs2Length * sizeof(pPath->String.ucs2[0]);
266 pPath->u16Size = pPath->u16Length + sizeof(pPath->String.ucs2[0]);
267
268 CFRelease(inStr);
269#endif
270 /* Client sends us UCS2, so convert it to UTF8. */
271 Log(("Root %ls path %.*ls\n", pwszRoot, pPath->u16Length/sizeof(pPath->String.ucs2[0]), pPath->String.ucs2));
272
273 /* Allocate buffer that will be able to contain
274 * the root prefix and the pPath converted to UTF8.
275 * Expect a 2 bytes UCS2 to be converted to 8 bytes UTF8
276 * in worst case.
277 */
278 uint32_t cbFullPath = (cbRoot/sizeof (RTUTF16) + ShflStringLength (pPath)) * 4;
279
280 pszFullPath = (char *)RTMemAllocZ (cbFullPath);
281
282 if (!pszFullPath)
283 {
284 rc = VERR_NO_MEMORY;
285 }
286 else
287 {
288 uint32_t cb = cbFullPath;
289
290 rc = RTUtf16ToUtf8Ex (pwszRoot, RTSTR_MAX, &pszFullPath, cb, NULL);
291 if (RT_FAILURE(rc))
292 {
293 AssertFailed();
294#ifdef RT_OS_DARWIN
295 RTMemFree(pPath);
296 pPath = pPathParameter;
297#endif
298 return rc;
299 }
300
301 char *dst = pszFullPath;
302
303 cbRoot = (uint32_t)strlen(dst);
304 if (dst[cbRoot - 1] != RTPATH_DELIMITER)
305 {
306 dst[cbRoot] = RTPATH_DELIMITER;
307 cbRoot++;
308 }
309
310 if (pcbFullPathRoot)
311 *pcbFullPathRoot = cbRoot - 1; /* Must index the path delimiter. */
312
313 dst += cbRoot;
314 cb -= cbRoot;
315
316 if (pPath->u16Length)
317 {
318 /* Convert and copy components. */
319 PRTUTF16 src = &pPath->String.ucs2[0];
320
321 /* Correct path delimiters */
322 if (pClient->PathDelimiter != RTPATH_DELIMITER)
323 {
324 LogFlow(("Correct path delimiter in %ls\n", src));
325 while (*src)
326 {
327 if (*src == pClient->PathDelimiter)
328 *src = RTPATH_DELIMITER;
329 src++;
330 }
331 src = &pPath->String.ucs2[0];
332 LogFlow(("Corrected string %ls\n", src));
333 }
334 if (*src == RTPATH_DELIMITER)
335 src++; /* we already appended a delimiter to the first part */
336
337 rc = RTUtf16ToUtf8Ex (src, RTSTR_MAX, &dst, cb, NULL);
338 if (RT_FAILURE(rc))
339 {
340 AssertFailed();
341#ifdef RT_OS_DARWIN
342 RTMemFree(pPath);
343 pPath = pPathParameter;
344#endif
345 return rc;
346 }
347
348 uint32_t l = (uint32_t)strlen (dst);
349
350 cb -= l;
351 dst += l;
352
353 Assert(cb > 0);
354 }
355
356 /* Nul terminate the string */
357 *dst = 0;
358 }
359#ifdef RT_OS_DARWIN
360 RTMemFree(pPath);
361 pPath = pPathParameter;
362#endif
363 }
364
365 if (RT_SUCCESS (rc))
366 {
367 /* When the host file system is case sensitive and the guest expects a case insensitive fs, then problems can occur */
368 if ( vbsfIsHostMappingCaseSensitive (root)
369 && !vbsfIsGuestMappingCaseSensitive(root))
370 {
371 RTFSOBJINFO info;
372 char *pszWildCardComponent = NULL;
373
374 if (fWildCard)
375 {
376 /* strip off the last path component, that contains the wildcard(s) */
377 uint32_t len = (uint32_t)strlen(pszFullPath);
378 char *src = pszFullPath + len - 1;
379
380 while(src > pszFullPath)
381 {
382 if (*src == RTPATH_DELIMITER)
383 break;
384 src--;
385 }
386 if (*src == RTPATH_DELIMITER)
387 {
388 bool fHaveWildcards = false;
389 char *temp = src;
390
391 while(*temp)
392 {
393 char uc = *temp;
394 if (uc == '*' || uc == '?' || uc == '>' || uc == '<' || uc == '"')
395 {
396 fHaveWildcards = true;
397 break;
398 }
399 temp++;
400 }
401
402 if (fHaveWildcards)
403 {
404 pszWildCardComponent = src;
405 *pszWildCardComponent = 0;
406 }
407 }
408 }
409
410 /** @todo don't check when creating files or directories; waste of time */
411 rc = RTPathQueryInfo(pszFullPath, &info, RTFSOBJATTRADD_NOTHING);
412 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
413 {
414 uint32_t len = (uint32_t)strlen(pszFullPath);
415 char *src = pszFullPath + len - 1;
416
417 Log(("Handle case insenstive guest fs on top of host case sensitive fs for %s\n", pszFullPath));
418
419 /* Find partial path that's valid */
420 while(src > pszFullPath)
421 {
422 if (*src == RTPATH_DELIMITER)
423 {
424 *src = 0;
425 rc = RTPathQueryInfo (pszFullPath, &info, RTFSOBJATTRADD_NOTHING);
426 *src = RTPATH_DELIMITER;
427 if (rc == VINF_SUCCESS)
428 {
429#ifdef DEBUG
430 *src = 0;
431 Log(("Found valid partial path %s\n", pszFullPath));
432 *src = RTPATH_DELIMITER;
433#endif
434 break;
435 }
436 }
437
438 src--;
439 }
440 Assert(*src == RTPATH_DELIMITER && RT_SUCCESS(rc));
441 if ( *src == RTPATH_DELIMITER
442 && RT_SUCCESS(rc))
443 {
444 src++;
445 for(;;)
446 {
447 char *end = src;
448 bool fEndOfString = true;
449
450 while(*end)
451 {
452 if (*end == RTPATH_DELIMITER)
453 break;
454 end++;
455 }
456
457 if (*end == RTPATH_DELIMITER)
458 {
459 fEndOfString = false;
460 *end = 0;
461 rc = RTPathQueryInfo(src, &info, RTFSOBJATTRADD_NOTHING);
462 Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND);
463 }
464 else
465 if (end == src)
466 rc = VINF_SUCCESS; /* trailing delimiter */
467 else
468 rc = VERR_FILE_NOT_FOUND;
469
470 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
471 {
472 /* path component is invalid; try to correct the casing */
473 rc = vbsfCorrectCasing(pszFullPath, src);
474 if (RT_FAILURE(rc))
475 {
476 if (!fEndOfString)
477 *end = RTPATH_DELIMITER; /* restore the original full path */
478 break;
479 }
480 }
481
482 if (fEndOfString)
483 break;
484
485 *end = RTPATH_DELIMITER;
486 src = end + 1;
487 }
488 if (RT_FAILURE(rc))
489 Log(("Unable to find suitable component rc=%d\n", rc));
490 }
491 else
492 rc = VERR_FILE_NOT_FOUND;
493
494 }
495 if (pszWildCardComponent)
496 *pszWildCardComponent = RTPATH_DELIMITER;
497
498 /* might be a new file so don't fail here! */
499 rc = VINF_SUCCESS;
500 }
501 *ppszFullPath = pszFullPath;
502
503 LogFlow(("vbsfBuildFullPath: %s rc=%Rrc\n", pszFullPath, rc));
504 }
505
506 return rc;
507}
508
509static void vbsfFreeFullPath (char *pszFullPath)
510{
511 RTMemFree (pszFullPath);
512}
513
514/**
515 * Convert shared folder create flags (see include/iprt/shflsvc.h) into iprt create flags.
516 *
517 * @returns iprt status code
518 * @param fShflFlags shared folder create flags
519 * @retval pfOpen iprt create flags
520 */
521static int vbsfConvertFileOpenFlags(unsigned fShflFlags, unsigned *pfOpen)
522{
523 unsigned fOpen = 0;
524 int rc = VINF_SUCCESS;
525
526 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_RW))
527 {
528 default:
529 case SHFL_CF_ACCESS_NONE:
530 {
531 /** @todo treat this as read access, but theoretically this could be a no access request. */
532 fOpen |= RTFILE_O_READ;
533 Log(("FLAG: SHFL_CF_ACCESS_NONE\n"));
534 break;
535 }
536
537 case SHFL_CF_ACCESS_READ:
538 {
539 fOpen |= RTFILE_O_READ;
540 Log(("FLAG: SHFL_CF_ACCESS_READ\n"));
541 break;
542 }
543
544 case SHFL_CF_ACCESS_WRITE:
545 {
546 fOpen |= RTFILE_O_WRITE;
547 Log(("FLAG: SHFL_CF_ACCESS_WRITE\n"));
548 break;
549 }
550
551 case SHFL_CF_ACCESS_READWRITE:
552 {
553 fOpen |= RTFILE_O_READWRITE;
554 Log(("FLAG: SHFL_CF_ACCESS_READWRITE\n"));
555 break;
556 }
557 }
558
559 /* Sharing mask */
560 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_DENY))
561 {
562 default:
563 case SHFL_CF_ACCESS_DENYNONE:
564 fOpen |= RTFILE_O_DENY_NONE;
565 Log(("FLAG: SHFL_CF_ACCESS_DENYNONE\n"));
566 break;
567
568 case SHFL_CF_ACCESS_DENYREAD:
569 fOpen |= RTFILE_O_DENY_READ;
570 Log(("FLAG: SHFL_CF_ACCESS_DENYREAD\n"));
571 break;
572
573 case SHFL_CF_ACCESS_DENYWRITE:
574 fOpen |= RTFILE_O_DENY_WRITE;
575 Log(("FLAG: SHFL_CF_ACCESS_DENYWRITE\n"));
576 break;
577
578 case SHFL_CF_ACCESS_DENYALL:
579 fOpen |= RTFILE_O_DENY_ALL;
580 Log(("FLAG: SHFL_CF_ACCESS_DENYALL\n"));
581 break;
582 }
583
584 /* Open/Create action mask */
585 switch (BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
586 {
587 case SHFL_CF_ACT_OPEN_IF_EXISTS:
588 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
589 {
590 fOpen |= RTFILE_O_OPEN_CREATE;
591 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
592 }
593 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
594 {
595 fOpen |= RTFILE_O_OPEN;
596 Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
597 }
598 else
599 {
600 Log(("FLAGS: invalid open/create action combination\n"));
601 rc = VERR_INVALID_PARAMETER;
602 }
603 break;
604 case SHFL_CF_ACT_FAIL_IF_EXISTS:
605 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
606 {
607 fOpen |= RTFILE_O_CREATE;
608 Log(("FLAGS: SHFL_CF_ACT_FAIL_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
609 }
610 else
611 {
612 Log(("FLAGS: invalid open/create action combination\n"));
613 rc = VERR_INVALID_PARAMETER;
614 }
615 break;
616 case SHFL_CF_ACT_REPLACE_IF_EXISTS:
617 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
618 {
619 fOpen |= RTFILE_O_CREATE_REPLACE;
620 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
621 }
622 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
623 {
624 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
625 Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
626 }
627 else
628 {
629 Log(("FLAGS: invalid open/create action combination\n"));
630 rc = VERR_INVALID_PARAMETER;
631 }
632 break;
633 case SHFL_CF_ACT_OVERWRITE_IF_EXISTS:
634 if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
635 {
636 fOpen |= RTFILE_O_CREATE_REPLACE;
637 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n"));
638 }
639 else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW))
640 {
641 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
642 Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n"));
643 }
644 else
645 {
646 Log(("FLAGS: invalid open/create action combination\n"));
647 rc = VERR_INVALID_PARAMETER;
648 }
649 break;
650 default:
651 rc = VERR_INVALID_PARAMETER;
652 Log(("FLAG: SHFL_CF_ACT_MASK_IF_EXISTS - invalid parameter\n"));
653 }
654
655 if (RT_SUCCESS(rc))
656 {
657 *pfOpen = fOpen;
658 }
659 return rc;
660}
661
662/**
663 * Open a file or create and open a new one.
664 *
665 * @returns IPRT status code
666 * @param pszPath Path to the file or folder on the host.
667 * @param pParms->CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
668 * @param pParms->Info When a new file is created this specifies the initial parameters.
669 * When a file is created or overwritten, it also specifies the
670 * initial size.
671 * @retval pParms->Result Shared folder status code, see include/VBox/shflsvc.h
672 * @retval pParms->Handle On success the (shared folder) handle of the file opened or
673 * created
674 * @retval pParms->Info On success the parameters of the file opened or created
675 */
676static int vbsfOpenFile (const char *pszPath, SHFLCREATEPARMS *pParms)
677{
678 LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms));
679 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
680
681 SHFLHANDLE handle = SHFL_HANDLE_NIL;
682 SHFLFILEHANDLE *pHandle = 0;
683 /* Open or create a file. */
684 unsigned fOpen = 0;
685 bool fNoError = false;
686 static int cErrors;
687
688 int rc = vbsfConvertFileOpenFlags(pParms->CreateFlags, &fOpen);
689 if (RT_SUCCESS(rc))
690 {
691 handle = vbsfAllocFileHandle();
692 }
693 if (SHFL_HANDLE_NIL != handle)
694 {
695 rc = VERR_NO_MEMORY; /* If this fails - rewritten immediately on success. */
696 pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(handle, SHFL_HF_TYPE_FILE);
697 }
698 if (0 != pHandle)
699 {
700 rc = RTFileOpen(&pHandle->file.Handle, pszPath, fOpen);
701 }
702 if (RT_FAILURE (rc))
703 {
704 switch (rc)
705 {
706 case VERR_FILE_NOT_FOUND:
707 pParms->Result = SHFL_FILE_NOT_FOUND;
708
709 /* This actually isn't an error, so correct the rc before return later,
710 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
711 fNoError = true;
712 break;
713 case VERR_PATH_NOT_FOUND:
714 pParms->Result = SHFL_PATH_NOT_FOUND;
715
716 /* This actually isn't an error, so correct the rc before return later,
717 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
718 fNoError = true;
719 break;
720 case VERR_ALREADY_EXISTS:
721 RTFSOBJINFO info;
722
723 /** @todo Possible race left here. */
724 if (RT_SUCCESS(RTPathQueryInfo (pszPath, &info, RTFSOBJATTRADD_NOTHING)))
725 {
726 pParms->Info = info;
727 }
728 pParms->Result = SHFL_FILE_EXISTS;
729
730 /* This actually isn't an error, so correct the rc before return later,
731 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
732 fNoError = true;
733 break;
734 case VERR_TOO_MANY_OPEN_FILES:
735 if (cErrors < 32)
736 {
737 LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath));
738#if defined RT_OS_LINUX || RT_OS_SOLARIS
739 if (cErrors < 1)
740 LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimt -n)\n"));
741#endif
742 cErrors++;
743 }
744 pParms->Result = SHFL_NO_RESULT;
745 break;
746 default:
747 pParms->Result = SHFL_NO_RESULT;
748 }
749 }
750
751 if (RT_SUCCESS(rc))
752 {
753 /** @note The shared folder status code is very approximate, as the runtime
754 * does not really provide this information. */
755 pParms->Result = SHFL_FILE_EXISTS; /* We lost the information as to whether it was
756 created when we eliminated the race. */
757 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
758 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
759 || ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS
760 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
761 {
762 /* For now, we do not treat a failure here as fatal. */
763 /* @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if
764 SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
765 RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
766 pParms->Result = SHFL_FILE_REPLACED;
767 }
768 if ( ( SHFL_CF_ACT_FAIL_IF_EXISTS
769 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
770 || ( SHFL_CF_ACT_CREATE_IF_NEW
771 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
772 {
773 pParms->Result = SHFL_FILE_CREATED;
774 }
775#if 0
776 /* @todo */
777 /* Set new attributes. */
778 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
779 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
780 || ( SHFL_CF_ACT_CREATE_IF_NEW
781 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
782 {
783 RTFileSetTimes(pHandle->file.Handle,
784 &pParms->Info.AccessTime,
785 &pParms->Info.ModificationTime,
786 &pParms->Info.ChangeTime,
787 &pParms->Info.BirthTime
788 );
789
790 RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
791 }
792#endif
793 RTFSOBJINFO info;
794
795 /* Get file information */
796 rc = RTFileQueryInfo (pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
797 if (RT_SUCCESS(rc))
798 {
799 pParms->Info = info;
800 }
801 }
802 if (RT_FAILURE(rc))
803 {
804 if ( (0 != pHandle)
805 && (NIL_RTFILE != pHandle->file.Handle)
806 && (0 != pHandle->file.Handle))
807 {
808 RTFileClose(pHandle->file.Handle);
809 pHandle->file.Handle = NIL_RTFILE;
810 }
811 if (SHFL_HANDLE_NIL != handle)
812 {
813 vbsfFreeFileHandle (handle);
814 }
815 }
816 else
817 {
818 pParms->Handle = handle;
819 }
820
821 /* Report the driver that all is okay, we're done here */
822 if (fNoError)
823 rc = VINF_SUCCESS;
824
825 LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc));
826 return rc;
827}
828
829/**
830 * Open a folder or create and open a new one.
831 *
832 * @returns IPRT status code
833 * @param pszPath Path to the file or folder on the host.
834 * @param pParms->CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
835 * @retval pParms->Result Shared folder status code, see include/VBox/shflsvc.h
836 * @retval pParms->Handle On success the (shared folder) handle of the folder opened or
837 * created
838 * @retval pParms->Info On success the parameters of the folder opened or created
839 *
840 * @note folders are created with fMode = 0777
841 */
842static int vbsfOpenDir (const char *pszPath, SHFLCREATEPARMS *pParms)
843{
844 LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms));
845 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
846
847 int rc = VERR_NO_MEMORY;
848 SHFLHANDLE handle = vbsfAllocDirHandle();
849 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(handle, SHFL_HF_TYPE_DIR);
850 if (0 != pHandle)
851 {
852 rc = VINF_SUCCESS;
853 pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */
854 /** @todo Can anyone think of a sensible, race-less way to do this? Although
855 I suspect that the race is inherent, due to the API available... */
856 /* Try to create the folder first if "create if new" is specified. If this
857 fails, and "open if exists" is specified, then we ignore the failure and try
858 to open the folder anyway. */
859 if ( SHFL_CF_ACT_CREATE_IF_NEW
860 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))
861 {
862 /** @todo render supplied attributes.
863 * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */
864 RTFMODE fMode = 0777;
865
866 pParms->Result = SHFL_FILE_CREATED;
867 rc = RTDirCreate(pszPath, fMode);
868 if (RT_FAILURE (rc))
869 {
870 switch (rc)
871 {
872 case VERR_ALREADY_EXISTS:
873 pParms->Result = SHFL_FILE_EXISTS;
874 break;
875 case VERR_PATH_NOT_FOUND:
876 pParms->Result = SHFL_PATH_NOT_FOUND;
877 break;
878 default:
879 pParms->Result = SHFL_NO_RESULT;
880 }
881 }
882 }
883 if ( RT_SUCCESS(rc)
884 || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
885 {
886 /* Open the directory now */
887 rc = RTDirOpen (&pHandle->dir.Handle, pszPath);
888 if (RT_SUCCESS(rc))
889 {
890 RTFSOBJINFO info;
891
892 rc = RTDirQueryInfo (pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING);
893 if (RT_SUCCESS(rc))
894 {
895 pParms->Info = info;
896 }
897 }
898 else
899 {
900 switch (rc)
901 {
902 case VERR_FILE_NOT_FOUND: /* Does this make sense? */
903 pParms->Result = SHFL_FILE_NOT_FOUND;
904 break;
905 case VERR_PATH_NOT_FOUND:
906 pParms->Result = SHFL_PATH_NOT_FOUND;
907 break;
908 case VERR_ACCESS_DENIED:
909 pParms->Result = SHFL_FILE_EXISTS;
910 break;
911 default:
912 pParms->Result = SHFL_NO_RESULT;
913 }
914 }
915 }
916 }
917 if (RT_FAILURE(rc))
918 {
919 if ( (0 != pHandle)
920 && (0 != pHandle->dir.Handle))
921 {
922 RTDirClose(pHandle->dir.Handle);
923 pHandle->dir.Handle = 0;
924 }
925 if (SHFL_HANDLE_NIL != handle)
926 {
927 vbsfFreeFileHandle (handle);
928 }
929 }
930 else
931 {
932 pParms->Handle = handle;
933 }
934 LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc));
935 return rc;
936}
937
938static int vbsfCloseDir (SHFLFILEHANDLE *pHandle)
939{
940 int rc = VINF_SUCCESS;
941
942 LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n",
943 pHandle->dir.Handle, pHandle->dir.SearchHandle));
944
945 RTDirClose (pHandle->dir.Handle);
946
947 if (pHandle->dir.SearchHandle)
948 RTDirClose(pHandle->dir.SearchHandle);
949
950 if (pHandle->dir.pLastValidEntry)
951 {
952 RTMemFree(pHandle->dir.pLastValidEntry);
953 pHandle->dir.pLastValidEntry = NULL;
954 }
955
956 LogFlow(("vbsfCloseDir: rc = %d\n", rc));
957
958 return rc;
959}
960
961
962static int vbsfCloseFile (SHFLFILEHANDLE *pHandle)
963{
964 int rc = VINF_SUCCESS;
965
966 LogFlow(("vbsfCloseFile: Handle = %08X\n",
967 pHandle->file.Handle));
968
969 rc = RTFileClose (pHandle->file.Handle);
970
971 LogFlow(("vbsfCloseFile: rc = %d\n", rc));
972
973 return rc;
974}
975
976/**
977 * Look up file or folder information by host path.
978 *
979 * @returns iprt status code (currently VINF_SUCCESS)
980 * @param pszFullPath The path of the file to be looked up
981 * @retval pParms->Result Status of the operation (success or error)
982 * @retval pParms->Info On success, information returned about the file
983 */
984static int vbsfLookupFile(char *pszPath, SHFLCREATEPARMS *pParms)
985{
986 RTFSOBJINFO info;
987 int rc;
988
989 rc = RTPathQueryInfo (pszPath, &info, RTFSOBJATTRADD_NOTHING);
990 LogFlow(("SHFL_CF_LOOKUP\n"));
991 /* Client just wants to know if the object exists. */
992 switch (rc)
993 {
994 case VINF_SUCCESS:
995 {
996 pParms->Info = info;
997 pParms->Result = SHFL_FILE_EXISTS;
998 break;
999 }
1000
1001 case VERR_FILE_NOT_FOUND:
1002 {
1003 pParms->Result = SHFL_FILE_NOT_FOUND;
1004 rc = VINF_SUCCESS;
1005 break;
1006 }
1007
1008 case VERR_PATH_NOT_FOUND:
1009 {
1010 pParms->Result = SHFL_PATH_NOT_FOUND;
1011 rc = VINF_SUCCESS;
1012 break;
1013 }
1014 }
1015 return rc;
1016}
1017
1018/**
1019 * Create or open a file or folder. Perform character set and case
1020 * conversion on the file name if necessary.
1021 *
1022 * @returns IPRT status code, but see note below
1023 * @param pClient Data structure describing the client accessing the shared
1024 * folder
1025 * @param root The index of the shared folder in the table of mappings.
1026 * The host path of the shared folder is found using this.
1027 * @param pPath The path of the file or folder relative to the host path
1028 * indexed by root.
1029 * @param cbPath Presumably the length of the path in pPath. Actually
1030 * ignored, as pPath contains a length parameter.
1031 * @param pParms->Info If a new file is created or an old one overwritten, set
1032 * these attributes
1033 * @retval pParms->Result Shared folder result code, see include/VBox/shflsvc.h
1034 * @retval pParms->Handle Shared folder handle to the newly opened file
1035 * @retval pParms->Info Attributes of the file or folder opened
1036 *
1037 * @note This function returns success if a "non-exceptional" error occurred,
1038 * such as "no such file". In this case, the caller should check the
1039 * pParms->Result return value and whether pParms->Handle is valid.
1040 */
1041int vbsfCreate (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
1042{
1043 int rc = VINF_SUCCESS;
1044
1045 LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
1046 pClient, pPath, cbPath, pParms, pParms->CreateFlags));
1047
1048 /* Check the client access rights to the root. */
1049 /** @todo */
1050
1051 /* Build a host full path for the given path, handle file name case issues (if the guest
1052 * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
1053 * necessary.
1054 */
1055 char *pszFullPath = NULL;
1056 uint32_t cbFullPathRoot = 0;
1057
1058 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1059
1060 if (RT_SUCCESS (rc))
1061 {
1062 /* Reset return values in case client forgot to do so. */
1063 pParms->Result = SHFL_NO_RESULT;
1064 pParms->Handle = SHFL_HANDLE_NIL;
1065
1066 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
1067 {
1068 rc = vbsfLookupFile(pszFullPath, pParms);
1069 }
1070 else
1071 {
1072 /* Query path information. */
1073 RTFSOBJINFO info;
1074
1075 rc = RTPathQueryInfo (pszFullPath, &info, RTFSOBJATTRADD_NOTHING);
1076 LogFlow(("RTPathQueryInfo returned %Rrc\n", rc));
1077
1078 if (RT_SUCCESS(rc))
1079 {
1080 /* Mark it as a directory in case the caller didn't. */
1081 /**
1082 * @todo I left this in in order not to change the behaviour of the
1083 * function too much. Is it really needed, and should it really be
1084 * here?
1085 */
1086 if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
1087 {
1088 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
1089 }
1090
1091 /**
1092 * @todo This should be in the Windows Guest Additions, as no-one else
1093 * needs it.
1094 */
1095 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
1096 {
1097 vbsfStripLastComponent (pszFullPath, cbFullPathRoot);
1098 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
1099 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
1100 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
1101 pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
1102 pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
1103 }
1104 }
1105
1106 rc = VINF_SUCCESS;
1107
1108 /* write access requested? */
1109 if (pParms->CreateFlags & ( SHFL_CF_ACT_REPLACE_IF_EXISTS
1110 | SHFL_CF_ACT_OVERWRITE_IF_EXISTS
1111 | SHFL_CF_ACT_CREATE_IF_NEW
1112 | SHFL_CF_ACCESS_WRITE))
1113 {
1114 /* is the guest allowed to write to this share? */
1115 bool fWritable;
1116 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1117 if (RT_FAILURE(rc) || !fWritable)
1118 rc = VERR_WRITE_PROTECT;
1119 }
1120
1121 if (RT_SUCCESS(rc))
1122 {
1123 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
1124 {
1125 rc = vbsfOpenDir (pszFullPath, pParms);
1126 }
1127 else
1128 {
1129 rc = vbsfOpenFile (pszFullPath, pParms);
1130 }
1131 }
1132 }
1133
1134 /* free the path string */
1135 vbsfFreeFullPath(pszFullPath);
1136 }
1137
1138 Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
1139
1140 return rc;
1141}
1142
1143int vbsfClose (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1144{
1145 int rc = VINF_SUCCESS;
1146
1147 LogFlow(("vbsfClose: pClient = %p, Handle = %RX64\n",
1148 pClient, Handle));
1149
1150 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1151 Assert(pHandle);
1152 if (!pHandle)
1153 return VERR_INVALID_HANDLE;
1154
1155 switch (ShflHandleType (&pHandle->Header))
1156 {
1157 case SHFL_HF_TYPE_DIR:
1158 {
1159 rc = vbsfCloseDir (pHandle);
1160 break;
1161 }
1162 case SHFL_HF_TYPE_FILE:
1163 {
1164 rc = vbsfCloseFile (pHandle);
1165 break;
1166 }
1167 default:
1168 AssertFailed();
1169 break;
1170 }
1171 vbsfFreeFileHandle(Handle);
1172
1173 Log(("vbsfClose: rc = %Rrc\n", rc));
1174
1175 return rc;
1176}
1177
1178int vbsfRead (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1179{
1180 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1181 size_t count = 0;
1182 int rc;
1183
1184 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1185 {
1186 AssertFailed();
1187 return VERR_INVALID_PARAMETER;
1188 }
1189
1190 Log(("vbsfRead %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer));
1191
1192 if (*pcbBuffer == 0)
1193 return VINF_SUCCESS; /* @todo correct? */
1194
1195
1196 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1197 if (rc != VINF_SUCCESS)
1198 {
1199 AssertRC(rc);
1200 return rc;
1201 }
1202
1203 rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1204 *pcbBuffer = (uint32_t)count;
1205 Log(("RTFileRead returned %Rrc bytes read %x\n", rc, count));
1206 return rc;
1207}
1208
1209int vbsfWrite (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1210{
1211 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1212 size_t count = 0;
1213 int rc;
1214
1215 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1216 {
1217 AssertFailed();
1218 return VERR_INVALID_PARAMETER;
1219 }
1220
1221 Log(("vbsfWrite %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer));
1222
1223 /* Is the guest allowed to write to this share?
1224 * XXX Actually this check was still done in vbsfCreate() -- RTFILE_O_WRITE cannot be set if vbsfMappingsQueryWritable() failed. */
1225 bool fWritable;
1226 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1227 if (RT_FAILURE(rc) || !fWritable)
1228 return VERR_WRITE_PROTECT;
1229
1230 if (*pcbBuffer == 0)
1231 return VINF_SUCCESS; /** @todo correct? */
1232
1233 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1234 if (rc != VINF_SUCCESS)
1235 {
1236 AssertRC(rc);
1237 return rc;
1238 }
1239
1240 rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1241 *pcbBuffer = (uint32_t)count;
1242 Log(("RTFileWrite returned %Rrc bytes written %x\n", rc, count));
1243 return rc;
1244}
1245
1246
1247int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1248{
1249 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1250 int rc = VINF_SUCCESS;
1251
1252 if (pHandle == 0)
1253 {
1254 AssertFailed();
1255 return VERR_INVALID_HANDLE;
1256 }
1257
1258 Log(("vbsfFlush %RX64\n", Handle));
1259 rc = RTFileFlush(pHandle->file.Handle);
1260 AssertRC(rc);
1261 return rc;
1262}
1263
1264int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer,
1265 uint32_t *pIndex, uint32_t *pcFiles)
1266{
1267 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR);
1268 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1269 uint32_t cbDirEntry, cbBufferOrg;
1270 int rc = VINF_SUCCESS;
1271 PSHFLDIRINFO pSFDEntry;
1272 PRTUTF16 pwszString;
1273 PRTDIR DirHandle;
1274 bool fUtf8;
1275
1276 fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1277
1278 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1279 {
1280 AssertFailed();
1281 return VERR_INVALID_PARAMETER;
1282 }
1283 Assert(pIndex && *pIndex == 0);
1284 DirHandle = pHandle->dir.Handle;
1285
1286 cbDirEntry = 4096;
1287 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1288 if (pDirEntry == 0)
1289 {
1290 AssertFailed();
1291 return VERR_NO_MEMORY;
1292 }
1293
1294 cbBufferOrg = *pcbBuffer;
1295 *pcbBuffer = 0;
1296 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1297
1298 *pIndex = 1; /* not yet complete */
1299 *pcFiles = 0;
1300
1301 if (pPath)
1302 {
1303 if (pHandle->dir.SearchHandle == 0)
1304 {
1305 /* Build a host full path for the given path
1306 * and convert ucs2 to utf8 if necessary.
1307 */
1308 char *pszFullPath = NULL;
1309
1310 Assert(pHandle->dir.pLastValidEntry == 0);
1311
1312 rc = vbsfBuildFullPath (pClient, root, pPath, pPath->u16Size, &pszFullPath, NULL, true);
1313
1314 if (RT_SUCCESS (rc))
1315 {
1316 rc = RTDirOpenFiltered (&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT);
1317
1318 /* free the path string */
1319 vbsfFreeFullPath(pszFullPath);
1320
1321 if (RT_FAILURE (rc))
1322 goto end;
1323 }
1324 else
1325 goto end;
1326 }
1327 Assert(pHandle->dir.SearchHandle);
1328 DirHandle = pHandle->dir.SearchHandle;
1329 }
1330
1331 while(cbBufferOrg)
1332 {
1333 size_t cbDirEntrySize = cbDirEntry;
1334 uint32_t cbNeeded;
1335
1336 /* Do we still have a valid last entry for the active search? If so, then return it here */
1337 if (pHandle->dir.pLastValidEntry)
1338 {
1339 pDirEntry = pHandle->dir.pLastValidEntry;
1340 }
1341 else
1342 {
1343 pDirEntry = pDirEntryOrg;
1344
1345 rc = RTDirReadEx(DirHandle, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING);
1346 if (rc == VERR_NO_MORE_FILES)
1347 {
1348 *pIndex = 0; /* listing completed */
1349 break;
1350 }
1351
1352 if (VINF_SUCCESS != rc && rc != VWRN_NO_DIRENT_INFO)
1353 {
1354 AssertFailed();
1355 if (rc != VERR_NO_TRANSLATION)
1356 break;
1357 else
1358 continue;
1359 }
1360 }
1361
1362 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String);
1363 if (fUtf8)
1364 cbNeeded += pDirEntry->cbName + 1;
1365 else
1366 /* Overestimating, but that's ok */
1367 cbNeeded += (pDirEntry->cbName + 1) * 2;
1368
1369 if (cbBufferOrg < cbNeeded)
1370 {
1371 /* No room, so save this directory entry, or else it's lost forever */
1372 pHandle->dir.pLastValidEntry = pDirEntry;
1373
1374 if (*pcFiles == 0)
1375 {
1376 AssertFailed();
1377 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1378 }
1379 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1380 }
1381
1382 pSFDEntry->Info = pDirEntry->Info;
1383 pSFDEntry->cucShortName = 0;
1384
1385 if (fUtf8)
1386 {
1387 void *src, *dst;
1388
1389 src = &pDirEntry->szName[0];
1390 dst = &pSFDEntry->name.String.utf8[0];
1391
1392 memcpy (dst, src, pDirEntry->cbName + 1);
1393
1394 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1395 pSFDEntry->name.u16Length = pDirEntry->cbName;
1396 }
1397 else
1398 {
1399 pSFDEntry->name.String.ucs2[0] = 0;
1400 pwszString = pSFDEntry->name.String.ucs2;
1401 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1402 AssertRC(rc2);
1403
1404#ifdef RT_OS_DARWIN
1405/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver...
1406 * The question is simply whether the NFD normalization is actually applied on a (virtaul) file
1407 * system level in darwin, or just by the user mode application libs. */
1408 {
1409 // Convert to
1410 // Normalization Form C (composed Unicode). We need this because
1411 // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode)
1412 // while most other OS', server-side programs usually expect NFC.
1413 uint16_t ucs2Length;
1414 CFRange rangeCharacters;
1415 CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0);
1416
1417 ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len (pwszString));
1418 ::CFStringNormalize(inStr, kCFStringNormalizationFormC);
1419 ucs2Length = ::CFStringGetLength(inStr);
1420
1421 rangeCharacters.location = 0;
1422 rangeCharacters.length = ucs2Length;
1423 ::CFStringGetCharacters(inStr, rangeCharacters, pwszString);
1424 pwszString[ucs2Length] = 0x0000; // NULL terminated
1425
1426 CFRelease(inStr);
1427 }
1428#endif
1429 pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len (pSFDEntry->name.String.ucs2) * 2;
1430 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1431
1432 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1433 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1434
1435 // adjust cbNeeded (it was overestimated before)
1436 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1437 }
1438
1439 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1440 *pcbBuffer += cbNeeded;
1441 cbBufferOrg-= cbNeeded;
1442
1443 *pcFiles += 1;
1444
1445 /* Free the saved last entry, that we've just returned */
1446 if (pHandle->dir.pLastValidEntry)
1447 {
1448 RTMemFree(pHandle->dir.pLastValidEntry);
1449 pHandle->dir.pLastValidEntry = NULL;
1450 }
1451
1452 if (flags & SHFL_LIST_RETURN_ONE)
1453 break; /* we're done */
1454 }
1455 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1456
1457end:
1458 if (pDirEntry)
1459 RTMemFree(pDirEntry);
1460
1461 return rc;
1462}
1463
1464int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1465{
1466 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1467 int rc = VINF_SUCCESS;
1468 RTFSOBJINFO *pObjInfo = (RTFSOBJINFO *)pBuffer;
1469
1470
1471 if (pHandle == 0 || pcbBuffer == 0 || pObjInfo == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1472 {
1473 AssertFailed();
1474 return VERR_INVALID_PARAMETER;
1475 }
1476
1477 /* @todo other options */
1478 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1479
1480 *pcbBuffer = 0;
1481
1482 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1483 {
1484 rc = RTDirQueryInfo(pHandle->dir.Handle, pObjInfo, RTFSOBJATTRADD_NOTHING);
1485 }
1486 else
1487 {
1488 rc = RTFileQueryInfo(pHandle->file.Handle, pObjInfo, RTFSOBJATTRADD_NOTHING);
1489 }
1490 if (rc == VINF_SUCCESS)
1491 {
1492 *pcbBuffer = sizeof(RTFSOBJINFO);
1493 }
1494 else
1495 AssertFailed();
1496
1497 return rc;
1498}
1499
1500static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1501{
1502 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1503 int rc = VINF_SUCCESS;
1504 RTFSOBJINFO *pSFDEntry;
1505
1506 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1507 {
1508 AssertFailed();
1509 return VERR_INVALID_PARAMETER;
1510 }
1511
1512 *pcbBuffer = 0;
1513 pSFDEntry = (RTFSOBJINFO *)pBuffer;
1514
1515 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1516
1517 /* Change only the time values that are not zero */
1518 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1519 {
1520 rc = RTDirSetTimes(pHandle->dir.Handle,
1521 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1522 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1523 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1524 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1525 );
1526 }
1527 else
1528 {
1529 rc = RTFileSetTimes(pHandle->file.Handle,
1530 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1531 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1532 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1533 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1534 );
1535 }
1536 if (rc != VINF_SUCCESS)
1537 {
1538 Log(("RTFileSetTimes failed with %Rrc\n", rc));
1539 Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1540 Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1541 Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1542 Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1543 /* temporary hack */
1544 rc = VINF_SUCCESS;
1545 }
1546
1547 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_FILE)
1548 {
1549 /* Change file attributes if necessary */
1550 if (pSFDEntry->Attr.fMode)
1551 {
1552 rc = RTFileSetMode((RTFILE)pHandle->file.Handle, pSFDEntry->Attr.fMode);
1553 if (rc != VINF_SUCCESS)
1554 {
1555 Log(("RTFileSetMode %x failed with %Rrc\n", pSFDEntry->Attr.fMode, rc));
1556 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1557 rc = VINF_SUCCESS;
1558 }
1559 }
1560 }
1561
1562 if (rc == VINF_SUCCESS)
1563 {
1564 uint32_t bufsize = sizeof(*pSFDEntry);
1565
1566 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1567 if (rc == VINF_SUCCESS)
1568 {
1569 *pcbBuffer = sizeof(RTFSOBJINFO);
1570 }
1571 else
1572 AssertFailed();
1573 }
1574
1575 return rc;
1576}
1577
1578
1579static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1580{
1581 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1582 int rc = VINF_SUCCESS;
1583 RTFSOBJINFO *pSFDEntry;
1584
1585 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1586 {
1587 AssertFailed();
1588 return VERR_INVALID_PARAMETER;
1589 }
1590
1591 *pcbBuffer = 0;
1592 pSFDEntry = (RTFSOBJINFO *)pBuffer;
1593
1594 if (flags & SHFL_INFO_SIZE)
1595 {
1596 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1597 if (rc != VINF_SUCCESS)
1598 AssertFailed();
1599 }
1600 else
1601 AssertFailed();
1602
1603 if (rc == VINF_SUCCESS)
1604 {
1605 RTFSOBJINFO fileinfo;
1606
1607 /* Query the new object info and return it */
1608 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1609 if (rc == VINF_SUCCESS)
1610 {
1611 *pSFDEntry = fileinfo;
1612 *pcbBuffer = sizeof(RTFSOBJINFO);
1613 }
1614 else
1615 AssertFailed();
1616 }
1617
1618 return rc;
1619}
1620
1621int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1622{
1623 int rc = VINF_SUCCESS;
1624 SHFLVOLINFO *pSFDEntry;
1625 char *pszFullPath = NULL;
1626 SHFLSTRING dummy;
1627
1628 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1629 {
1630 AssertFailed();
1631 return VERR_INVALID_PARAMETER;
1632 }
1633
1634 /* @todo other options */
1635 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1636
1637 *pcbBuffer = 0;
1638 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1639
1640 ShflStringInitBuffer(&dummy, sizeof(dummy));
1641 rc = vbsfBuildFullPath (pClient, root, &dummy, 0, &pszFullPath, NULL);
1642
1643 if (RT_SUCCESS (rc))
1644 {
1645 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1646 if (rc != VINF_SUCCESS)
1647 goto exit;
1648
1649 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1650 if (rc != VINF_SUCCESS)
1651 goto exit;
1652
1653 rc = RTFsQueryProperties(pszFullPath, &pSFDEntry->fsProperties);
1654 if (rc != VINF_SUCCESS)
1655 goto exit;
1656
1657 *pcbBuffer = sizeof(SHFLVOLINFO);
1658 }
1659 else AssertFailed();
1660
1661exit:
1662 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc));
1663 /* free the path string */
1664 vbsfFreeFullPath(pszFullPath);
1665 return rc;
1666}
1667
1668int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1669{
1670 if (pcbBuffer == 0 || pBuffer == 0)
1671 {
1672 AssertFailed();
1673 return VERR_INVALID_PARAMETER;
1674 }
1675
1676 if (flags & SHFL_INFO_FILE)
1677 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1678
1679 if (flags & SHFL_INFO_VOLUME)
1680 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
1681
1682 AssertFailed();
1683 return VERR_INVALID_PARAMETER;
1684}
1685
1686int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1687{
1688 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
1689
1690 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1691 {
1692 AssertFailed();
1693 return VERR_INVALID_PARAMETER;
1694 }
1695
1696 /* is the guest allowed to write to this share? */
1697 bool fWritable;
1698 int rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1699 if (RT_FAILURE(rc) || !fWritable)
1700 return VERR_WRITE_PROTECT;
1701
1702 if (flags & SHFL_INFO_FILE)
1703 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1704
1705 if (flags & SHFL_INFO_SIZE)
1706 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1707
1708// if (flags & SHFL_INFO_VOLUME)
1709// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1710 AssertFailed();
1711 return VERR_INVALID_PARAMETER;
1712}
1713
1714int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1715{
1716 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1717 uint32_t fRTLock = 0;
1718 int rc;
1719
1720 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
1721
1722 if (pHandle == 0)
1723 {
1724 AssertFailed();
1725 return VERR_INVALID_HANDLE;
1726 }
1727 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
1728 || (flags & SHFL_LOCK_ENTIRE)
1729 )
1730 {
1731 AssertFailed();
1732 return VERR_INVALID_PARAMETER;
1733 }
1734
1735 /* Lock type */
1736 switch(flags & SHFL_LOCK_MODE_MASK)
1737 {
1738 case SHFL_LOCK_SHARED:
1739 fRTLock = RTFILE_LOCK_READ;
1740 break;
1741
1742 case SHFL_LOCK_EXCLUSIVE:
1743 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
1744 break;
1745
1746 default:
1747 AssertFailed();
1748 return VERR_INVALID_PARAMETER;
1749 }
1750
1751 /* Lock wait type */
1752 if (flags & SHFL_LOCK_WAIT)
1753 fRTLock |= RTFILE_LOCK_WAIT;
1754 else
1755 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
1756
1757#ifdef RT_OS_WINDOWS
1758 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
1759 if (rc != VINF_SUCCESS)
1760 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1761#else
1762 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
1763 rc = VINF_SUCCESS;
1764#endif
1765 return rc;
1766}
1767
1768int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1769{
1770 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1771 int rc;
1772
1773 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
1774
1775 if (pHandle == 0)
1776 {
1777 return VERR_INVALID_HANDLE;
1778 }
1779 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
1780 || (flags & SHFL_LOCK_ENTIRE)
1781 )
1782 {
1783 return VERR_INVALID_PARAMETER;
1784 }
1785
1786#ifdef RT_OS_WINDOWS
1787 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
1788 if (rc != VINF_SUCCESS)
1789 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1790#else
1791 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
1792 rc = VINF_SUCCESS;
1793#endif
1794
1795 return rc;
1796}
1797
1798
1799int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
1800{
1801 int rc = VINF_SUCCESS;
1802
1803 /* Validate input */
1804 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR)
1805 || cbPath == 0
1806 || pPath == 0)
1807 {
1808 AssertFailed();
1809 return VERR_INVALID_PARAMETER;
1810 }
1811
1812 /* Build a host full path for the given path
1813 * and convert ucs2 to utf8 if necessary.
1814 */
1815 char *pszFullPath = NULL;
1816
1817 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, NULL);
1818 if (RT_SUCCESS (rc))
1819 {
1820 /* is the guest allowed to write to this share? */
1821 bool fWritable;
1822 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1823 if (RT_FAILURE(rc) || !fWritable)
1824 rc = VERR_WRITE_PROTECT;
1825
1826 if (RT_SUCCESS (rc))
1827 {
1828 if (flags & SHFL_REMOVE_FILE)
1829 rc = RTFileDelete(pszFullPath);
1830 else
1831 rc = RTDirRemove(pszFullPath);
1832 }
1833
1834#ifndef DEBUG_dmik
1835 // VERR_ACCESS_DENIED for example?
1836 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
1837#endif
1838 /* free the path string */
1839 vbsfFreeFullPath(pszFullPath);
1840 }
1841 return rc;
1842}
1843
1844
1845int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
1846{
1847 int rc = VINF_SUCCESS;
1848
1849 /* Validate input */
1850 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
1851 || pSrc == 0
1852 || pDest == 0)
1853 {
1854 AssertFailed();
1855 return VERR_INVALID_PARAMETER;
1856 }
1857
1858 /* Build a host full path for the given path
1859 * and convert ucs2 to utf8 if necessary.
1860 */
1861 char *pszFullPathSrc = NULL;
1862 char *pszFullPathDest = NULL;
1863
1864 rc = vbsfBuildFullPath (pClient, root, pSrc, pSrc->u16Size, &pszFullPathSrc, NULL);
1865 if (rc != VINF_SUCCESS)
1866 return rc;
1867
1868 rc = vbsfBuildFullPath (pClient, root, pDest, pDest->u16Size, &pszFullPathDest, NULL);
1869 if (RT_SUCCESS (rc))
1870 {
1871 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
1872
1873 /* is the guest allowed to write to this share? */
1874 bool fWritable;
1875 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1876 if (RT_FAILURE(rc) || !fWritable)
1877 rc = VERR_WRITE_PROTECT;
1878
1879 if (RT_SUCCESS (rc))
1880 {
1881 if (flags & SHFL_RENAME_FILE)
1882 {
1883 rc = RTFileMove(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0);
1884 }
1885 else
1886 {
1887 /* NT ignores the REPLACE flag and simply return and already exists error. */
1888 rc = RTDirRename(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0);
1889 }
1890 }
1891
1892#ifndef DEBUG_dmik
1893 AssertRC(rc);
1894#endif
1895 /* free the path string */
1896 vbsfFreeFullPath(pszFullPathDest);
1897 }
1898 /* free the path string */
1899 vbsfFreeFullPath(pszFullPathSrc);
1900 return rc;
1901}
1902
1903/*
1904 * Clean up our mess by freeing all handles that are still valid.
1905 *
1906 */
1907int vbsfDisconnect(SHFLCLIENTDATA *pClient)
1908{
1909 for (int i=0;i<SHFLHANDLE_MAX;i++)
1910 {
1911 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(i, SHFL_HF_TYPE_MASK);
1912
1913 if (pHandle)
1914 {
1915 Log(("Open handle %08x\n", i));
1916 vbsfClose(pClient, SHFL_HANDLE_ROOT /* incorrect, but it's not important */, (SHFLHANDLE)i);
1917 }
1918 }
1919 return VINF_SUCCESS;
1920}
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