VirtualBox

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

Last change on this file since 6379 was 6379, checked in by vboxsync, 17 years ago

support read-only shared folders

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