VirtualBox

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

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

Backed out 25962/25963; there is no handle type distinction

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette