VirtualBox

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

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

Windows shared folders: another directory listing bugfix.

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