VirtualBox

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

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

Shared folders flag for appending data to a file. Windows addition only. Todo: Linux additions and IPRT support.

  • 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 (fShflFlags & SHFL_CF_APPEND)
608 {
609 Log(("FLAG: SHFL_CF_APPEND\n"));
610 /** @todo fOpen |= RTFILE_O_APPEND; */
611 }
612
613 if (RT_SUCCESS(rc))
614 {
615 *pfOpen = fOpen;
616 }
617 return rc;
618}
619
620/**
621 * Open a file or create and open a new one.
622 *
623 * @returns IPRT status code
624 * @param pszPath Path to the file or folder on the host.
625 * @param pParms->CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
626 * @param pParms->Info When a new file is created this specifies the initial parameters.
627 * When a file is created or overwritten, it also specifies the
628 * initial size.
629 * @retval pParms->Result Shared folder status code, see include/VBox/shflsvc.h
630 * @retval pParms->Handle On success the (shared folder) handle of the file opened or
631 * created
632 * @retval pParms->Info On success the parameters of the file opened or created
633 */
634static int vbsfOpenFile (const char *pszPath, SHFLCREATEPARMS *pParms)
635{
636 LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms));
637 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
638
639 SHFLHANDLE handle = SHFL_HANDLE_NIL;
640 SHFLFILEHANDLE *pHandle = 0;
641 /* Open or create a file. */
642 unsigned fOpen = 0;
643 bool fNoError = false;
644
645 int rc = vbsfConvertFileOpenFlags(pParms->CreateFlags, &fOpen);
646 if (RT_SUCCESS(rc))
647 {
648 handle = vbsfAllocFileHandle();
649 }
650 if (SHFL_HANDLE_NIL != handle)
651 {
652 rc = VERR_NO_MEMORY; /* If this fails - rewritten immediately on success. */
653 pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(handle, SHFL_HF_TYPE_FILE);
654 }
655 if (0 != pHandle)
656 {
657 rc = RTFileOpen(&pHandle->file.Handle, pszPath, fOpen);
658 }
659 if (RT_FAILURE (rc))
660 {
661 switch (rc)
662 {
663 case VERR_FILE_NOT_FOUND:
664 pParms->Result = SHFL_FILE_NOT_FOUND;
665
666 /* This actually isn't an error, so correct the rc before return later,
667 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
668 fNoError = true;
669 break;
670 case VERR_PATH_NOT_FOUND:
671 pParms->Result = SHFL_PATH_NOT_FOUND;
672
673 /* This actually isn't an error, so correct the rc before return later,
674 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
675 fNoError = true;
676 break;
677 case VERR_ALREADY_EXISTS:
678 RTFSOBJINFO info;
679
680 /** @todo Possible race left here. */
681 if (RT_SUCCESS(RTPathQueryInfo (pszPath, &info, RTFSOBJATTRADD_NOTHING)))
682 {
683 pParms->Info = info;
684 }
685 pParms->Result = SHFL_FILE_EXISTS;
686
687 /* This actually isn't an error, so correct the rc before return later,
688 because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */
689 fNoError = true;
690 break;
691 default:
692 pParms->Result = SHFL_NO_RESULT;
693 }
694 }
695
696 if (RT_SUCCESS(rc))
697 {
698 /** @note The shared folder status code is very approximate, as the runtime
699 * does not really provide this information. */
700 pParms->Result = SHFL_FILE_EXISTS; /* We lost the information as to whether it was
701 created when we eliminated the race. */
702 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
703 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
704 || ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS
705 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
706 {
707 /* For now, we do not treat a failure here as fatal. */
708 /* @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if
709 SHFL_CF_ACT_FAIL_IF_EXISTS is set. */
710 RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject);
711 pParms->Result = SHFL_FILE_REPLACED;
712 }
713 if ( ( SHFL_CF_ACT_FAIL_IF_EXISTS
714 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
715 || ( SHFL_CF_ACT_CREATE_IF_NEW
716 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
717 {
718 pParms->Result = SHFL_FILE_CREATED;
719 }
720#if 0
721 /* @todo */
722 /* Set new attributes. */
723 if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS
724 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))
725 || ( SHFL_CF_ACT_CREATE_IF_NEW
726 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)))
727 {
728 RTFileSetTimes(pHandle->file.Handle,
729 &pParms->Info.AccessTime,
730 &pParms->Info.ModificationTime,
731 &pParms->Info.ChangeTime,
732 &pParms->Info.BirthTime
733 );
734
735 RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode);
736 }
737#endif
738 RTFSOBJINFO info;
739
740 /* Get file information */
741 rc = RTFileQueryInfo (pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING);
742 if (RT_SUCCESS(rc))
743 {
744 pParms->Info = info;
745 }
746 }
747 if (RT_FAILURE(rc))
748 {
749 if ( (0 != pHandle)
750 && (NIL_RTFILE != pHandle->file.Handle)
751 && (0 != pHandle->file.Handle))
752 {
753 RTFileClose(pHandle->file.Handle);
754 pHandle->file.Handle = NIL_RTFILE;
755 }
756 if (SHFL_HANDLE_NIL != handle)
757 {
758 vbsfFreeFileHandle (handle);
759 }
760 }
761 else
762 {
763 pParms->Handle = handle;
764 }
765
766 /* Report the driver that all is okay, we're done here */
767 if (fNoError)
768 rc = VINF_SUCCESS;
769
770 LogFlow(("vbsfOpenFile: rc = %Vrc\n", rc));
771 return rc;
772}
773
774/**
775 * Open a folder or create and open a new one.
776 *
777 * @returns IPRT status code
778 * @param pszPath Path to the file or folder on the host.
779 * @param pParms->CreateFlags Creation or open parameters, see include/VBox/shflsvc.h
780 * @retval pParms->Result Shared folder status code, see include/VBox/shflsvc.h
781 * @retval pParms->Handle On success the (shared folder) handle of the folder opened or
782 * created
783 * @retval pParms->Info On success the parameters of the folder opened or created
784 *
785 * @note folders are created with fMode = 0777
786 */
787static int vbsfOpenDir (const char *pszPath, SHFLCREATEPARMS *pParms)
788{
789 LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms));
790 Log(("SHFL create flags %08x\n", pParms->CreateFlags));
791
792 int rc = VERR_NO_MEMORY;
793 SHFLHANDLE handle = vbsfAllocDirHandle();
794 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(handle, SHFL_HF_TYPE_DIR);
795 if (0 != pHandle)
796 {
797 rc = VINF_SUCCESS;
798 pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */
799 /** @todo Can anyone think of a sensible, race-less way to do this? Although
800 I suspect that the race is inherent, due to the API available... */
801 /* Try to create the folder first if "create if new" is specified. If this
802 fails, and "open if exists" is specified, then we ignore the failure and try
803 to open the folder anyway. */
804 if ( SHFL_CF_ACT_CREATE_IF_NEW
805 == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))
806 {
807 /** @todo render supplied attributes.
808 * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */
809 RTFMODE fMode = 0777;
810
811 pParms->Result = SHFL_FILE_CREATED;
812 rc = RTDirCreate(pszPath, fMode);
813 if (RT_FAILURE (rc))
814 {
815 switch (rc)
816 {
817 case VERR_ALREADY_EXISTS:
818 pParms->Result = SHFL_FILE_EXISTS;
819 break;
820 case VERR_PATH_NOT_FOUND:
821 pParms->Result = SHFL_PATH_NOT_FOUND;
822 break;
823 default:
824 pParms->Result = SHFL_NO_RESULT;
825 }
826 }
827 }
828 if ( RT_SUCCESS(rc)
829 || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)))
830 {
831 /* Open the directory now */
832 rc = RTDirOpen (&pHandle->dir.Handle, pszPath);
833 if (RT_SUCCESS(rc))
834 {
835 RTFSOBJINFO info;
836
837 rc = RTDirQueryInfo (pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING);
838 if (RT_SUCCESS(rc))
839 {
840 pParms->Info = info;
841 }
842 }
843 else
844 {
845 switch (rc)
846 {
847 case VERR_FILE_NOT_FOUND: /* Does this make sense? */
848 pParms->Result = SHFL_FILE_NOT_FOUND;
849 break;
850 case VERR_PATH_NOT_FOUND:
851 pParms->Result = SHFL_PATH_NOT_FOUND;
852 break;
853 case VERR_ACCESS_DENIED:
854 pParms->Result = SHFL_FILE_EXISTS;
855 break;
856 default:
857 pParms->Result = SHFL_NO_RESULT;
858 }
859 }
860 }
861 }
862 if (RT_FAILURE(rc))
863 {
864 if ( (0 != pHandle)
865 && (0 != pHandle->dir.Handle))
866 {
867 RTDirClose(pHandle->dir.Handle);
868 pHandle->dir.Handle = 0;
869 }
870 if (SHFL_HANDLE_NIL != handle)
871 {
872 vbsfFreeFileHandle (handle);
873 }
874 }
875 else
876 {
877 pParms->Handle = handle;
878 }
879 LogFlow(("vbsfOpenDir: rc = %Vrc\n", rc));
880 return rc;
881}
882
883static int vbsfCloseDir (SHFLFILEHANDLE *pHandle)
884{
885 int rc = VINF_SUCCESS;
886
887 LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n",
888 pHandle->dir.Handle, pHandle->dir.SearchHandle));
889
890 RTDirClose (pHandle->dir.Handle);
891
892 if (pHandle->dir.SearchHandle)
893 RTDirClose(pHandle->dir.SearchHandle);
894
895 if (pHandle->dir.pLastValidEntry)
896 {
897 RTMemFree(pHandle->dir.pLastValidEntry);
898 pHandle->dir.pLastValidEntry = NULL;
899 }
900
901 LogFlow(("vbsfCloseDir: rc = %d\n", rc));
902
903 return rc;
904}
905
906
907static int vbsfCloseFile (SHFLFILEHANDLE *pHandle)
908{
909 int rc = VINF_SUCCESS;
910
911 LogFlow(("vbsfCloseFile: Handle = %08X\n",
912 pHandle->file.Handle));
913
914 rc = RTFileClose (pHandle->file.Handle);
915
916 LogFlow(("vbsfCloseFile: rc = %d\n", rc));
917
918 return rc;
919}
920
921/**
922 * Look up file or folder information by host path.
923 *
924 * @returns iprt status code (currently VINF_SUCCESS)
925 * @param pszFullPath The path of the file to be looked up
926 * @retval pParms->Result Status of the operation (success or error)
927 * @retval pParms->Info On success, information returned about the file
928 */
929static int vbsfLookupFile(char *pszPath, SHFLCREATEPARMS *pParms)
930{
931 RTFSOBJINFO info;
932 int rc;
933
934 rc = RTPathQueryInfo (pszPath, &info, RTFSOBJATTRADD_NOTHING);
935 LogFlow(("SHFL_CF_LOOKUP\n"));
936 /* Client just wants to know if the object exists. */
937 switch (rc)
938 {
939 case VINF_SUCCESS:
940 {
941 pParms->Info = info;
942 pParms->Result = SHFL_FILE_EXISTS;
943 break;
944 }
945
946 case VERR_FILE_NOT_FOUND:
947 {
948 pParms->Result = SHFL_FILE_NOT_FOUND;
949 rc = VINF_SUCCESS;
950 break;
951 }
952
953 case VERR_PATH_NOT_FOUND:
954 {
955 pParms->Result = SHFL_PATH_NOT_FOUND;
956 rc = VINF_SUCCESS;
957 break;
958 }
959 }
960 return rc;
961}
962
963/**
964 * Create or open a file or folder. Perform character set and case
965 * conversion on the file name if necessary.
966 *
967 * @returns IPRT status code, but see note below
968 * @param pClient Data structure describing the client accessing the shared
969 * folder
970 * @param root The index of the shared folder in the table of mappings.
971 * The host path of the shared folder is found using this.
972 * @param pPath The path of the file or folder relative to the host path
973 * indexed by root.
974 * @param cbPath Presumably the length of the path in pPath. Actually
975 * ignored, as pPath contains a length parameter.
976 * @param pParms->Info If a new file is created or an old one overwritten, set
977 * these attributes
978 * @retval pParms->Result Shared folder result code, see include/VBox/shflsvc.h
979 * @retval pParms->Handle Shared folder handle to the newly opened file
980 * @retval pParms->Info Attributes of the file or folder opened
981 *
982 * @note This function returns success if a "non-exceptional" error occurred,
983 * such as "no such file". In this case, the caller should check the
984 * pParms->Result return value and whether pParms->Handle is valid.
985 */
986int vbsfCreate (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms)
987{
988 int rc = VINF_SUCCESS;
989
990 LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n",
991 pClient, pPath, cbPath, pParms, pParms->CreateFlags));
992
993 /* Check the client access rights to the root. */
994 /** @todo */
995
996 /* Build a host full path for the given path, handle file name case issues (if the guest
997 * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if
998 * necessary.
999 */
1000 char *pszFullPath = NULL;
1001 uint32_t cbFullPathRoot = 0;
1002
1003 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot);
1004
1005 if (VBOX_SUCCESS (rc))
1006 {
1007 /* Reset return values in case client forgot to do so. */
1008 pParms->Result = SHFL_NO_RESULT;
1009 pParms->Handle = SHFL_HANDLE_NIL;
1010
1011 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP))
1012 {
1013 rc = vbsfLookupFile(pszFullPath, pParms);
1014 }
1015 else
1016 {
1017 /* Query path information. */
1018 RTFSOBJINFO info;
1019
1020 rc = RTPathQueryInfo (pszFullPath, &info, RTFSOBJATTRADD_NOTHING);
1021 LogFlow(("RTPathQueryInfo returned %Vrc\n", rc));
1022
1023 if (RT_SUCCESS(rc))
1024 {
1025 /* Mark it as a directory in case the caller didn't. */
1026 /**
1027 * @todo I left this in in order not to change the behaviour of the
1028 * function too much. Is it really needed, and should it really be
1029 * here?
1030 */
1031 if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY))
1032 {
1033 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
1034 }
1035
1036 /**
1037 * @todo This should be in the Windows Guest Additions, as no-one else
1038 * needs it.
1039 */
1040 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY))
1041 {
1042 vbsfStripLastComponent (pszFullPath, cbFullPathRoot);
1043 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS;
1044 pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW;
1045 pParms->CreateFlags |= SHFL_CF_DIRECTORY;
1046 pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
1047 pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
1048 }
1049 }
1050
1051 rc = VINF_SUCCESS;
1052
1053 /* write access requested? */
1054 if (pParms->CreateFlags & ( SHFL_CF_ACT_REPLACE_IF_EXISTS
1055 | SHFL_CF_ACT_OVERWRITE_IF_EXISTS
1056 | SHFL_CF_ACT_CREATE_IF_NEW
1057 | SHFL_CF_ACCESS_WRITE))
1058 {
1059 /* is the guest allowed to write to this share? */
1060 bool fWritable;
1061 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1062 if (RT_FAILURE(rc) || !fWritable)
1063 rc = VERR_WRITE_PROTECT;
1064 }
1065
1066 if (RT_SUCCESS(rc))
1067 {
1068 if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY))
1069 {
1070 rc = vbsfOpenDir (pszFullPath, pParms);
1071 }
1072 else
1073 {
1074 rc = vbsfOpenFile (pszFullPath, pParms);
1075 }
1076 }
1077 }
1078
1079 /* free the path string */
1080 vbsfFreeFullPath(pszFullPath);
1081 }
1082
1083 Log(("vbsfCreate: handle = %RX64 rc = %Vrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result));
1084
1085 return rc;
1086}
1087
1088int vbsfClose (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1089{
1090 int rc = VINF_SUCCESS;
1091
1092 LogFlow(("vbsfClose: pClient = %p, Handle = %RX64\n",
1093 pClient, Handle));
1094
1095 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1096 Assert(pHandle);
1097 if (!pHandle)
1098 return VERR_INVALID_HANDLE;
1099
1100 switch (ShflHandleType (&pHandle->Header))
1101 {
1102 case SHFL_HF_TYPE_DIR:
1103 {
1104 rc = vbsfCloseDir (pHandle);
1105 break;
1106 }
1107 case SHFL_HF_TYPE_FILE:
1108 {
1109 rc = vbsfCloseFile (pHandle);
1110 break;
1111 }
1112 default:
1113 AssertFailed();
1114 break;
1115 }
1116 vbsfFreeFileHandle(Handle);
1117
1118 Log(("vbsfClose: rc = %Rrc\n", rc));
1119
1120 return rc;
1121}
1122
1123int vbsfRead (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1124{
1125 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1126 size_t count = 0;
1127 int rc;
1128
1129 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1130 {
1131 AssertFailed();
1132 return VERR_INVALID_PARAMETER;
1133 }
1134
1135 Log(("vbsfRead %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer));
1136
1137 if (*pcbBuffer == 0)
1138 return VINF_SUCCESS; /* @todo correct? */
1139
1140
1141 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1142 if (rc != VINF_SUCCESS)
1143 {
1144 AssertRC(rc);
1145 return rc;
1146 }
1147
1148 rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1149 *pcbBuffer = (uint32_t)count;
1150 Log(("RTFileRead returned %Vrc bytes read %x\n", rc, count));
1151 return rc;
1152}
1153
1154int vbsfWrite (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer)
1155{
1156 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1157 size_t count = 0;
1158 int rc;
1159
1160 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1161 {
1162 AssertFailed();
1163 return VERR_INVALID_PARAMETER;
1164 }
1165
1166 Log(("vbsfWrite %RX64 offset %RX64 bytes %x\n", Handle, offset, *pcbBuffer));
1167
1168 /* Is the guest allowed to write to this share?
1169 * XXX Actually this check was still done in vbsfCreate() -- RTFILE_O_WRITE cannot be set if vbsfMappingsQueryWritable() failed. */
1170 bool fWritable;
1171 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1172 if (RT_FAILURE(rc) || !fWritable)
1173 return VERR_WRITE_PROTECT;
1174
1175 if (*pcbBuffer == 0)
1176 return VINF_SUCCESS; /** @todo correct? */
1177
1178 rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL);
1179 if (rc != VINF_SUCCESS)
1180 {
1181 AssertRC(rc);
1182 return rc;
1183 }
1184
1185 rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count);
1186 *pcbBuffer = (uint32_t)count;
1187 Log(("RTFileWrite returned %Vrc bytes written %x\n", rc, count));
1188 return rc;
1189}
1190
1191
1192int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle)
1193{
1194 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1195 int rc = VINF_SUCCESS;
1196
1197 if (pHandle == 0)
1198 {
1199 AssertFailed();
1200 return VERR_INVALID_HANDLE;
1201 }
1202
1203 Log(("vbsfFlush %RX64\n", Handle));
1204 rc = RTFileFlush(pHandle->file.Handle);
1205 AssertRC(rc);
1206 return rc;
1207}
1208
1209int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer,
1210 uint32_t *pIndex, uint32_t *pcFiles)
1211{
1212 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR);
1213 PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg;
1214 uint32_t cbDirEntry, cbBufferOrg;
1215 int rc = VINF_SUCCESS;
1216 PSHFLDIRINFO pSFDEntry;
1217 PRTUTF16 pwszString;
1218 PRTDIR DirHandle;
1219 bool fUtf8;
1220
1221 fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0;
1222
1223 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1224 {
1225 AssertFailed();
1226 return VERR_INVALID_PARAMETER;
1227 }
1228 Assert(pIndex && *pIndex == 0);
1229 DirHandle = pHandle->dir.Handle;
1230
1231 cbDirEntry = 4096;
1232 pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry);
1233 if (pDirEntry == 0)
1234 {
1235 AssertFailed();
1236 return VERR_NO_MEMORY;
1237 }
1238
1239 cbBufferOrg = *pcbBuffer;
1240 *pcbBuffer = 0;
1241 pSFDEntry = (PSHFLDIRINFO)pBuffer;
1242
1243 *pIndex = 1; /* not yet complete */
1244 *pcFiles = 0;
1245
1246 if (pPath)
1247 {
1248 if (pHandle->dir.SearchHandle == 0)
1249 {
1250 /* Build a host full path for the given path
1251 * and convert ucs2 to utf8 if necessary.
1252 */
1253 char *pszFullPath = NULL;
1254
1255 Assert(pHandle->dir.pLastValidEntry == 0);
1256
1257 rc = vbsfBuildFullPath (pClient, root, pPath, pPath->u16Size, &pszFullPath, NULL, true);
1258
1259 if (VBOX_SUCCESS (rc))
1260 {
1261 rc = RTDirOpenFiltered (&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT);
1262
1263 /* free the path string */
1264 vbsfFreeFullPath(pszFullPath);
1265
1266 if (VBOX_FAILURE (rc))
1267 goto end;
1268 }
1269 else
1270 goto end;
1271 }
1272 Assert(pHandle->dir.SearchHandle);
1273 DirHandle = pHandle->dir.SearchHandle;
1274 }
1275
1276 while(cbBufferOrg)
1277 {
1278 uint32_t cbDirEntrySize = cbDirEntry;
1279 uint32_t cbNeeded;
1280
1281 /* Do we still have a valid last entry for the active search? If so, then return it here */
1282 if (pHandle->dir.pLastValidEntry)
1283 {
1284 pDirEntry = pHandle->dir.pLastValidEntry;
1285 }
1286 else
1287 {
1288 pDirEntry = pDirEntryOrg;
1289
1290 rc = RTDirReadEx(DirHandle, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING);
1291 if (rc == VERR_NO_MORE_FILES)
1292 {
1293 *pIndex = 0; /* listing completed */
1294 break;
1295 }
1296
1297 if (VINF_SUCCESS != rc && rc != VWRN_NO_DIRENT_INFO)
1298 {
1299 AssertFailed();
1300 if (rc != VERR_NO_TRANSLATION)
1301 break;
1302 else
1303 continue;
1304 }
1305 }
1306
1307 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String);
1308 if (fUtf8)
1309 cbNeeded += pDirEntry->cbName + 1;
1310 else
1311 /* Overestimating, but that's ok */
1312 cbNeeded += (pDirEntry->cbName + 1) * 2;
1313
1314 if (cbBufferOrg < cbNeeded)
1315 {
1316 /* No room, so save this directory entry, or else it's lost forever */
1317 pHandle->dir.pLastValidEntry = pDirEntry;
1318
1319 if (*pcFiles == 0)
1320 {
1321 AssertFailed();
1322 return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */
1323 }
1324 return VINF_SUCCESS; /* Return directly and don't free pDirEntry */
1325 }
1326
1327 pSFDEntry->Info = pDirEntry->Info;
1328 pSFDEntry->cucShortName = 0;
1329
1330 if (fUtf8)
1331 {
1332 void *src, *dst;
1333
1334 src = &pDirEntry->szName[0];
1335 dst = &pSFDEntry->name.String.utf8[0];
1336
1337 memcpy (dst, src, pDirEntry->cbName + 1);
1338
1339 pSFDEntry->name.u16Size = pDirEntry->cbName + 1;
1340 pSFDEntry->name.u16Length = pDirEntry->cbName;
1341 }
1342 else
1343 {
1344 pSFDEntry->name.String.ucs2[0] = 0;
1345 pwszString = pSFDEntry->name.String.ucs2;
1346 int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL);
1347 AssertRC(rc2);
1348
1349 pSFDEntry->name.u16Length = RTUtf16Len (pSFDEntry->name.String.ucs2) * 2;
1350 pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2;
1351
1352 Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size));
1353 Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2));
1354
1355 // adjust cbNeeded (it was overestimated before)
1356 cbNeeded = RT_OFFSETOF (SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size;
1357 }
1358
1359 pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded);
1360 *pcbBuffer += cbNeeded;
1361 cbBufferOrg-= cbNeeded;
1362
1363 *pcFiles += 1;
1364
1365 /* Free the saved last entry, that we've just returned */
1366 if (pHandle->dir.pLastValidEntry)
1367 {
1368 RTMemFree(pHandle->dir.pLastValidEntry);
1369 pHandle->dir.pLastValidEntry = NULL;
1370 }
1371
1372 if (flags & SHFL_LIST_RETURN_ONE)
1373 break; /* we're done */
1374 }
1375 Assert(rc != VINF_SUCCESS || *pcbBuffer > 0);
1376
1377end:
1378 if (pDirEntry)
1379 RTMemFree(pDirEntry);
1380
1381 return rc;
1382}
1383
1384int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1385{
1386 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1387 int rc = VINF_SUCCESS;
1388 RTFSOBJINFO *pObjInfo = (RTFSOBJINFO *)pBuffer;
1389
1390
1391 if (pHandle == 0 || pcbBuffer == 0 || pObjInfo == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1392 {
1393 AssertFailed();
1394 return VERR_INVALID_PARAMETER;
1395 }
1396
1397 /* @todo other options */
1398 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE));
1399
1400 *pcbBuffer = 0;
1401
1402 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1403 {
1404 rc = RTDirQueryInfo(pHandle->dir.Handle, pObjInfo, RTFSOBJATTRADD_NOTHING);
1405 }
1406 else
1407 {
1408 rc = RTFileQueryInfo(pHandle->file.Handle, pObjInfo, RTFSOBJATTRADD_NOTHING);
1409 }
1410 if (rc == VINF_SUCCESS)
1411 {
1412 *pcbBuffer = sizeof(RTFSOBJINFO);
1413 }
1414 else
1415 AssertFailed();
1416
1417 return rc;
1418}
1419
1420static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1421{
1422 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE);
1423 int rc = VINF_SUCCESS;
1424 RTFSOBJINFO *pSFDEntry;
1425
1426 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1427 {
1428 AssertFailed();
1429 return VERR_INVALID_PARAMETER;
1430 }
1431
1432 *pcbBuffer = 0;
1433 pSFDEntry = (RTFSOBJINFO *)pBuffer;
1434
1435 Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE));
1436
1437 /* Change only the time values that are not zero */
1438 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_DIR)
1439 {
1440 rc = RTDirSetTimes(pHandle->dir.Handle,
1441 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1442 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1443 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1444 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1445 );
1446 }
1447 else
1448 {
1449 rc = RTFileSetTimes(pHandle->file.Handle,
1450 (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL,
1451 (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL,
1452 (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL,
1453 (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL
1454 );
1455 }
1456 if (rc != VINF_SUCCESS)
1457 {
1458 Log(("RTFileSetTimes failed with %Vrc\n", rc));
1459 Log(("AccessTime %VX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1460 Log(("ModificationTime %VX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime)));
1461 Log(("ChangeTime %VX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime)));
1462 Log(("BirthTime %VX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime)));
1463 /* temporary hack */
1464 rc = VINF_SUCCESS;
1465 }
1466
1467 if (pHandle->Header.u32Flags & SHFL_HF_TYPE_FILE)
1468 {
1469 /* Change file attributes if necessary */
1470 if (pSFDEntry->Attr.fMode)
1471 {
1472 rc = RTFileSetMode((RTFILE)pHandle->file.Handle, pSFDEntry->Attr.fMode);
1473 if (rc != VINF_SUCCESS)
1474 {
1475 Log(("RTFileSetMode %x failed with %Vrc\n", pSFDEntry->Attr.fMode, rc));
1476 /* silent failure, because this tends to fail with e.g. windows guest & linux host */
1477 rc = VINF_SUCCESS;
1478 }
1479 }
1480 }
1481
1482 if (rc == VINF_SUCCESS)
1483 {
1484 uint32_t bufsize = sizeof(*pSFDEntry);
1485
1486 rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry);
1487 if (rc == VINF_SUCCESS)
1488 {
1489 *pcbBuffer = sizeof(RTFSOBJINFO);
1490 }
1491 else
1492 AssertFailed();
1493 }
1494
1495 return rc;
1496}
1497
1498
1499static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1500{
1501 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1502 int rc = VINF_SUCCESS;
1503 RTFSOBJINFO *pSFDEntry;
1504
1505 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(RTFSOBJINFO))
1506 {
1507 AssertFailed();
1508 return VERR_INVALID_PARAMETER;
1509 }
1510
1511 *pcbBuffer = 0;
1512 pSFDEntry = (RTFSOBJINFO *)pBuffer;
1513
1514 if (flags & SHFL_INFO_SIZE)
1515 {
1516 rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject);
1517 if (rc != VINF_SUCCESS)
1518 AssertFailed();
1519 }
1520 else
1521 AssertFailed();
1522
1523 if (rc == VINF_SUCCESS)
1524 {
1525 RTFSOBJINFO fileinfo;
1526
1527 /* Query the new object info and return it */
1528 rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING);
1529 if (rc == VINF_SUCCESS)
1530 {
1531 *pSFDEntry = fileinfo;
1532 *pcbBuffer = sizeof(RTFSOBJINFO);
1533 }
1534 else
1535 AssertFailed();
1536 }
1537
1538 return rc;
1539}
1540
1541int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1542{
1543 int rc = VINF_SUCCESS;
1544 SHFLVOLINFO *pSFDEntry;
1545 char *pszFullPath = NULL;
1546 SHFLSTRING dummy;
1547
1548 if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO))
1549 {
1550 AssertFailed();
1551 return VERR_INVALID_PARAMETER;
1552 }
1553
1554 /* @todo other options */
1555 Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME));
1556
1557 *pcbBuffer = 0;
1558 pSFDEntry = (PSHFLVOLINFO)pBuffer;
1559
1560 ShflStringInitBuffer(&dummy, sizeof(dummy));
1561 rc = vbsfBuildFullPath (pClient, root, &dummy, 0, &pszFullPath, NULL);
1562
1563 if (VBOX_SUCCESS (rc))
1564 {
1565 rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector);
1566 if (rc != VINF_SUCCESS)
1567 goto exit;
1568
1569 rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial);
1570 if (rc != VINF_SUCCESS)
1571 goto exit;
1572
1573 rc = RTFsQueryProperties(pszFullPath, &pSFDEntry->fsProperties);
1574 if (rc != VINF_SUCCESS)
1575 goto exit;
1576
1577 *pcbBuffer = sizeof(SHFLVOLINFO);
1578 }
1579 else AssertFailed();
1580
1581exit:
1582 AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Vrc\n", rc));
1583 /* free the path string */
1584 vbsfFreeFullPath(pszFullPath);
1585 return rc;
1586}
1587
1588int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1589{
1590 if (pcbBuffer == 0 || pBuffer == 0)
1591 {
1592 AssertFailed();
1593 return VERR_INVALID_PARAMETER;
1594 }
1595
1596 if (flags & SHFL_INFO_FILE)
1597 return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1598
1599 if (flags & SHFL_INFO_VOLUME)
1600 return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer);
1601
1602 AssertFailed();
1603 return VERR_INVALID_PARAMETER;
1604}
1605
1606int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer)
1607{
1608 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME);
1609
1610 if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0)
1611 {
1612 AssertFailed();
1613 return VERR_INVALID_PARAMETER;
1614 }
1615
1616 /* is the guest allowed to write to this share? */
1617 bool fWritable;
1618 int rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1619 if (RT_FAILURE(rc) || !fWritable)
1620 return VERR_WRITE_PROTECT;
1621
1622 if (flags & SHFL_INFO_FILE)
1623 return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1624
1625 if (flags & SHFL_INFO_SIZE)
1626 return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1627
1628// if (flags & SHFL_INFO_VOLUME)
1629// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer);
1630 AssertFailed();
1631 return VERR_INVALID_PARAMETER;
1632}
1633
1634int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1635{
1636 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1637 uint32_t fRTLock = 0;
1638 int rc;
1639
1640 Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL);
1641
1642 if (pHandle == 0)
1643 {
1644 AssertFailed();
1645 return VERR_INVALID_HANDLE;
1646 }
1647 if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL)
1648 || (flags & SHFL_LOCK_ENTIRE)
1649 )
1650 {
1651 AssertFailed();
1652 return VERR_INVALID_PARAMETER;
1653 }
1654
1655 /* Lock type */
1656 switch(flags & SHFL_LOCK_MODE_MASK)
1657 {
1658 case SHFL_LOCK_SHARED:
1659 fRTLock = RTFILE_LOCK_READ;
1660 break;
1661
1662 case SHFL_LOCK_EXCLUSIVE:
1663 fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE;
1664 break;
1665
1666 default:
1667 AssertFailed();
1668 return VERR_INVALID_PARAMETER;
1669 }
1670
1671 /* Lock wait type */
1672 if (flags & SHFL_LOCK_WAIT)
1673 fRTLock |= RTFILE_LOCK_WAIT;
1674 else
1675 fRTLock |= RTFILE_LOCK_IMMEDIATELY;
1676
1677#ifdef RT_OS_WINDOWS
1678 rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length);
1679 if (rc != VINF_SUCCESS)
1680 Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1681#else
1682 Log(("vbsfLock: Pretend success handle=%x\n", Handle));
1683 rc = VINF_SUCCESS;
1684#endif
1685 return rc;
1686}
1687
1688int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags)
1689{
1690 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(Handle, SHFL_HF_TYPE_FILE);
1691 int rc;
1692
1693 Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL);
1694
1695 if (pHandle == 0)
1696 {
1697 return VERR_INVALID_HANDLE;
1698 }
1699 if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL)
1700 || (flags & SHFL_LOCK_ENTIRE)
1701 )
1702 {
1703 return VERR_INVALID_PARAMETER;
1704 }
1705
1706#ifdef RT_OS_WINDOWS
1707 rc = RTFileUnlock(pHandle->file.Handle, offset, length);
1708 if (rc != VINF_SUCCESS)
1709 Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc));
1710#else
1711 Log(("vbsfUnlock: Pretend success handle=%x\n", Handle));
1712 rc = VINF_SUCCESS;
1713#endif
1714
1715 return rc;
1716}
1717
1718
1719int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags)
1720{
1721 int rc = VINF_SUCCESS;
1722
1723 /* Validate input */
1724 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR)
1725 || cbPath == 0
1726 || pPath == 0)
1727 {
1728 AssertFailed();
1729 return VERR_INVALID_PARAMETER;
1730 }
1731
1732 /* Build a host full path for the given path
1733 * and convert ucs2 to utf8 if necessary.
1734 */
1735 char *pszFullPath = NULL;
1736
1737 rc = vbsfBuildFullPath (pClient, root, pPath, cbPath, &pszFullPath, NULL);
1738 if (VBOX_SUCCESS (rc))
1739 {
1740 /* is the guest allowed to write to this share? */
1741 bool fWritable;
1742 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1743 if (RT_FAILURE(rc) || !fWritable)
1744 rc = VERR_WRITE_PROTECT;
1745
1746 if (VBOX_SUCCESS (rc))
1747 {
1748 if (flags & SHFL_REMOVE_FILE)
1749 rc = RTFileDelete(pszFullPath);
1750 else
1751 rc = RTDirRemove(pszFullPath);
1752 }
1753
1754#ifndef DEBUG_dmik
1755 // VERR_ACCESS_DENIED for example?
1756 // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY);
1757#endif
1758 /* free the path string */
1759 vbsfFreeFullPath(pszFullPath);
1760 }
1761 return rc;
1762}
1763
1764
1765int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags)
1766{
1767 int rc = VINF_SUCCESS;
1768
1769 /* Validate input */
1770 if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_RENAME_REPLACE_IF_EXISTS)
1771 || pSrc == 0
1772 || pDest == 0)
1773 {
1774 AssertFailed();
1775 return VERR_INVALID_PARAMETER;
1776 }
1777
1778 /* Build a host full path for the given path
1779 * and convert ucs2 to utf8 if necessary.
1780 */
1781 char *pszFullPathSrc = NULL;
1782 char *pszFullPathDest = NULL;
1783
1784 rc = vbsfBuildFullPath (pClient, root, pSrc, pSrc->u16Size, &pszFullPathSrc, NULL);
1785 if (rc != VINF_SUCCESS)
1786 return rc;
1787
1788 rc = vbsfBuildFullPath (pClient, root, pDest, pDest->u16Size, &pszFullPathDest, NULL);
1789 if (VBOX_SUCCESS (rc))
1790 {
1791 Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest));
1792
1793 /* is the guest allowed to write to this share? */
1794 bool fWritable;
1795 rc = vbsfMappingsQueryWritable (pClient, root, &fWritable);
1796 if (RT_FAILURE(rc) || !fWritable)
1797 rc = VERR_WRITE_PROTECT;
1798
1799 if (VBOX_SUCCESS (rc))
1800 {
1801 if (flags & SHFL_RENAME_FILE)
1802 {
1803 rc = RTFileMove(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0);
1804 }
1805 else
1806 {
1807 /* NT ignores the REPLACE flag and simply return and already exists error. */
1808 rc = RTDirRename(pszFullPathSrc, pszFullPathDest, (flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0);
1809 }
1810 }
1811
1812#ifndef DEBUG_dmik
1813 AssertRC(rc);
1814#endif
1815 /* free the path string */
1816 vbsfFreeFullPath(pszFullPathDest);
1817 }
1818 /* free the path string */
1819 vbsfFreeFullPath(pszFullPathSrc);
1820 return rc;
1821}
1822
1823/*
1824 * Clean up our mess by freeing all handles that are still valid.
1825 *
1826 */
1827int vbsfDisconnect(SHFLCLIENTDATA *pClient)
1828{
1829 for (int i=0;i<SHFLHANDLE_MAX;i++)
1830 {
1831 SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(i, SHFL_HF_TYPE_MASK);
1832
1833 if (pHandle)
1834 {
1835 Log(("Open handle %08x\n", i));
1836 vbsfClose(pClient, SHFL_HANDLE_ROOT /* incorrect, but it's not important */, (SHFLHANDLE)i);
1837 }
1838 }
1839 return VINF_SUCCESS;
1840}
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