VirtualBox

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

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

more places to check if writing to a shared folder is allowed

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.6 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 rc = VINF_SUCCESS;
1022
1023 /* write access requested? */
1024 if (pParms->CreateFlags & ( SHFL_CF_ACT_REPLACE_IF_EXISTS
1025 | SHFL_CF_ACT_OVERWRITE_IF_EXISTS
1026 | SHFL_CF_ACT_CREATE_IF_NEW
1027 | SHFL_CF_ACCESS_WRITE))
1028 {
1029 /* is the guest allowed to write to this share? */
1030 bool fWritable;
1031 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1032 if (RT_FAILURE(rc) || !fWritable)
1033 rc = VERR_WRITE_PROTECT;
1034 }
1035
1036 if (RT_SUCCESS(rc))
1037 {
1038 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
1039 rc = vbsfOpenDir (pszFullPath, pParms);
1040 else
1041 rc = vbsfOpenFile (pszFullPath, pParms);
1042 }
1043 }
1044
1045 /* free the path string */
1046 vbsfFreeFullPath(pszFullPath);
1047 }
1048
1049 Log(("vbsfCreate: handle = %RX64 rc = %Vrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
1050
1051 return rc;
1052}
1053
1054int vbsfClose (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1055{
1056 int rc = VINF_SUCCESS;
1057
1058 LogFlow(("vbsfClose: pClient = %p, Handle = %RX64\n",
1059 pClient, Handle));
1060
1061 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1062 Assert(pHandle);
1063 if (!pHandle)
1064 return VERR_INVALID_HANDLE;
1065
1066 switch (ShflHandleType (&pHandle->Header))
1067 {
1068 case SHFL_HF_TYPE_DIR:
1069 {
1070 rc = vbsfCloseDir (pHandle);
1071 break;
1072 }
1073 case SHFL_HF_TYPE_FILE:
1074 {
1075 rc = vbsfCloseFile (pHandle);
1076 break;
1077 }
1078 default:
1079 AssertFailed();
1080 break;
1081 }
1082 vbsfFreeFileHandle(Handle);
1083
1084 Log(("vbsfClose: rc = %Rrc\n", rc));
1085
1086 return rc;
1087}
1088
1089int vbsfRead (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1090{
1091 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1092 size_t count = 0;
1093 int rc;
1094
1095 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1096 {
1097 AssertFailed();
1098 return VERR_INVALID_PARAMETER;
1099 }
1100
1101 Log(("vbsfRead %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer));
1102
1103 if (*pcbBuffer == 0)
1104 return VINF_SUCCESS; /* @todo correct? */
1105
1106
1107 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1108 if (rc != VINF_SUCCESS)
1109 {
1110 AssertRC(rc);
1111 return rc;
1112 }
1113
1114 rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1115 *pcbBuffer = (uint32_t)count;
1116 Log(("RTFileRead returned %Vrc bytes read %x\n", rc, count));
1117 return rc;
1118}
1119
1120int vbsfWrite (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1121{
1122 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1123 size_t count = 0;
1124 int rc;
1125
1126 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1127 {
1128 AssertFailed();
1129 return VERR_INVALID_PARAMETER;
1130 }
1131
1132 Log(("vbsfWrite %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer));
1133
1134 /* is the guest allowed to write to this share? */
1135 bool fWritable;
1136 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1137 if (RT_FAILURE(rc) || !fWritable)
1138 return VERR_WRITE_PROTECT;
1139
1140 if (*pcbBuffer == 0)
1141 return VINF_SUCCESS; /** @todo correct? */
1142
1143 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1144 if (rc != VINF_SUCCESS)
1145 {
1146 AssertRC(rc);
1147 return rc;
1148 }
1149
1150 rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1151 *pcbBuffer = (uint32_t)count;
1152 Log(("RTFileWrite returned %Vrc bytes written %x\n", rc, count));
1153 return rc;
1154}
1155
1156
1157int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1158{
1159 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1160 int rc = VINF_SUCCESS;
1161
1162 if (pHandle == 0)
1163 {
1164 AssertFailed();
1165 return VERR_INVALID_HANDLE;
1166 }
1167
1168 Log(("vbsfFlush %RX64\n", Handle));
1169 rc = RTFileFlush(pHandle->file.Handle);
1170 AssertRC(rc);
1171 return rc;
1172}
1173
1174int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer,
1175 uint32_t *pIndex, uint32_t *pcFiles)
1176{
1177 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR);
1178 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1179 uint32_t cbDirEntry, cbBufferOrg;
1180 int rc = VINF_SUCCESS;
1181 PSHFLDIRINFO pSFDEntry;
1182 PRTUCS2 puszString;
1183 PRTDIR DirHandle;
1184 bool fUtf8;
1185
1186 fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1187
1188 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1189 {
1190 AssertFailed();
1191 return VERR_INVALID_PARAMETER;
1192 }
1193 Assert(pIndex && *pIndex == 0);
1194 DirHandle = pHandle->dir.Handle;
1195
1196 cbDirEntry = 4096;
1197 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1198 if (pDirEntry == 0)
1199 {
1200 AssertFailed();
1201 return VERR_NO_MEMORY;
1202 }
1203
1204 cbBufferOrg = *pcbBuffer;
1205 *pcbBuffer = 0;
1206 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1207
1208 *pIndex = 1; /* not yet complete */
1209 *pcFiles = 0;
1210
1211 if (pPath)
1212 {
1213 if (pHandle->dir.SearchHandle == 0)
1214 {
1215 /* Build a host full path for the given path
1216 * and convert ucs2 to utf8 if necessary.
1217 */
1218 char *pszFullPath = NULL;
1219
1220 Assert(pHandle->dir.pLastValidEntry == 0);
1221
1222 rc = vbsfBuildFullPath (pClient, root, pPath, pPath->u16Size, &pszFullPath, NULL, true);
1223
1224 if (VBOX_SUCCESS (rc))
1225 {
1226 rc = RTDirOpenFiltered (&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT);
1227
1228 /* free the path string */
1229 vbsfFreeFullPath(pszFullPath);
1230
1231 if (VBOX_FAILURE (rc))
1232 goto end;
1233 }
1234 else
1235 goto end;
1236 }
1237 Assert(pHandle->dir.SearchHandle);
1238 DirHandle = pHandle->dir.SearchHandle;
1239 }
1240
1241 while(cbBufferOrg)
1242 {
1243 uint32_t cbDirEntrySize = cbDirEntry;
1244 uint32_t cbNeeded;
1245
1246 /* Do we still have a valid last entry for the active search? If so, then return it here */
1247 if (pHandle->dir.pLastValidEntry)
1248 {
1249 pDirEntry = pHandle->dir.pLastValidEntry;
1250 }
1251 else
1252 {
1253 pDirEntry = pDirEntryOrg;
1254
1255 rc = RTDirReadEx(DirHandle, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING);
1256 if (rc == VERR_NO_MORE_FILES)
1257 {
1258 *pIndex = 0; /* listing completed */
1259 break;
1260 }
1261
1262 if (VINF_SUCCESS != rc && rc != VWRN_NO_DIRENT_INFO)
1263 {
1264 AssertFailed();
1265 if (rc != VERR_NO_TRANSLATION)
1266 break;
1267 else
1268 continue;
1269 }
1270 }
1271
1272 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String);
1273 if (fUtf8)
1274 cbNeeded += pDirEntry->cbName + 1;
1275 else
1276 /* Overestimating, but that's ok */
1277 cbNeeded += (pDirEntry->cbName + 1) * 2;
1278
1279 if (cbBufferOrg < cbNeeded)
1280 {
1281 /* No room, so save this directory entry, or else it's lost forever */
1282 pHandle->dir.pLastValidEntry = pDirEntry;
1283
1284 if (*pcFiles == 0)
1285 {
1286 AssertFailed();
1287 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1288 }
1289 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1290 }
1291
1292 pSFDEntry->Info = pDirEntry->Info;
1293 pSFDEntry->cucShortName = 0;
1294
1295 if (fUtf8)
1296 {
1297 void *src, *dst;
1298
1299 src = &pDirEntry->szName[0];
1300 dst = &pSFDEntry->name.String.utf8[0];
1301
1302 memcpy (dst, src, pDirEntry->cbName + 1);
1303
1304 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1305 pSFDEntry->name.u16Length = pDirEntry->cbName;
1306 }
1307 else
1308 {
1309 pSFDEntry->name.String.ucs2[0] = 0;
1310 puszString = pSFDEntry->name.String.ucs2;
1311 int rc2 = RTStrUtf8ToUcs2Ex(&puszString, pDirEntry->cbName+1, pDirEntry->szName);
1312 AssertRC(rc2);
1313
1314 pSFDEntry->name.u16Length = RTStrUcs2Len (pSFDEntry->name.String.ucs2) * 2;
1315 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1316
1317 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1318 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1319
1320 // adjust cbNeeded (it was overestimated before)
1321 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1322 }
1323
1324 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1325 *pcbBuffer += cbNeeded;
1326 cbBufferOrg-= cbNeeded;
1327
1328 *pcFiles += 1;
1329
1330 /* Free the saved last entry, that we've just returned */
1331 if (pHandle->dir.pLastValidEntry)
1332 {
1333 RTMemFree(pHandle->dir.pLastValidEntry);
1334 pHandle->dir.pLastValidEntry = NULL;
1335 }
1336
1337 if (flags & SHFL_LIST_RETURN_ONE)
1338 break; /* we're done */
1339 }
1340 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1341
1342end:
1343 if (pDirEntry)
1344 RTMemFree(pDirEntry);
1345
1346 return rc;
1347}
1348
1349int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1350{
1351 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1352 int rc = VINF_SUCCESS;
1353 RTFSOBJINFO *pObjInfo = (RTFSOBJINFO *)pBuffer;
1354
1355
1356 if (pHandle == 0 || pcbBuffer == 0 || pObjInfo == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1357 {
1358 AssertFailed();
1359 return VERR_INVALID_PARAMETER;
1360 }
1361
1362 /* @todo other options */
1363 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1364
1365 *pcbBuffer = 0;
1366
1367 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1368 {
1369 rc = RTDirQueryInfo(pHandle->dir.Handle, pObjInfo, RTFSOBJATTRADD_NOTHING);
1370 }
1371 else
1372 {
1373 rc = RTFileQueryInfo(pHandle->file.Handle, pObjInfo, RTFSOBJATTRADD_NOTHING);
1374 }
1375 if (rc == VINF_SUCCESS)
1376 {
1377 *pcbBuffer = sizeof(RTFSOBJINFO);
1378 }
1379 else
1380 AssertFailed();
1381
1382 return rc;
1383}
1384
1385int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1386{
1387 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1388 int rc = VINF_SUCCESS;
1389 RTFSOBJINFO *pSFDEntry;
1390
1391 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1392 {
1393 AssertFailed();
1394 return VERR_INVALID_PARAMETER;
1395 }
1396
1397 /* is the guest allowed to write to this share? */
1398 bool fWritable;
1399 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1400 if (RT_FAILURE(rc) || !fWritable)
1401 return VERR_WRITE_PROTECT;
1402
1403 *pcbBuffer = 0;
1404 pSFDEntry = (RTFSOBJINFO *)pBuffer;
1405
1406 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1407
1408 /* Change only the time values that are not zero */
1409 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1410 {
1411 rc = RTDirSetTimes(pHandle->dir.Handle,
1412 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1413 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1414 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1415 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1416 );
1417 }
1418 else
1419 {
1420 rc = RTFileSetTimes(pHandle->file.Handle,
1421 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1422 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1423 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1424 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1425 );
1426 }
1427 if (rc != VINF_SUCCESS)
1428 {
1429 Log(("RTFileSetTimes failed with %Vrc\n", rc));
1430 Log(("AccessTime %VX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1431 Log(("ModificationTime %VX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1432 Log(("ChangeTime %VX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1433 Log(("BirthTime %VX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1434 /* temporary hack */
1435 rc = VINF_SUCCESS;
1436 }
1437
1438 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_FILE)
1439 {
1440 /* Change file attributes if necessary */
1441 if (pSFDEntry->Attr.fMode)
1442 {
1443 rc = RTFileSetMode((RTFILE)pHandle->file.Handle, pSFDEntry->Attr.fMode);
1444 if (rc != VINF_SUCCESS)
1445 {
1446 Log(("RTFileSetMode %x failed with %Vrc\n", pSFDEntry->Attr.fMode, rc));
1447 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1448 rc = VINF_SUCCESS;
1449 }
1450 }
1451 }
1452
1453 if (rc == VINF_SUCCESS)
1454 {
1455 uint32_t bufsize = sizeof(*pSFDEntry);
1456
1457 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1458 if (rc == VINF_SUCCESS)
1459 {
1460 *pcbBuffer = sizeof(RTFSOBJINFO);
1461 }
1462 else
1463 AssertFailed();
1464 }
1465
1466 return rc;
1467}
1468
1469
1470int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1471{
1472 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1473 int rc = VINF_SUCCESS;
1474 RTFSOBJINFO *pSFDEntry;
1475
1476 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1477 {
1478 AssertFailed();
1479 return VERR_INVALID_PARAMETER;
1480 }
1481
1482 /* is the guest allowed to write to this share? */
1483 bool fWritable;
1484 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1485 if (RT_FAILURE(rc) || !fWritable)
1486 return VERR_WRITE_PROTECT;
1487
1488 rc = VINF_SUCCESS;
1489
1490 *pcbBuffer = 0;
1491 pSFDEntry = (RTFSOBJINFO *)pBuffer;
1492
1493 if (flags & SHFL_INFO_SIZE)
1494 {
1495 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1496 if (rc != VINF_SUCCESS)
1497 AssertFailed();
1498 }
1499 else
1500 AssertFailed();
1501
1502 if (rc == VINF_SUCCESS)
1503 {
1504 RTFSOBJINFO fileinfo;
1505
1506 /* Query the new object info and return it */
1507 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1508 if (rc == VINF_SUCCESS)
1509 {
1510 *pSFDEntry = fileinfo;
1511 *pcbBuffer = sizeof(RTFSOBJINFO);
1512 }
1513 else
1514 AssertFailed();
1515 }
1516
1517 return rc;
1518}
1519
1520int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1521{
1522 int rc = VINF_SUCCESS;
1523 SHFLVOLINFO *pSFDEntry;
1524 char *pszFullPath = NULL;
1525 SHFLSTRING dummy;
1526
1527 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1528 {
1529 AssertFailed();
1530 return VERR_INVALID_PARAMETER;
1531 }
1532
1533 /* @todo other options */
1534 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1535
1536 *pcbBuffer = 0;
1537 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1538
1539 ShflStringInitBuffer(&dummy, sizeof(dummy));
1540 rc = vbsfBuildFullPath (pClient, root, &dummy, 0, &pszFullPath, NULL);
1541
1542 if (VBOX_SUCCESS (rc))
1543 {
1544 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1545 if (rc != VINF_SUCCESS)
1546 goto exit;
1547
1548 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1549 if (rc != VINF_SUCCESS)
1550 goto exit;
1551
1552 rc = RTFsQueryProperties(pszFullPath, &pSFDEntry->fsProperties);
1553 if (rc != VINF_SUCCESS)
1554 goto exit;
1555
1556 *pcbBuffer = sizeof(SHFLVOLINFO);
1557 }
1558 else AssertFailed();
1559
1560exit:
1561 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Vrc\n", rc));
1562 /* free the path string */
1563 vbsfFreeFullPath(pszFullPath);
1564 return rc;
1565}
1566
1567int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1568{
1569 if (pcbBuffer == 0 || pBuffer == 0)
1570 {
1571 AssertFailed();
1572 return VERR_INVALID_PARAMETER;
1573 }
1574
1575 if (flags & SHFL_INFO_FILE)
1576 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1577
1578 if (flags & SHFL_INFO_VOLUME)
1579 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
1580
1581 AssertFailed();
1582 return VERR_INVALID_PARAMETER;
1583}
1584
1585int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1586{
1587 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
1588
1589 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1590 {
1591 AssertFailed();
1592 return VERR_INVALID_PARAMETER;
1593 }
1594
1595 /* is the guest allowed to write to this share? */
1596 bool fWritable;
1597 int rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1598 if (RT_FAILURE(rc) || !fWritable)
1599 return VERR_WRITE_PROTECT;
1600
1601 if (flags & SHFL_INFO_FILE)
1602 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1603
1604 if (flags & SHFL_INFO_SIZE)
1605 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1606
1607// if (flags & SHFL_INFO_VOLUME)
1608// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1609 AssertFailed();
1610 return VERR_INVALID_PARAMETER;
1611}
1612
1613int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1614{
1615 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1616 uint32_t fRTLock = 0;
1617 int rc;
1618
1619 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
1620
1621 if (pHandle == 0)
1622 {
1623 AssertFailed();
1624 return VERR_INVALID_HANDLE;
1625 }
1626 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
1627 || (flags & SHFL_LOCK_ENTIRE)
1628 )
1629 {
1630 AssertFailed();
1631 return VERR_INVALID_PARAMETER;
1632 }
1633
1634 /* Lock type */
1635 switch(flags & SHFL_LOCK_MODE_MASK)
1636 {
1637 case SHFL_LOCK_SHARED:
1638 fRTLock = RTFILE_LOCK_READ;
1639 break;
1640
1641 case SHFL_LOCK_EXCLUSIVE:
1642 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
1643 break;
1644
1645 default:
1646 AssertFailed();
1647 return VERR_INVALID_PARAMETER;
1648 }
1649
1650 /* Lock wait type */
1651 if (flags & SHFL_LOCK_WAIT)
1652 fRTLock |= RTFILE_LOCK_WAIT;
1653 else
1654 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
1655
1656#ifdef RT_OS_WINDOWS
1657 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
1658 if (rc != VINF_SUCCESS)
1659 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1660#else
1661 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
1662 rc = VINF_SUCCESS;
1663#endif
1664 return rc;
1665}
1666
1667int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1668{
1669 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1670 int rc;
1671
1672 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
1673
1674 if (pHandle == 0)
1675 {
1676 return VERR_INVALID_HANDLE;
1677 }
1678 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
1679 || (flags & SHFL_LOCK_ENTIRE)
1680 )
1681 {
1682 return VERR_INVALID_PARAMETER;
1683 }
1684
1685#ifdef RT_OS_WINDOWS
1686 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
1687 if (rc != VINF_SUCCESS)
1688 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1689#else
1690 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
1691 rc = VINF_SUCCESS;
1692#endif
1693
1694 return rc;
1695}
1696
1697
1698int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
1699{
1700 int rc = VINF_SUCCESS;
1701
1702 /* Validate input */
1703 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR)
1704 || cbPath == 0
1705 || pPath == 0)
1706 {
1707 AssertFailed();
1708 return VERR_INVALID_PARAMETER;
1709 }
1710
1711 /* Build a host full path for the given path
1712 * and convert ucs2 to utf8 if necessary.
1713 */
1714 char *pszFullPath = NULL;
1715
1716 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, NULL);
1717 if (VBOX_SUCCESS (rc))
1718 {
1719 /* is the guest allowed to write to this share? */
1720 bool fWritable;
1721 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1722 if (RT_FAILURE(rc) || !fWritable)
1723 rc = VERR_WRITE_PROTECT;
1724
1725 if (VBOX_SUCCESS (rc))
1726 {
1727 if (flags & SHFL_REMOVE_FILE)
1728 rc = RTFileDelete(pszFullPath);
1729 else
1730 rc = RTDirRemove(pszFullPath);
1731 }
1732
1733#ifndef DEBUG_dmik
1734 // VERR_ACCESS_DENIED for example?
1735 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
1736#endif
1737 /* free the path string */
1738 vbsfFreeFullPath(pszFullPath);
1739 }
1740 return rc;
1741}
1742
1743
1744int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
1745{
1746 int rc = VINF_SUCCESS;
1747
1748 /* Validate input */
1749 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
1750 || pSrc == 0
1751 || pDest == 0)
1752 {
1753 AssertFailed();
1754 return VERR_INVALID_PARAMETER;
1755 }
1756
1757 /* Build a host full path for the given path
1758 * and convert ucs2 to utf8 if necessary.
1759 */
1760 char *pszFullPathSrc = NULL;
1761 char *pszFullPathDest = NULL;
1762
1763 rc = vbsfBuildFullPath (pClient, root, pSrc, pSrc->u16Size, &pszFullPathSrc, NULL);
1764 if (rc != VINF_SUCCESS)
1765 return rc;
1766
1767 rc = vbsfBuildFullPath (pClient, root, pDest, pDest->u16Size, &pszFullPathDest, NULL);
1768 if (VBOX_SUCCESS (rc))
1769 {
1770 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
1771
1772 /* is the guest allowed to write to this share? */
1773 bool fWritable;
1774 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1775 if (RT_FAILURE(rc) || !fWritable)
1776 rc = VERR_WRITE_PROTECT;
1777
1778 if (VBOX_SUCCESS (rc))
1779 {
1780 if (flags & SHFL_RENAME_FILE)
1781 {
1782 rc = RTFileMove(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0);
1783 }
1784 else
1785 {
1786 /* NT ignores the REPLACE flag and simply return and already exists error. */
1787 rc = RTDirRename(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0);
1788 }
1789 }
1790
1791#ifndef DEBUG_dmik
1792 AssertRC(rc);
1793#endif
1794 /* free the path string */
1795 vbsfFreeFullPath(pszFullPathDest);
1796 }
1797 /* free the path string */
1798 vbsfFreeFullPath(pszFullPathSrc);
1799 return rc;
1800}
1801
1802/*
1803 * Clean up our mess by freeing all handles that are still valid.
1804 *
1805 */
1806int vbsfDisconnect(SHFLCLIENTDATA *pClient)
1807{
1808 for (int i=0;i<SHFLHANDLE_MAX;i++)
1809 {
1810 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(i, SHFL_HF_TYPE_MASK);
1811
1812 if (pHandle)
1813 {
1814 Log(("Open handle %08x\n", i));
1815 vbsfClose(pClient, SHFL_HANDLE_ROOT /* incorrect, but it's not important */, (SHFLHANDLE)i);
1816 }
1817 }
1818 return VINF_SUCCESS;
1819}
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